Bug 1446247 - Pass namespace into IsCustomElementName to allow for non-dashed XUL elements;r=smaug draft
authorBrian Grinstead <bgrinstead@mozilla.com>
Thu, 29 Mar 2018 09:34:56 -0700
changeset 774811 6d097556d05652cbe5d6111fd1209e038ce60104
parent 774444 6aa3b57955fed5e137d0306478e1a4b424a6d392
push id104514
push userbgrinstead@mozilla.com
push dateThu, 29 Mar 2018 16:35:19 +0000
reviewerssmaug
bugs1446247
milestone61.0a1
Bug 1446247 - Pass namespace into IsCustomElementName to allow for non-dashed XUL elements;r=smaug This will make it possible to migrate existing bindings without also needing to mass-rewrite frontend code at the same time. MozReview-Commit-ID: IBBqC4eeDDX
dom/base/CustomElementRegistry.cpp
dom/base/Element.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -677,18 +677,20 @@ CustomElementRegistry::Define(const nsAS
     aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
     return;
   }
 
   /**
    * 2. If name is not a valid custom element name, then throw a "SyntaxError"
    *    DOMException and abort these steps.
    */
+  nsIDocument* doc = mWindow->GetExtantDoc();
+  uint32_t nameSpaceID = doc ? doc->GetDefaultNamespaceID() : kNameSpaceID_XHTML;
   RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
