Bug 1331334 - Implement :defined pseudo-class for custom elements, r=emilio
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 28 Jun 2018 14:55:45 +0300
changeset 424177 b32be80ba88314e27a5b3f84be6532f89cedcb7a
parent 424176 a17a279fda0f57d460cff53a4ac8e62f03424fc3
child 424178 b778f39b883bc94990e6d8926620267d3851b1a7
push id104752
push useropettay@mozilla.com
push dateThu, 28 Jun 2018 12:22:10 +0000
treeherdermozilla-inbound@b32be80ba883 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1331334
milestone63.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 1331334 - Implement :defined pseudo-class for custom elements, r=emilio
dom/base/CustomElementRegistry.cpp
dom/base/Element.cpp
dom/base/Element.h
dom/base/nsContentUtils.cpp
dom/events/EventStates.h
servo/components/style/element_state.rs
servo/components/style/gecko/non_ts_pseudo_class_list.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/gecko/wrapper.rs
testing/web-platform/meta/custom-elements/Document-createElement.html.ini
testing/web-platform/meta/custom-elements/pseudo-class-defined.html.ini
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1150,16 +1150,17 @@ CustomElementRegistry::Upgrade(Element* 
     data->mState = CustomElementData::State::eFailed;
     // Empty element's custom element reaction queue.
     data->mReactionQueue.Clear();
     return;
   }
 
   // Step 8.
   data->mState = CustomElementData::State::eCustom;
+  aElement->SetDefined(true);
 
   // Step 9.
   aElement->SetCustomElementDefinition(aDefinition);
 }
 
 //-----------------------------------------------------
 // CustomElementReactionsStack
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4279,28 +4279,28 @@ Element::ClearServoData(nsIDocument* aDo
     aDoc->ClearServoRestyleRoot();
   }
 }
 
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
   SetHasCustomElementData();
+
+  if (aData->mState != CustomElementData::State::eCustom) {
+    SetDefined(false);
+  }
+
   nsExtendedDOMSlots *slots = ExtendedDOMSlots();
   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
   #if DEBUG
