--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -730,32 +730,52 @@ CustomElementRegistry::Define(const nsAS
* 7. If extends is not null, then:
* 1. If extends is a valid custom element name, then throw a
* "NotSupportedError" DOMException.
* 2. If the element interface for extends and the HTML namespace is
* HTMLUnknownElement (e.g., if extends does not indicate an element
* definition in this specification), then throw a "NotSupportedError"
* DOMException.
* 3. Set localName to extends.
+ *
+ * Special note for XUL elements:
+ *
+ * For step 7.1, we'll subject XUL to the same rules as HTML, so that a
+ * custom built-in element will not be extending from a dashed name.
+ * Step 7.2 is disregarded. But, we do check if the name is a dashed name
+ * (i.e. step 2) given that there is no reason for a custom built-in element
+ * type to take on a non-dashed name.
+ * This also ensures the name of the built-in custom element type can never
+ * be the same as the built-in element name, so we don't break the assumption
+ * elsewhere.
*/
nsAutoString localName(aName);
if (aOptions.mExtends.WasPassed()) {
RefPtr<nsAtom> extendsAtom(NS_Atomize(aOptions.mExtends.Value()));
- if (nsContentUtils::IsCustomElementName(extendsAtom, nameSpaceID)) {
+ if (nsContentUtils::IsCustomElementName(extendsAtom, kNameSpaceID_XHTML)) {
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 ||
- tag == eHTMLTag_multicol) {
- aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
- return;
+ if (nameSpaceID == kNameSpaceID_XHTML) {
+ // bgsound and multicol are unknown html element.
+ int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom);
+ if (tag == eHTMLTag_userdefined ||
+ tag == eHTMLTag_bgsound ||
+ tag == eHTMLTag_multicol) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
+ } else { // kNameSpaceID_XUL
+ // As stated above, ensure the name of the customized built-in element
+ // (the one that goes to the |is| attribute) is a dashed name.
+ if (!nsContentUtils::IsNameWithDash(nameAtom)) {
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
}
localName.Assign(aOptions.mExtends.Value());
}
/**
* 8. If this CustomElementRegistry's element definition is running flag is set,
* then throw a "NotSupportedError" DOMException and abort these steps.
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4269,20 +4269,37 @@ 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, NodeInfo()->NamespaceID())) {
- MOZ_ASSERT(type == name);
- } else {
- MOZ_ASSERT(type != name);
+ if (NodeInfo()->NamespaceID() == kNameSpaceID_XHTML) {
+ if (nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML)) {
+ MOZ_ASSERT(type == name);
+ } else {
+ MOZ_ASSERT(type != name);
+ }
+ } else { // kNameSpaceID_XUL
+ // 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.
+ if (type != name) {
+ // Assert that the name of the built-in custom element type is always
+ // a dashed name.
+ MOZ_ASSERT(nsContentUtils::IsNameWithDash(type));
+ }
+ }
}
#endif
slots->mCustomElementData = aData;
}
CustomElementDefinition*
Element::GetCustomElementDefinition() const
{
--- a/dom/base/nsContentCreatorFunctions.h
+++ b/dom/base/nsContentCreatorFunctions.h
@@ -57,17 +57,19 @@ CreateHTMLElement(uint32_t aNodeType,
nsresult
NS_NewMathMLElement(mozilla::dom::Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
#ifdef MOZ_XUL
nsresult
NS_NewXULElement(mozilla::dom::Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
- mozilla::dom::FromParser aFromParser);
+ mozilla::dom::FromParser aFromParser,
+ nsAtom* aIsAtom = nullptr,
+ mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
void
NS_TrustedNewXULElement(mozilla::dom::Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
#endif
nsresult
NS_NewSVGElement(mozilla::dom::Element** aResult,
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3179,23 +3179,18 @@ nsContentUtils::NewURIWithDocumentCharse
aDocument->GetDocumentCharacterSet(),
aBaseURI, sIOService);
}
return NS_NewURI(aResult, aSpec, nullptr, aBaseURI, sIOService);
}
// static
bool
-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;
- }
-
+nsContentUtils::IsNameWithDash(nsAtom* aName)
+{
// 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') {
@@ -3235,16 +3230,29 @@ nsContentUtils::IsCustomElementName(nsAt
(name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
return false;
}
i++;
}
}
+ return hasDash;
+}
+
+// static
+bool
+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;
+ }
+
+ bool hasDash = IsNameWithDash(aName);
if (!hasDash) {
return false;
}
// The custom element name must not be one of the following values:
// annotation-xml
// color-profile
// font-face
@@ -9890,18 +9898,32 @@ nsContentUtils::NewXULOrHTMLElement(Elem
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, kNameSpaceID_XHTML));
- } else {
- isCustomElementName = nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
+ } 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.
+ // if so, set isCustomElementName to false to kick off all the logics
+ // that pick up aIsAtom.
+ if (nsContentUtils::IsNameWithDash(aIsAtom) &&
+ !nsContentUtils::IsNameWithDash(name)) {
+ isCustomElementName = false;
+ } else {
+ isCustomElementName = nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
+ }
+ } else {
+ 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;
}
@@ -9960,19 +9982,21 @@ nsContentUtils::NewXULOrHTMLElement(Elem
ErrorResult rv;
// Step 5.
if (definition->IsCustomBuiltIn()) {
// SetupCustomElement() should be called with an element that don't have
// CustomElementData setup, if not we will hit the assertion in
// SetCustomElementData().
// Built-in element
- MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
- "Custom built-in XUL elements are not supported yet.");
- *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
+ if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
+ *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
+ } else {
+ NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
+ }
(*aResult)->SetCustomElementData(new CustomElementData(typeAtom));
if (synchronousCustomElements) {
CustomElementRegistry::Upgrade(*aResult, definition, rv);
if (rv.MaybeSetPendingException(cx)) {
aes.ReportException();
}
} else {
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -700,16 +700,21 @@ public:
* aDocument.
*/
static nsresult NewURIWithDocumentCharset(nsIURI** aResult,
const nsAString& aSpec,
nsIDocument* aDocument,
nsIURI* aBaseURI);
/**
+ * Returns true if |aName| is a name with dashes.
+ */
+ static bool IsNameWithDash(nsAtom* aName);
+
+ /**
* Returns true if |aName| is a valid name to be registered via
* customElements.define.
*/
static bool IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID);
static nsresult CheckQName(const nsAString& aQualifiedName,
bool aNamespaceAware = true,
const char16_t** aColon = nullptr);
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -181,23 +181,23 @@ nsNameSpaceManager::GetNameSpaceID(nsAto
nsresult
NS_NewElement(Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
FromParser aFromParser,
const nsAString* aIs)
{
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
int32_t ns = ni->NamespaceID();
+ RefPtr<nsAtom> isAtom = aIs ? NS_Atomize(*aIs) : nullptr;
if (ns == kNameSpaceID_XHTML) {
- RefPtr<nsAtom> isAtom = aIs ? NS_Atomize(*aIs) : nullptr;
return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, isAtom);
}
#ifdef MOZ_XUL
if (ns == kNameSpaceID_XUL) {
- return NS_NewXULElement(aResult, ni.forget(), aFromParser);
+ return NS_NewXULElement(aResult, ni.forget(), aFromParser, isAtom);
}
#endif
if (ns == kNameSpaceID_MathML) {
// If the mathml.disabled pref. is true, convert all MathML nodes into
// disabled MathML nodes by swapping the namespace.
if (ni->NodeInfoManager()->MathMLEnabled()) {
return NS_NewMathMLElement(aResult, ni.forget());
}
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -43,16 +43,17 @@
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.h"
#include "mozilla/dom/HTMLEmbedElement.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/XULElementBinding.h"
+#include "mozilla/dom/XULPopupElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ResolveSystemBinding.h"
#include "mozilla/dom/WebIDLGlobalNameHash.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/XrayExpandoClass.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "ipc/ErrorIPCUtils.h"
@@ -3783,35 +3784,42 @@ HTMLConstructor(JSContext* aCx, unsigned
if (!constructor) {
return false;
}
if (constructor != js::CheckedUnwrap(callee)) {
return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
}
} else {
- // Step 5.
- // If the definition is for a customized built-in element, the localName
- // should be one of the ones defined in the specification for this interface.
-
- // Customized built-in elements are not supported for XUL yet.
- if (ns == kNameSpaceID_XUL) {
- return Throw(aCx, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ constructorGetterCallback cb;
+ if (ns == kNameSpaceID_XHTML) {
+ // Step 5.
+ // If the definition is for a customized built-in element, the localName
+ // should be one of the ones defined in the specification for this interface.
+ tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
+ if (tag == eHTMLTag_userdefined) {
+ return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
+ }
+
+ MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
+
+ // If the definition is for a customized built-in element, the active
+ // function should be the localname's element interface.
+ cb = sConstructorGetterCallback[tag];
+ } else { // kNameSpaceID_XUL
+ if (definition->mLocalName == nsGkAtoms::menupopup ||
+ definition->mLocalName == nsGkAtoms::popup ||
+ definition->mLocalName == nsGkAtoms::panel ||
+ definition->mLocalName == nsGkAtoms::tooltip) {
+ cb = XULPopupElementBinding::GetConstructorObject;
+ } else {
+ cb = XULElementBinding::GetConstructorObject;
+ }
}
- tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
- if (tag == eHTMLTag_userdefined) {
- return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
- }
-
- MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
-
- // If the definition is for a customized built-in element, the active
- // function should be the localname's element interface.
- constructorGetterCallback cb = sConstructorGetterCallback[tag];
if (!cb) {
return ThrowErrorMessage(aCx, MSG_ILLEGAL_CONSTRUCTOR);
}
// We want to get the constructor from our global's compartment, not the
// caller compartment.
JSAutoCompartment ac(aCx, global.Get());
JS::Rooted<JSObject*> constructor(aCx, cb(aCx));
--- a/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
+++ b/dom/tests/mochitest/webcomponents/test_xul_custom_element.xul
@@ -24,19 +24,54 @@
}
}
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";
+ class TestWithoutDashExtended extends TestWithoutDash {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ this.textContent = "quux";
+ }
+ }
+ customElements.define("testwithoutdash-extended", TestWithoutDashExtended, { extends: "testwithoutdash" });
+
+ class TestCustomBuiltInElement extends XULElement {
+ constructor() {
+ super();
+ }
+ connectedCallback() {
+ this.textContent = "baz";
+ }
+ }
+ customElements.define("test-built-in-element",
+ TestCustomBuiltInElement, { extends: "axulelement" });
+
+ class TestPopupExtendElement extends XULPopupElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ this.textContent = "quuz";
+ }
+ }
+ customElements.define("test-popup-extend",
+ TestPopupExtendElement, { extends: "popup" });
+
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+ function basicCustomElementCreate() {
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");
let element2 = element.cloneNode(false);
is(element2.textContent, "", "Shouldn't have cloned the textContent");
document.querySelector("#content").appendChild(element2);
@@ -45,31 +80,228 @@
let element3 = new TestCustomElement();
is(element3.localName, "test-custom-element", "Should see the right tag");
is(element3.textContent, "", "Shouldn't have been inserted yet");
is(element3.namespaceURI, XUL_NS, "Should have set the right namespace");
document.querySelector("#content").appendChild(element3);
is(element3.textContent, "foo", "Should have set the textContent");
ok(element3 instanceof TestCustomElement, "Should be an instance of TestCustomElement");
+ }
+ function parserBasicElementUpgrade() {
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");
+ }
+ function tagNameWithoutDash() {
let element5 = document.getElementById("element5");
ok(element5 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
+ }
+
+ function upgradeAfterDefine() {
+ class TestCustomElement1 extends XULElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ this.textContent = "bar";
+ }
+ }
+
+ let element = document.createElementNS(XUL_NS, "test-custom-element-1");
+ ok(!(element instanceof TestCustomElement1), "Should not be an instance of TestCustomElement1");
+ customElements.define("test-custom-element-1", TestCustomElement1);
+ ok(!(element instanceof TestCustomElement1), "Should not be an instance of TestCustomElement1");
+ document.querySelector("#content").appendChild(element);
+ ok(element instanceof TestCustomElement1, "Should be upgraded to an instance of TestCustomElement1");
+ is(element.textContent, "bar", "Should have set the textContent");
+ }
+
+ function basicElementCreateBuiltIn() {
+ let element = document.createElementNS(XUL_NS, "axulelement", { is: "test-built-in-element" });
+ ok(element instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement");
+ is(element.getAttribute("is"), "test-built-in-element", "The |is| attribute of the created element should be the extended type.");
+ document.querySelector("#content").appendChild(element);
+ is(element.textContent, "baz", "Should have set the textContent");
+
+ let element2 = element.cloneNode(false);
+ is(element2.localName, "axulelement", "Should see the right tag");
+ is(element2.getAttribute("is"), "test-built-in-element", "The |is| attribute of the created element should be the extended type.");
+ is(element2.textContent, "", "Shouldn't have cloned the textContent");
+ document.querySelector("#content").appendChild(element2);
+ is(element2.textContent, "baz", "Should have set the textContent");
+ ok(element2 instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement");
+
+ let element3 = new TestCustomBuiltInElement();
+ is(element3.localName, "axulelement", "Should see the right tag");
+ is(element3.textContent, "", "Shouldn't have been inserted yet");
+ is(element3.namespaceURI, XUL_NS, "Should have set the right namespace");
+ document.querySelector("#content").appendChild(element3);
+ is(element3.textContent, "baz", "Should have set the textContent");
+ ok(element3 instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement");
+ }
+
+ function parserBasicElementUpgradeBuiltIn() {
+ let element = document.getElementById("element6");
+ is(element.textContent, "baz",
+ "Parser should have instantiated the custom element.");
+ ok(element instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement");
+ }
+
+ function subclassElementCreateBuiltIn() {
+ let element = document.createElementNS(XUL_NS, "popup", { is: "test-popup-extend" });
+ ok(element instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement");
+ is(element.getAttribute("is"), "test-popup-extend", "The |is| attribute of the created element should be the extended type.");
+ document.querySelector("#content").appendChild(element);
+ is(element.textContent, "quuz", "Should have set the textContent");
+
+ let element2 = element.cloneNode(false);
+ is(element2.localName, "popup", "Should see the right tag");
+ is(element2.getAttribute("is"), "test-popup-extend", "The |is| attribute of the created element should be the extended type.");
+ is(element2.textContent, "", "Shouldn't have cloned the textContent");
+ document.querySelector("#content").appendChild(element2);
+ is(element2.textContent, "quuz", "Should have set the textContent");
+ ok(element2 instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement");
+
+ let element3 = new TestPopupExtendElement();
+ is(element3.localName, "popup", "Should see the right tag");
+ is(element3.textContent, "", "Shouldn't have been inserted yet");
+ is(element3.namespaceURI, XUL_NS, "Should have set the right namespace");
+ document.querySelector("#content").appendChild(element3);
+ is(element3.textContent, "quuz", "Should have set the textContent");
+ ok(element3 instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement");
+ }
+
+ function parserSubclassElementUpgradeBuiltIn() {
+ let element = document.getElementById("element7");
+ is(element.textContent, "quuz",
+ "Parser should have instantiated the custom element.");
+ ok(element instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement");
+ }
+
+ function upgradeAfterDefineBuiltIn() {
+ class TestCustomBuiltInElement1 extends XULElement {
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ this.textContent = "qux";
+ }
+ }
+ let element = document.createElementNS(XUL_NS, "axulelement", { is: "test-built-in-element-1" });
+ ok(!(element instanceof TestCustomBuiltInElement1), "Should not be an instance of TestCustomBuiltInElement1");
+ customElements.define("test-built-in-element-1",
+ TestCustomBuiltInElement1, { extends: "axulelement" });
+ ok(!(element instanceof TestCustomBuiltInElement1), "Should not be an instance of TestCustomBuiltInElement1");
+ document.querySelector("#content").appendChild(element);
+ ok(element instanceof TestCustomBuiltInElement1, "Should be upgraded to an instance of TestCustomBuiltInElement1");
+ is(element.textContent, "qux", "Should have set the textContent");
+ }
+
+ function throwForInvalidBuiltInName() {
+ try {
+ // <axulelement is="testwithoutdashbuiltin" /> is not allowed;
+ // built-in type names need dashes.
+ customElements.define(
+ "testwithoutdashbuiltin", class extends XULElement {}, { extends: "axulelement" });
+ ok(false, "Built-in type name without dash should be rejected.");
+ } catch (e) {
+ ok(true, "Built-in type name without dash is rejected.");
+ }
+ try {
+ // <test-built-in-element-2 is="test-custom-element-2" /> is not allowed;
+ // built-in type tag names forbid dashes
+ customElements.define(
+ "test-built-in-element-2", class extends XULElement {}, { extends: "test-custom-element-2" });
+ ok(false, "Extending from a name with dash should be rejected.");
+ } catch (e) {
+ ok(true, "Extending from a name with dash is rejected.");
+ }
+ }
+
+ function extendingWithoutDashCustomElement() {
+ let element = document.createElementNS(XUL_NS, "testwithoutdash", { is: "testwithoutdash-extended" });
+ ok(element instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended");
+ ok(element instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
+ is(element.getAttribute("is"), "testwithoutdash-extended", "The |is| attribute of the created element should be the extended type.");
+ document.querySelector("#content").appendChild(element);
+ is(element.textContent, "quux", "Should have set the textContent");
+
+ let element2 = element.cloneNode(false);
+ is(element2.localName, "testwithoutdash", "Should see the right tag");
+ is(element2.getAttribute("is"), "testwithoutdash-extended", "The |is| attribute of the created element should be the extended type.");
+ is(element2.textContent, "", "Shouldn't have cloned the textContent");
+ document.querySelector("#content").appendChild(element2);
+ is(element2.textContent, "quux", "Should have set the textContent");
+ ok(element2 instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended");
+ ok(element2 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
+
+ let element3 = new TestWithoutDashExtended();
+ is(element3.localName, "testwithoutdash", "Should see the right tag");
+ is(element3.textContent, "", "Shouldn't have been inserted yet");
+ is(element3.namespaceURI, XUL_NS, "Should have set the right namespace");
+ document.querySelector("#content").appendChild(element3);
+ is(element3.textContent, "quux", "Should have set the textContent");
+ ok(element3 instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended");
+ ok(element3 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash");
+ }
+
+ function nonCustomElementCreate() {
+ // All of these should be created as plain XUL elements without hitting
+ // any assertions.
+ let elements = [
+ document.createElementNS(XUL_NS, "axulelement", { is: "test-custom-element" }),
+ document.createElementNS(XUL_NS, "axulelement", { is: "testwithoutdash" }),
+ document.createElementNS(XUL_NS, "axulelement", { is: "test-custom-element-1" }),
+ document.createElementNS(XUL_NS, "name-with-dash", { is: "name-with-dash" }),
+ document.createElementNS(XUL_NS, "name-with-dash", { is: "another-name-with-dash" }),
+ document.createElementNS(XUL_NS, "testwithoutdash-extended"),
+ document.createElementNS(XUL_NS, "test-built-in-element"),
+ document.createElementNS(XUL_NS, "test-popup-extend"),
+ document.createElementNS(XUL_NS, "test-built-in-element-1")];
+
+ for (let element of elements) {
+ is(Object.getPrototypeOf(element), XULElement.prototype,
+ `<${element.localName} is="${element.getAttribute("is")}" /> should not be a custom element.`);
+ }
+ }
+
+ function runTest() {
+ basicCustomElementCreate();
+ parserBasicElementUpgrade();
+
+ tagNameWithoutDash();
+ upgradeAfterDefine();
+
+ basicElementCreateBuiltIn();
+ parserBasicElementUpgradeBuiltIn();
+
+ subclassElementCreateBuiltIn();
+ parserSubclassElementUpgradeBuiltIn();
+
+ upgradeAfterDefineBuiltIn();
+
+ throwForInvalidBuiltInName();
+ extendingWithoutDashCustomElement();
+
+ nonCustomElementCreate();
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"/>
+ <axulelement id="element6" is="test-built-in-element" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
+ <popup id="element7" is="test-popup-extend" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/>
</div>
<pre id="test"></pre>
</body>
</window>
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -186,17 +186,20 @@ nsXULElement* nsXULElement::Construct(al
already_AddRefed<nsXULElement>
nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
mozilla::dom::NodeInfo *aNodeInfo,
bool aIsScriptable,
bool aIsRoot)
{
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
nsCOMPtr<Element> baseElement;
- NS_NewXULElement(getter_AddRefs(baseElement), ni.forget(), dom::FROM_PARSER_NETWORK);
+ NS_NewXULElement(getter_AddRefs(baseElement),
+ ni.forget(),
+ dom::FROM_PARSER_NETWORK,
+ aPrototype->mIsAtom);
if (baseElement) {
nsXULElement* element = FromNode(baseElement);
if (aPrototype->mHasIdAttribute) {
element->SetHasID();
}
if (aPrototype->mHasClassAttribute) {
@@ -261,31 +264,32 @@ nsXULElement::CreateFromPrototype(nsXULP
aIsScriptable, aIsRoot);
element.forget(aResult);
return NS_OK;
}
nsresult
NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
- FromParser aFromParser)
+ FromParser aFromParser, nsAtom* aIsAtom,
+ mozilla::dom::CustomElementDefinition* aDefinition)
{
RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
MOZ_ASSERT(nodeInfo, "need nodeinfo for non-proto Create");
NS_ASSERTION(nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
"Trying to create XUL elements that don't have the XUL namespace");
nsIDocument* doc = nodeInfo->GetDocument();
if (doc && !doc->AllowXULXBL()) {
return NS_ERROR_NOT_AVAILABLE;
}
- return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, nullptr, nullptr);
+ return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIsAtom, aDefinition);
}
void
NS_TrustedNewXULElement(Element** aResult,
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
{
RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
MOZ_ASSERT(ni, "need nodeinfo for non-proto Create");
@@ -2288,16 +2292,22 @@ nsXULPrototypeElement::SetAttrAt(uint32_
!aValue.IsEmpty()) {
mHasIdAttribute = true;
// Store id as atom.
// id="" means that the element has no id. Not that it has
// emptystring as id.
mAttributes[aPos].mValue.ParseAtom(aValue);
return NS_OK;
+ } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::is)) {
+ // Store is as atom.
+ mAttributes[aPos].mValue.ParseAtom(aValue);
+ mIsAtom = mAttributes[aPos].mValue.GetAtomValue();
+
+ return NS_OK;
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) {
mHasClassAttribute = true;
// Compute the element's class list
mAttributes[aPos].mValue.ParseAtomArray(aValue);
return NS_OK;
} else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
mHasStyleAttribute = true;
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -145,17 +145,18 @@ class nsXULPrototypeElement : public nsX
{
public:
nsXULPrototypeElement()
: nsXULPrototypeNode(eType_Element),
mNumAttributes(0),
mHasIdAttribute(false),
mHasClassAttribute(false),
mHasStyleAttribute(false),
- mAttributes(nullptr)
+ mAttributes(nullptr),
+ mIsAtom(nullptr)
{
}
virtual ~nsXULPrototypeElement()
{
Unlink();
}
@@ -188,16 +189,17 @@ public:
RefPtr<mozilla::dom::NodeInfo> mNodeInfo;
uint32_t mNumAttributes:29;
uint32_t mHasIdAttribute:1;
uint32_t mHasClassAttribute:1;
uint32_t mHasStyleAttribute:1;
nsXULPrototypeAttribute* mAttributes; // [OWNER]
+ RefPtr<nsAtom> mIsAtom;
};
namespace mozilla {
namespace dom {
class XULDocument;
} // namespace dom
} // namespace mozilla