Bug 1460962 - Support customized built-in element in XUL r=smaug
This patch enables us to specify a custom element type with |is| attribute or property when creating a XUL element. Because non-dashed names are valid custom element names in XUL (bug 1446247), other checks has to modified accordingly. The checks I am settling with are 1) Forbids the custom built-in element names to be a non-dashed name. 2) Forbids the custom built-in element to extend a dashed built-in element name. This also ensures the custom built-in element types don't take on the same name as the element name it extends. MozReview-Commit-ID: GCQ9RnfvvrC
This commit is contained in:
@@ -766,22 +766,42 @@ CustomElementRegistry::Define(const nsAString& aName,
|
||||
* 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());
|
||||
|
||||
@@ -4278,10 +4278,27 @@ Element::SetCustomElementData(CustomElementData* aData)
|
||||
#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;
|
||||
|
||||
@@ -62,7 +62,9 @@ NS_NewMathMLElement(mozilla::dom::Element** aResult,
|
||||
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,
|
||||
|
||||
@@ -3183,13 +3183,8 @@ nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
|
||||
|
||||
// static
|
||||
bool
|
||||
nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID)
|
||||
nsContentUtils::IsNameWithDash(nsAtom* aName)
|
||||
{
|
||||
// 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)*
|
||||
@@ -3239,6 +3234,19 @@ nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -9886,8 +9894,22 @@ nsContentUtils::NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* a
|
||||
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();
|
||||
@@ -9956,9 +9978,11 @@ nsContentUtils::NewXULOrHTMLElement(Element** aResult, mozilla::dom::NodeInfo* a
|
||||
// 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);
|
||||
|
||||
@@ -703,6 +703,11 @@ public:
|
||||
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.
|
||||
|
||||
@@ -186,13 +186,13 @@ NS_NewElement(Element** aResult,
|
||||
{
|
||||
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) {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#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"
|
||||
@@ -3807,25 +3808,32 @@ HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
// Customized built-in elements are not supported for XUL yet.
|
||||
if (ns == kNameSpaceID_XUL) {
|
||||
return Throw(aCx, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,44 @@
|
||||
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");
|
||||
@@ -50,14 +85,209 @@
|
||||
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();
|
||||
}
|
||||
@@ -69,6 +299,8 @@
|
||||
<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>
|
||||
|
||||
@@ -190,7 +190,10 @@ nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
|
||||
{
|
||||
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);
|
||||
@@ -265,7 +268,8 @@ nsXULElement::CreateFromPrototype(nsXULPrototypeElement* aPrototype,
|
||||
|
||||
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;
|
||||
|
||||
@@ -279,7 +283,7 @@ NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& a
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, nullptr, nullptr);
|
||||
return nsContentUtils::NewXULOrHTMLElement(aResult, nodeInfo, aFromParser, aIsAtom, aDefinition);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -2291,6 +2295,12 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
|
||||
// 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;
|
||||
|
||||
@@ -150,7 +150,8 @@ public:
|
||||
mHasIdAttribute(false),
|
||||
mHasClassAttribute(false),
|
||||
mHasStyleAttribute(false),
|
||||
mAttributes(nullptr)
|
||||
mAttributes(nullptr),
|
||||
mIsAtom(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -193,6 +194,7 @@ public:
|
||||
uint32_t mHasClassAttribute:1;
|
||||
uint32_t mHasStyleAttribute:1;
|
||||
nsXULPrototypeAttribute* mAttributes; // [OWNER]
|
||||
RefPtr<nsAtom> mIsAtom;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
Reference in New Issue
Block a user