-    nsAtom* name = NodeInfo()->NameAtom();
-    nsAtom* type = aData->GetCustomElementType();
-    if (NodeInfo()->NamespaceID() == kNameSpaceID_XHTML) {
-      if (nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML)) {
-        MOZ_ASSERT(type == name);
-      } else {
-        MOZ_ASSERT(type != name);
-      }
-    } else { // kNameSpaceID_XUL
+    // We assert only XUL usage, since web may pass whatever as 'is' value
+    if (NodeInfo()->NamespaceID() == kNameSpaceID_XUL) {
+      nsAtom* name = NodeInfo()->NameAtom();
+      nsAtom* type = aData->GetCustomElementType();
       // Check to see if the tag name is a dashed name.
       if (nsContentUtils::IsNameWithDash(name)) {
         // Assert that a tag name with dashes is always an autonomous custom
         // element.
         MOZ_ASSERT(type == name);
       } else {
         // Could still be an autonomous custom element with a non-dashed tag name.
         // Need the check below for sure.
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -145,17 +145,17 @@ class Grid;
   { 0x96, 0xa2, 0xc5, 0x8b, 0x7b, 0x64, 0x97, 0xd1 } }
 
 class Element : public FragmentOrElement
 {
 public:
 #ifdef MOZILLA_INTERNAL_API
   explicit Element(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) :
     FragmentOrElement(aNodeInfo),
-    mState(NS_EVENT_STATE_MOZ_READONLY)
+    mState(NS_EVENT_STATE_MOZ_READONLY | NS_EVENT_STATE_DEFINED)
   {
     MOZ_ASSERT(mNodeInfo->NodeType() == ELEMENT_NODE,
                "Bad NodeType in aNodeInfo");
     SetIsElement();
   }
 
   ~Element()
   {
@@ -548,16 +548,24 @@ public:
   /**
    * Sets the custom element definition, called when custom element is created
    * or upgraded.
    *
    * @param aDefinition The custom element definition.
    */
   void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
 
+  void SetDefined(bool aSet)
+  {
+    if (aSet) {
+      AddStates(NS_EVENT_STATE_DEFINED);
+    } else {
+      RemoveStates(NS_EVENT_STATE_DEFINED);
+    }
+  }
 protected:
   /**
    * Method to get the _intrinsic_ content state of this element.  This is the
    * state that is independent of the element's presentation.  To get the full
    * content state, use State().  See mozilla/EventStates.h for
    * the possible bits that could be set here.
    */
   virtual EventStates IntrinsicState() const;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9919,19 +9919,16 @@ nsContentUtils::NewXULOrHTMLElement(Elem
   MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
              nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
              "Can only create XUL or XHTML elements.");
 
   nsAtom *name = nodeInfo->NameAtom();
   int32_t tag = eHTMLTag_unknown;
   bool isCustomElementName = false;
   if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
-    if (aIsAtom && !nsContentUtils::IsNameWithDash(aIsAtom)) {
-      aIsAtom = nullptr;
-    }
     tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
     isCustomElementName = (tag == eHTMLTag_userdefined &&
                            nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
   } else { // kNameSpaceID_XUL
     if (aIsAtom) {
       // Make sure the customized built-in element to be constructed confirms
       // to our naming requirement, i.e. [is] must be a dashed name and
       // the tag name must not.
@@ -10037,16 +10034,17 @@ nsContentUtils::NewXULOrHTMLElement(Elem
       DoCustomElementCreate(aResult, nodeInfo->GetDocument(), nodeInfo,
                             definition->mConstructor, rv);
       if (rv.MaybeSetPendingException(cx)) {
         if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
           NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(), aFromParser));
         } else {
           NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
         }
+        (*aResult)->SetDefined(false);
       }
       return NS_OK;
     }
 
     // Step 6.2.
     if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
       NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
     } else {
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -238,17 +238,18 @@ private:
 // Handler for the content has been disabled.
 #define NS_EVENT_STATE_HANDLER_DISABLED NS_DEFINE_EVENT_STATE_MACRO(19)
 // Handler for the content has crashed
 #define NS_EVENT_STATE_HANDLER_CRASHED NS_DEFINE_EVENT_STATE_MACRO(20)
 // Content is required.
 #define NS_EVENT_STATE_REQUIRED      NS_DEFINE_EVENT_STATE_MACRO(21)
 // Content is optional (and can be required).
 #define NS_EVENT_STATE_OPTIONAL      NS_DEFINE_EVENT_STATE_MACRO(22)
-// Free bit                          NS_DEFINE_EVENT_STATE_MACRO(23)
+// Element is either a defined custom element or uncustomized element.
+#define NS_EVENT_STATE_DEFINED       NS_DEFINE_EVENT_STATE_MACRO(23)
 // Link has been visited.
 #define NS_EVENT_STATE_VISITED       NS_DEFINE_EVENT_STATE_MACRO(24)
 // Link hasn't been visited.
 #define NS_EVENT_STATE_UNVISITED     NS_DEFINE_EVENT_STATE_MACRO(25)
 // Drag is hovering over content.
 #define NS_EVENT_STATE_DRAGOVER      NS_DEFINE_EVENT_STATE_MACRO(26)
 // Content value is in-range (and can be out-of-range).
 #define NS_EVENT_STATE_INRANGE       NS_DEFINE_EVENT_STATE_MACRO(27)
@@ -349,16 +350,17 @@ private:
 // INTRINSIC_STATES, which are are computed by the element itself
 // and returned from Element::IntrinsicState.
 #define EXTERNALLY_MANAGED_STATES (           \
   MANUALLY_MANAGED_STATES |                   \
   DIR_ATTR_STATES |                           \
   DISABLED_STATES |                           \
   REQUIRED_STATES |                           \
   NS_EVENT_STATE_ACTIVE |                     \
+  NS_EVENT_STATE_DEFINED |                    \
   NS_EVENT_STATE_DRAGOVER |                   \
   NS_EVENT_STATE_FOCUS |                      \
   NS_EVENT_STATE_FOCUSRING |                  \
   NS_EVENT_STATE_FOCUS_WITHIN |               \
   NS_EVENT_STATE_FULL_SCREEN |                \
   NS_EVENT_STATE_HOVER |                      \
   NS_EVENT_STATE_URLTARGET                    \
 )
--- a/servo/components/style/element_state.rs
+++ b/servo/components/style/element_state.rs
@@ -66,17 +66,18 @@ bitflags! {
         /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-crashed
         const IN_HANDLER_CRASHED_STATE = 1 << 20;
         /// <https://html.spec.whatwg.org/multipage/#selector-required>
         const IN_REQUIRED_STATE = 1 << 21;
         /// <https://html.spec.whatwg.org/multipage/#selector-optional>
         const IN_OPTIONAL_STATE = 1 << 22;
         /// <https://html.spec.whatwg.org/multipage/#selector-read-write>
         const IN_READ_WRITE_STATE = 1 << 22;
-        /// There is a free bit at 23.
+        /// <https://html.spec.whatwg.org/multipage/semantics-other.html#selector-defined>
+        const IN_DEFINED_STATE = 1 << 23;
         /// <https://html.spec.whatwg.org/multipage/#selector-visited>
         const IN_VISITED_STATE = 1 << 24;
         /// <https://html.spec.whatwg.org/multipage/#selector-link>
         const IN_UNVISITED_STATE = 1 << 25;
         /// <https://drafts.csswg.org/selectors-4/#the-any-link-pseudo>
         const IN_VISITED_OR_UNVISITED_STATE = ElementState::IN_VISITED_STATE.bits |
                                               ElementState::IN_UNVISITED_STATE.bits;
         /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -42,16 +42,17 @@ macro_rules! apply_non_ts_list {
             bare: [
                 ("-moz-table-border-nonzero", MozTableBorderNonzero, mozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
                 ("-moz-browser-frame", MozBrowserFrame, mozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
                 ("link", Link, link, IN_UNVISITED_STATE, _),
                 ("any-link", AnyLink, anyLink, IN_VISITED_OR_UNVISITED_STATE, _),
                 ("visited", Visited, visited, IN_VISITED_STATE, _),
                 ("active", Active, active, IN_ACTIVE_STATE, _),
                 ("checked", Checked, checked, IN_CHECKED_STATE, _),
+                ("defined", Defined, defined, IN_DEFINED_STATE, _),
                 ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
                 ("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
                 ("focus", Focus, focus, IN_FOCUS_STATE, _),
                 ("focus-within", FocusWithin, focusWithin, IN_FOCUS_WITHIN_STATE, _),
                 ("hover", Hover, hover, IN_HOVER_STATE, _),
                 ("-moz-drag-over", MozDragOver, mozDragOver, IN_DRAGOVER_STATE, _),
                 ("target", Target, target, IN_TARGET_STATE, _),
                 ("indeterminate", Indeterminate, indeterminate, IN_INDETERMINATE_STATE, _),
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -182,16 +182,19 @@ impl NonTSPseudoClass {
     fn is_enabled_in_content(&self) -> bool {
         use gecko_bindings::structs::mozilla;
         match *self {
             // For pseudo-classes with pref, the availability in content
             // depends on the pref.
             NonTSPseudoClass::Fullscreen => unsafe {
                 mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
             },
+            NonTSPseudoClass::Defined => unsafe {
+                structs::nsContentUtils_sIsCustomElementsEnabled
+            },
             // Otherwise, a pseudo-class is enabled in content when it
             // doesn't have any enabled flag.
             _ => !self.has_any_flag(
                 NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME,
             ),
         }
     }
 
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -2119,16 +2119,17 @@ impl<'le> ::selectors::Element for Gecko
         context: &mut MatchingContext<Self::Impl>,
         flags_setter: &mut F,
     ) -> bool
     where
         F: FnMut(&Self, ElementSelectorFlags),
     {
         use selectors::matching::*;
         match *pseudo_class {
+            NonTSPseudoClass::Defined |
             NonTSPseudoClass::Focus |
             NonTSPseudoClass::Enabled |
             NonTSPseudoClass::Disabled |
             NonTSPseudoClass::Checked |
             NonTSPseudoClass::Fullscreen |
             NonTSPseudoClass::MozFullScreen |
             NonTSPseudoClass::Indeterminate |
             NonTSPseudoClass::PlaceholderShown |
--- a/testing/web-platform/meta/custom-elements/Document-createElement.html.ini
+++ b/testing/web-platform/meta/custom-elements/Document-createElement.html.ini
@@ -2,11 +2,8 @@
   [document.createElement must report a NotSupportedError when the element is adopted into a the document of an iframe during construction]
     expected: FAIL
 
   [document.createElement must report a NotSupportedError when the element is inserted into a the document of an iframe during construction]
     expected: FAIL
 
   [document.createElement must not report a NotSupportedError when the element is adopted back from a the document of an iframe during construction]
     expected: FAIL
-
-  [document.createElement must create an instance of autonomous custom elements when it has is attribute]
-    expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/custom-elements/pseudo-class-defined.html.ini
+++ /dev/null
@@ -1,119 +0,0 @@
-[pseudo-class-defined.html]
-  max-asserts: 4
-  [<div> should be :defined]
-    expected: FAIL
-
-  [createElement("div") should be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/1999/xhtml", "div") should be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/2000/svg", "div") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElement("div") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "div") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/2000/svg", "div") should be :defined]
-    expected: FAIL
-
-  [<a-a> should not be :defined]
-    expected: FAIL
-
-  [createElement("a-a") should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/1999/xhtml", "a-a") should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/2000/svg", "a-a") should be :defined]
-    expected: FAIL
-
-  [Upgraded createElement("a-a") should be :defined]
-    expected: FAIL
-
-  [Upgraded createElementNS("http://www.w3.org/1999/xhtml", "a-a") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElement("a-a") should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "a-a") should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/2000/svg", "a-a") should be :defined]
-    expected: FAIL
-
-  [<font-face> should be :defined]
-    expected: FAIL
-
-  [createElement("font-face") should be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/1999/xhtml", "font-face") should be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/2000/svg", "font-face") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElement("font-face") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "font-face") should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/2000/svg", "font-face") should be :defined]
-    expected: FAIL
-
-  [<abbr is=my-abbr> should not be :defined]
-    expected: FAIL
-
-  [createElement("abbr", { is: "my-abbr" }) should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/2000/svg", "abbr", { is: "my-abbr" }) should be :defined]
-    expected: FAIL
-
-  [Upgraded createElement("abbr", { is: "my-abbr" }) should be :defined]
-    expected: FAIL
-
-  [Upgraded createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElement("abbr", { is: "my-abbr" }) should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "abbr", { is: "my-abbr" }) should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/2000/svg", "abbr", { is: "my-abbr" }) should be :defined]
-    expected: FAIL
-
-  [<p> should not be :defined]
-    expected: FAIL
-
-  [createElement("p", { is: "" }) should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/1999/xhtml", "p", { is: "" }) should not be :defined]
-    expected: FAIL
-
-  [createElementNS("http://www.w3.org/2000/svg", "p", { is: "" }) should be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElement("p", { is: "" }) should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/1999/xhtml", "p", { is: "" }) should not be :defined]
-    expected: FAIL
-
-  [Without browsing context: createElementNS("http://www.w3.org/2000/svg", "p", { is: "" }) should be :defined]
-    expected: FAIL
-