Bug 1111633 - Unresolved pseudo class. r=bz
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Tue, 13 Jan 2015 19:58:06 +0100
changeset 223757 95a7e60a6970
parent 223756 ca2c472d1aae
child 223758 3d23e775033a
push id28107
push userryanvm@gmail.com
push dateWed, 14 Jan 2015 21:17:38 +0000
treeherdermozilla-central@af5716d852c6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1111633
milestone38.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 1111633 - Unresolved pseudo class. r=bz
dom/base/Element.h
dom/base/nsDocument.cpp
dom/events/EventStates.h
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html
layout/style/nsCSSPseudoClassList.h
testing/web-platform/meta/custom-elements/instantiating-custom-elements/custom-element-type-local-name-and-is-attribute.html.ini
testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag.html.ini
testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-type-extension.html.ini
testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector-all.html.ini
testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector.html.ini
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -50,16 +50,17 @@ class ContentUnbinder;
 class nsContentList;
 class nsDOMSettableTokenList;
 class nsDOMTokenList;
 struct nsRect;
 class nsFocusManager;
 class nsGlobalWindow;
 class nsICSSDeclaration;
 class nsISMILAttr;
+class nsDocument;
 
 namespace mozilla {
 namespace dom {
   struct ScrollIntoViewOptions;
   struct ScrollToOptions;
 } // namespace dom
 } // namespace mozilla
 
@@ -393,16 +394,17 @@ protected:
   }
 
 private:
   // Need to allow the ESM, nsGlobalWindow, and the focus manager to
   // set our state
   friend class mozilla::EventStateManager;
   friend class ::nsGlobalWindow;
   friend class ::nsFocusManager;