-  if (!nsContentUtils::IsCustomElementName(nameAtom)) {
+  if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   /**
    * 3. If this CustomElementRegistry contains an entry with name name, then
    *    throw a "NotSupportedError" DOMException and abort these steps.
    */
@@ -720,17 +722,17 @@ CustomElementRegistry::Define(const nsAS
    *       HTMLUnknownElement (e.g., if extends does not indicate an element
    *       definition in this specification), then throw a "NotSupportedError"
    *       DOMException.
    *    3. Set localName to extends.
    */
   nsAutoString localName(aName);
   if (aOptions.mExtends.WasPassed()) {
     RefPtr<nsAtom> extendsAtom(NS_Atomize(aOptions.mExtends.Value()));
-    if (nsContentUtils::IsCustomElementName(extendsAtom)) {
+    if (nsContentUtils::IsCustomElementName(extendsAtom, nameSpaceID)) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
 
     // bgsound and multicol are unknown html element.
     int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom);
     if (tag == eHTMLTag_userdefined ||
         tag == eHTMLTag_bgsound ||
@@ -960,17 +962,19 @@ CustomElementRegistry::WhenDefined(const
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
   RefPtr<Promise> promise = Promise::Create(global, aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
 
   RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
-  if (!nsContentUtils::IsCustomElementName(nameAtom)) {
+  nsIDocument* doc = mWindow->GetExtantDoc();
+  uint32_t nameSpaceID = doc ? doc->GetDefaultNamespaceID() : kNameSpaceID_XHTML;
+  if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
     promise->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
     return promise.forget();
   }
 
   if (mCustomDefinitions.GetWeak(nameAtom)) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1191,17 +1191,17 @@ Element::AttachShadow(const ShadowRootIn
   /**
    * 2. If context object’s local name is not
    *      a valid custom element name, "article", "aside", "blockquote",
    *      "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
    *      "header", "main" "nav", "p", "section", or "span",
    *    then throw a "NotSupportedError" DOMException.
    */
   nsAtom* nameAtom = NodeInfo()->NameAtom();
-  if (!(nsContentUtils::IsCustomElementName(nameAtom) ||
+  if (!(nsContentUtils::IsCustomElementName(nameAtom, NodeInfo()->NamespaceID()) ||
         nameAtom == nsGkAtoms::article ||
         nameAtom == nsGkAtoms::aside ||
         nameAtom == nsGkAtoms::blockquote ||
         nameAtom == nsGkAtoms::body ||
         nameAtom == nsGkAtoms::div ||
         nameAtom == nsGkAtoms::footer ||
         nameAtom == nsGkAtoms::h1 ||
         nameAtom == nsGkAtoms::h2 ||
@@ -4318,17 +4318,17 @@ Element::ClearServoData(nsIDocument* aDo
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
   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 (nsContentUtils::IsCustomElementName(name)) {
+    if (nsContentUtils::IsCustomElementName(name, NodeInfo()->NamespaceID())) {
       MOZ_ASSERT(type == name);
     } else {
       MOZ_ASSERT(type != name);
     }
   #endif
   slots->mCustomElementData = aData;
 }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3188,18 +3188,23 @@ nsContentUtils::NewURIWithDocumentCharse
                      aDocument->GetDocumentCharacterSet(),
                      aBaseURI, sIOService);
   }
   return NS_NewURI(aResult, aSpec, nullptr, aBaseURI, sIOService);
 }
 
 // static
 bool
-nsContentUtils::IsCustomElementName(nsAtom* aName)
-{
+nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID)
+{
+  // Allow non-dashed names in XUL for XBL to Custom Element migrations.
+  if (aNameSpaceID == kNameSpaceID_XUL) {
+    return true;
+  }
+
   // A valid custom element name is a sequence of characters name which
   // must match the PotentialCustomElementName production:
   // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
   const char16_t* name = aName->GetUTF16String();
   uint32_t len = aName->GetLength();
   bool hasDash = false;
 
   if (!len || name[0] < 'a' || name[0] > 'z') {
@@ -9978,19 +9983,19 @@ nsContentUtils::NewXULOrHTMLElement(Elem
              "Can only create XUL or XHTML elements.");
 
   nsAtom *name = nodeInfo->NameAtom();
   int32_t tag = eHTMLTag_unknown;
   bool isCustomElementName = false;
   if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
     tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
     isCustomElementName = (tag == eHTMLTag_userdefined &&
-                           nsContentUtils::IsCustomElementName(name));
+                           nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
   } else {
-    isCustomElementName = nsContentUtils::IsCustomElementName(name);
+    isCustomElementName = nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
   }
 
   RefPtr<nsAtom> tagAtom = nodeInfo->NameAtom();
   RefPtr<nsAtom> typeAtom;
   bool isCustomElement = isCustomElementName || aIsAtom;
   if (isCustomElement) {
     typeAtom = isCustomElementName ? tagAtom.get() : aIsAtom;
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -705,17 +705,17 @@ public:
                                             const nsAString& aSpec,
                                             nsIDocument* aDocument,
                                             nsIURI* aBaseURI);
 
   /**
    * Returns true if |aName| is a valid name to be registered via
    * customElements.define.
    */
-  static bool IsCustomElementName(nsAtom* aName);
+  static bool IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID);
 
   static nsresult CheckQName(const nsAString& aQualifiedName,
                              bool aNamespaceAware = true,
                              const char16_t** aColon = nullptr);
 
   static nsresult SplitQName(const nsIContent* aNamespaceResolver,
                              const nsString& aQName,
                              int32_t *aNamespace, nsAtom **aLocalName);
--- a/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
+++ b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
@@ -21,16 +21,19 @@
 
       connectedCallback() {
         this.textContent = "foo";
       }
     }
 
     customElements.define("test-custom-element", TestCustomElement);
 
+    class TestWithoutDash extends XULElement { }
+    customElements.define("testwithoutdash", TestWithoutDash);
+
     function runTest() {
       const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
       let element = document.createElementNS(XUL_NS, "test-custom-element");
       document.querySelector("#content").appendChild(element);
       is(element.textContent, "foo", "Should have set the textContent");
       ok(element instanceof TestCustomElement, "Should be an instance of TestCustomElement");
 
@@ -48,21 +51,25 @@
       is(element3.textContent, "foo", "Should have set the textContent");
       ok(element3 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
 
       let element4 = document.getElementById("element4");
       is(element4.textContent, "foo",
          "Parser should have instantiated the custom element.");
       ok(element4 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
 
+      let element5 = document.getElementById("element5");
+      ok(element5 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
+
       SimpleTest.finish();
     }
   ]]>
   </script>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
     <p id="display"></p>
     <div id="content" style="display: none">
       <test-custom-element id="element4" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
+      <testwithoutdash id="element5" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
     </div>
     <pre id="test"></pre>
   </body>
 </window>