+  friend class ::nsDocument;
 
   // Also need to allow Link to call UpdateLinkState.
   friend class Link;
 
   void NotifyStateChange(EventStates aStates);
 
   void NotifyStyleStateChange(EventStates aStates);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5839,16 +5839,17 @@ nsDocument::RegisterUnresolvedElement(El
   if (!unresolved) {
     unresolved = new nsTArray<nsRefPtr<Element>>();
     // Ownership of unresolved is taken by mCandidatesMap.
     mRegistry->mCandidatesMap.Put(&key, unresolved);
   }
 
   nsRefPtr<Element>* elem = unresolved->AppendElement();
   *elem = aElement;
+  aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
 
   return NS_OK;
 }
 
 namespace {
 
 class ProcessStackRunner MOZ_FINAL : public nsIRunnable
 {
@@ -6269,21 +6270,27 @@ nsDocument::RegisterElement(JSContext* a
 
   // Do element upgrade.
   nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
   mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
   if (candidates) {
     for (size_t i = 0; i < candidates->Length(); ++i) {
       Element *elem = candidates->ElementAt(i);
 
+      elem->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
+
       // Make sure that the element name matches the name in the definition.
       // (e.g. a definition for x-button extending button should match
       // <button is="x-button"> but not <x-button>.
-      if (elem->NodeInfo()->NameAtom() != nameAtom) {
-        // Skip over this element because definition does not apply.
+      // Note: we also check the tag name, because if it's not the above
+      // mentioned case, it can be that only the |is| property has been
+      // changed, which we should ignore by the spec.
+      if (elem->NodeInfo()->NameAtom() != nameAtom &&
+          elem->Tag() == nameAtom) {
+        //Skip over this element because definition does not apply.
         continue;
       }
 
       nsWrapperCache* cache;
       CallQueryInterface(elem, &cache);
       MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
 
       JS::RootedObject wrapper(aCx);
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -260,27 +260,30 @@ private:
 // Element is ltr (for :dir pseudo-class)
 #define NS_EVENT_STATE_LTR NS_DEFINE_EVENT_STATE_MACRO(42)
 // Element is rtl (for :dir pseudo-class)
 #define NS_EVENT_STATE_RTL NS_DEFINE_EVENT_STATE_MACRO(43)
 // Handler for play preview plugin
 #define NS_EVENT_STATE_TYPE_PLAY_PREVIEW NS_DEFINE_EVENT_STATE_MACRO(44)
 // Element is highlighted (devtools inspector)
 #define NS_EVENT_STATE_DEVTOOLS_HIGHLIGHTED NS_DEFINE_EVENT_STATE_MACRO(45)
+// Element is an unresolved custom element candidate
+#define NS_EVENT_STATE_UNRESOLVED NS_DEFINE_EVENT_STATE_MACRO(46)
 
 // Event state that is used for values that need to be parsed but do nothing.
 #define NS_EVENT_STATE_IGNORE NS_DEFINE_EVENT_STATE_MACRO(63)
 
 /**
  * NOTE: do not go over 63 without updating EventStates::InternalType!
  */
 
 #define DIRECTION_STATES (NS_EVENT_STATE_LTR | NS_EVENT_STATE_RTL)
 
 #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS |     \
                             NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER |   \
                             NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
-                            NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR)
+                            NS_EVENT_STATE_FULL_SCREEN | NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | \
+                            NS_EVENT_STATE_UNRESOLVED)
 
 #define INTRINSIC_STATES (~ESM_MANAGED_STATES)
 
 #endif // mozilla_EventStates_h_
 
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -31,8 +31,9 @@ support-files =
 [test_shadowroot.html]
 [test_shadowroot_inert_element.html]
 [test_shadowroot_host.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_multiple_shadow.html]
 [test_shadowroot_style_order.html]
 [test_shadowroot_youngershadowroot.html]
 [test_style_fallback_content.html]
+[test_unresolved_pseudo_class.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_unresolved_pseudo_class.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1111633
+-->
+<head>
+  <title>Test template element in stale document.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <style>
+  :unresolved {
+    color: rgb(0, 0, 255);
+    background-color: rgb(0, 0, 255);
+  }
+
+  x-foo { color: rgb(255, 0, 0); }
+
+  [is="x-del"]:not(:unresolved) { color: rgb(255, 0, 0); }
+
+  [is="x-bar"]:not(:unresolved) { color: rgb(255, 0, 0); }
+
+  [is="x-bar"]:unresolved { background-color: rgb(255, 0, 0); }
+
+  x-baz:not(:unresolved) {
+    color: rgb(255, 0, 0);
+    background-color: rgb(255, 0, 0);
+  }
+
+  span { color: rgb(0,255,0); }
+
+  x-foo:unresolved + span { color: rgb(255,0,0); }
+
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1111633">Bug 1111633</a>
+<div id="container"></div>
+<x-foo id="foo"></x-foo>
+<span id="span1">This text should be green</span>
+<span id="bar" is="x-bar"></span>
+<x-baz id="baz"></x-baz>
+<span id="del" is="x-del"></span>
+<script>
+
+// Before registerElement
+var foo = document.querySelector('#foo');
+is(getComputedStyle(foo).color, "rgb(0, 0, 255)", "foo - color");
+is(getComputedStyle(foo).backgroundColor, "rgb(0, 0, 255)", "foo - backgroundColor");
+
+var bar = document.querySelector('#bar');
+is(getComputedStyle(bar).color, "rgb(0, 0, 255)", "bar - color");
+is(getComputedStyle(bar).backgroundColor, "rgb(255, 0, 0)", "bar - backgroundColor");
+
+var baz = document.querySelector('#baz');
+is(getComputedStyle(baz).color, "rgb(0, 0, 255)", "baz - color");
+is(getComputedStyle(baz).backgroundColor, "rgb(0, 0, 255)", "baz - backgroundColor");
+
+var span1 = document.querySelector('#span1');
+is(getComputedStyle(span1).color, "rgb(255, 0, 0)", "span1 - color");
+
+var Foo = document.registerElement('x-foo', { prototype: Object.create(HTMLElement.prototype) });
+
+var Bar = document.registerElement('x-bar', { extends: 'span', prototype: Object.create(HTMLSpanElement.prototype) });
+
+var Baz = document.registerElement('x-baz', { prototype: Object.create(HTMLElement.prototype) });
+
+// After registerElement
+is(getComputedStyle(foo).color, "rgb(255, 0, 0)",
+   "foo - color (after registerElement)");
+
+is(getComputedStyle(bar).color,
+   "rgb(255, 0, 0)", "bar - color (after registerElement)");
+
+is(getComputedStyle(baz).color,
+   "rgb(255, 0, 0)", "baz - color (after registerElement)");
+is(getComputedStyle(baz).backgroundColor,
+   "rgb(255, 0, 0)", "baz - backgroundColor (after registerElement)");
+
+is(getComputedStyle(span1).color, "rgb(0, 255, 0)", "span1 - color (after registerElement)");
+
+// After tree removal
+var del = document.querySelector('#del');
+is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color");
+var par = del.parentNode;
+par.removeChild(del);
+// Changing is attribute after creation should not change the type
+// of a custom element, even if the element was removed and re-append to the tree.
+del.setAttribute("is", "foobar");
+par.appendChild(del);
+is(getComputedStyle(del).color, "rgb(0, 0, 255)", "del - color (after reappend)");
+var Del = document.registerElement('x-del', { prototype: Object.create(HTMLSpanElement.prototype) });
+// [is="x-del"] will not match any longer so the rule of span will apply
+is(getComputedStyle(del).color, "rgb(0, 255, 0)", "del - color (after registerElement)");
+// but the element should have been upgraded:
+ok(del instanceof Del, "element was upgraded correctly after changing |is|");
+
+</script>
+</body>
+</html>
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -85,16 +85,19 @@ CSS_PSEUDO_CLASS(onlyOfType, ":only-of-t
 CSS_PSEUDO_CLASS(nthChild, ":nth-child", 0, "")
 CSS_PSEUDO_CLASS(nthLastChild, ":nth-last-child", 0, "")
 CSS_PSEUDO_CLASS(nthOfType, ":nth-of-type", 0, "")
 CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "")
 
 // Match nodes that are HTML but not XHTML
 CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")
 
+// Match all custom elements whose created callback has not yet been invoked
+ CSS_STATE_PSEUDO_CLASS(unresolved, ":unresolved", 0, "", NS_EVENT_STATE_UNRESOLVED)
+
 // Matches nodes that are in a native-anonymous subtree (i.e., nodes in
 // a subtree of C++ anonymous content constructed by Gecko for its own
 // purposes).
 CSS_PSEUDO_CLASS(mozNativeAnonymous, ":-moz-native-anonymous",
                  CSS_PSEUDO_CLASS_UA_SHEET_ONLY, "")
 
 // Matches anything when the specified look-and-feel metric is set
 CSS_PSEUDO_CLASS(mozSystemMetric, ":-moz-system-metric", 0, "")
--- a/testing/web-platform/meta/custom-elements/instantiating-custom-elements/custom-element-type-local-name-and-is-attribute.html.ini
+++ b/testing/web-platform/meta/custom-elements/instantiating-custom-elements/custom-element-type-local-name-and-is-attribute.html.ini
@@ -4,8 +4,11 @@
     expected: FAIL
 
   [Custom element type must be taken from the local name of the custom element even if IS attribute provided. The element extends HTML5 elements, IS attribute refers to another custom element type.]
     expected: FAIL
 
   [Test custom element type after changing IS attribute value. Element is HTML5 element with IS attribute referring to custom element type]
     expected: FAIL
 
+  [Custom element type must be taken from the local name of the element even if IS attribute provided. There's no definition for the value of IS attribute at first]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[unresolved-element-pseudoclass-css-test-custom-tag.html]
-  type: reftest
-  reftype: ==
-  refurl: /custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag-ref.html
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-type-extension.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[unresolved-element-pseudoclass-css-test-type-extension.html]
-  type: reftest
-  reftype: ==
-  refurl: /custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-type-extension-ref.html
-  expected: FAIL
--- a/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector-all.html.ini
+++ b/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector-all.html.ini
@@ -1,26 +1,7 @@
 [unresolved-element-pseudoclass-matching-query-selector-all.html]
   type: testharness
-  [Test that single unresolved custom element is accessible by Document.querySelectorAll(\':unresolved\')]
-    expected: FAIL
-
-  [Test that single registered custom element is not accessible by :unresolved]
-    expected: FAIL
-
-  [If there are more than one unresolved custom element then all of them accessible by Document.querySelectorAll(\':unresolved\')]
-    expected: FAIL
-
-  [Unresolved custom element, created via Document.createElement(), should be accessible by Document.querySelectorAll(\':unresolved\')]
-    expected: FAIL
-
-  [All unresolved custom element including nested ones are accessible by Document.querySelectorAll(\':unresolved\')]
-    expected: FAIL
-
-  [Unresolved custom element should be accessible by Document.querySelectorAll(\':unresolved\') in a loaded document]
+  [Custom element type must be taken from the local name of the element even if IS attribute provided. There's no definition for the value of IS attribute at first]
     expected: FAIL
 
   [Test that Document.querySelectorAll(\':unresolved\') returns unresolved custom elements, extending HTML elements by IS attribute]
     expected: FAIL
-
-  [Test Document.querySelectorAll(\':unresolved\') returns mix of custom elements of different types]
-    expected: FAIL
-
--- a/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector.html.ini
+++ b/testing/web-platform/meta/custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-matching-query-selector.html.ini
@@ -1,26 +1,5 @@
 [unresolved-element-pseudoclass-matching-query-selector.html]
   type: testharness
-  [Test that unresolved custom element is accessible by Document.querySelector(\':unresolved\')]
-    expected: FAIL
-
-  [Test that registered custom element are not accessible by :unresolved]
-    expected: FAIL
-
-  [If there are more than one unresolved custom element, all of them should be accessible by :unresolved pseudoclass]
-    expected: FAIL
-
-  [Unresolved custom element, created via Document.createElement(), should be accessible by Document.querySelector(\':unresolved\')]
-    expected: FAIL
-
-  [Unresolved custom element inside div element should be accessible by :unresolved pseudoclass]
-    expected: FAIL
-
-  [All unresolved custom element including nested ones should be accessible by Document.querySelector(\':unresolved\')]
-    expected: FAIL
-
-  [Document.querySelector(): Unresolved custom element should be accessible by :unresolved in loaded document]
-    expected: FAIL
-
   [Test that Document.querySelector(\':unresolved\') returns custom element, extending HTML elements by IS attribute]
     expected: FAIL