mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 1081039 - cloneNode on a custom element should call createdCallback if cloned in a document with a custom element definition. r=smaug
This commit is contained in:
parent
5014637e84
commit
b66c6ab07e
@ -5424,23 +5424,30 @@ nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::SwizzleCustomElement(Element* aElement,
|
||||
const nsAString& aTypeExtension,
|
||||
uint32_t aNamespaceID,
|
||||
ErrorResult& rv)
|
||||
nsDocument::SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension)
|
||||
{
|
||||
nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
|
||||
nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
|
||||
if (!mRegistry || tagAtom == typeAtom) {
|
||||
if (!mRegistry) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
|
||||
nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
|
||||
do_GetAtom(*aTypeExtension) : tagAtom;
|
||||
|
||||
if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
|
||||
// Custom element setup in the parser happens after the "is"
|
||||
// attribute is added.
|
||||
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
|
||||
}
|
||||
|
||||
CustomElementDefinition* data;
|
||||
CustomElementHashKey key(aNamespaceID, typeAtom);
|
||||
if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
|
||||
// The type extension doesn't exist in the registry,
|
||||
// thus we don't need to swizzle, but it is possibly
|
||||
// an upgrade candidate.
|
||||
// thus we don't need to enqueue callback or adjust
|
||||
// the "is" attribute, but it is possibly an upgrade candidate.
|
||||
RegisterUnresolvedElement(aElement, typeAtom);
|
||||
return;
|
||||
}
|
||||
@ -5452,11 +5459,6 @@ nsDocument::SwizzleCustomElement(Element* aElement,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
|
||||
// Swizzling in the parser happens after the "is" attribute is added.
|
||||
aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
|
||||
}
|
||||
|
||||
// Enqueuing the created callback will set the CustomElementData on the
|
||||
// element, causing prototype swizzling to occur in Element::WrapObject.
|
||||
EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
|
||||
@ -5472,10 +5474,9 @@ nsDocument::CreateElement(const nsAString& aTagName,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SwizzleCustomElement(elem, aTypeExtension,
|
||||
GetDefaultNamespaceID(), rv);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
if (!aTagName.Equals(aTypeExtension)) {
|
||||
// Custom element type can not extend itself.
|
||||
SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
|
||||
}
|
||||
|
||||
return elem.forget();
|
||||
@ -5540,9 +5541,9 @@ nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
|
||||
}
|
||||
}
|
||||
|
||||
SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
|
||||
if (rv.Failed()) {
|
||||
return nullptr;
|
||||
if (!aQualifiedName.Equals(aTypeExtension)) {
|
||||
// A custom element type can not extend itself.
|
||||
SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
|
||||
}
|
||||
|
||||
return elem.forget();
|
||||
@ -5769,12 +5770,12 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
||||
getter_AddRefs(newElement));
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
|
||||
ErrorResult errorResult;
|
||||
nsCOMPtr<Element> element = do_QueryInterface(newElement);
|
||||
document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
|
||||
errorResult);
|
||||
if (errorResult.Failed()) {
|
||||
return true;
|
||||
if (definition->mLocalName != typeAtom) {
|
||||
// This element is a custom element by extension, thus we need to
|
||||
// do some special setup. For non-extended custom elements, this happens
|
||||
// when the element is created.
|
||||
document->SetupCustomElement(element, definition->mNamespaceID, &elemName);
|
||||
}
|
||||
|
||||
rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
|
||||
@ -6095,7 +6096,7 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
|
||||
nsCOMPtr<nsIAtom> nameAtom;;
|
||||
nsCOMPtr<nsIAtom> nameAtom;
|
||||
int32_t namespaceID = kNameSpaceID_XHTML;
|
||||
JS::Rooted<JSObject*> protoObject(aCx);
|
||||
{
|
||||
|
@ -1557,11 +1557,12 @@ private:
|
||||
public:
|
||||
static void ProcessBaseElementQueue();
|
||||
|
||||
// Modify the prototype and "is" attribute of newly created custom elements.
|
||||
virtual void SwizzleCustomElement(Element* aElement,
|
||||
const nsAString& aTypeExtension,
|
||||
uint32_t aNamespaceID,
|
||||
mozilla::ErrorResult& rv);
|
||||
// Enqueue created callback or register upgrade candidate for
|
||||
// newly created custom elements, possibly extending an existing type.
|
||||
// ex. <x-button>, <button is="x-button> (type extension)
|
||||
virtual void SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension);
|
||||
|
||||
static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
|
@ -2237,10 +2237,9 @@ public:
|
||||
Element* aCustomElement,
|
||||
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition = nullptr) = 0;
|
||||
virtual void SwizzleCustomElement(Element* aElement,
|
||||
const nsAString& aTypeExtension,
|
||||
uint32_t aNamespaceID,
|
||||
mozilla::ErrorResult& rv) = 0;
|
||||
virtual void SetupCustomElement(Element* aElement,
|
||||
uint32_t aNamespaceID,
|
||||
const nsAString* aTypeExtension = nullptr) = 0;
|
||||
virtual void
|
||||
RegisterElement(JSContext* aCx, const nsAString& aName,
|
||||
const mozilla::dom::ElementRegistrationOptions& aOptions,
|
||||
|
@ -363,6 +363,24 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep,
|
||||
rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (clone->IsElement()) {
|
||||
// The cloned node may be a custom element that may require
|
||||
// enqueing created callback and prototype swizzling.
|
||||
Element* elem = clone->AsElement();
|
||||
if (nsContentUtils::IsCustomElementName(nodeInfo->NameAtom())) {
|
||||
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID());
|
||||
} else {
|
||||
// Check if node may be custom element by type extension.
|
||||
// ex. <button is="x-button">
|
||||
nsAutoString extension;
|
||||
if (elem->GetAttr(kNameSpaceID_None, nsGkAtoms::is, extension) &&
|
||||
!extension.IsEmpty()) {
|
||||
elem->OwnerDoc()->SetupCustomElement(elem, nodeInfo->NamespaceID(),
|
||||
&extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aParent) {
|
||||
// If we're cloning we need to insert the cloned children into the cloned
|
||||
// parent.
|
||||
|
@ -266,13 +266,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Element may be unresolved at this point.
|
||||
doc->RegisterUnresolvedElement(*aResult);
|
||||
|
||||
// Try to enqueue a created callback. The custom element data will be set
|
||||
// and created callback will be enqueued if the custom element type
|
||||
// has already been registered.
|
||||
doc->EnqueueLifecycleCallback(nsIDocument::eCreated, *aResult);
|
||||
doc->SetupCustomElement(*aResult, kNameSpaceID_XHTML);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ support-files =
|
||||
[test_bug900724.html]
|
||||
[test_bug1017896.html]
|
||||
[test_content_element.html]
|
||||
[test_custom_element_adopt_callbacks.html]
|
||||
[test_custom_element_clone_callbacks.html]
|
||||
[test_custom_element_clone_callbacks_extended.html]
|
||||
[test_nested_content_element.html]
|
||||
[test_dest_insertion_points.html]
|
||||
[test_dest_insertion_points_shadow.html]
|
||||
|
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
|
||||
-->
|
||||
<head>
|
||||
<title>Test callbacks for adopted custom elements.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<template id="template"><x-foo></x-foo></template>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
|
||||
<script>
|
||||
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
p.createdCallback = function() {
|
||||
ok(false, "Created callback should not be called for adopted node.");
|
||||
};
|
||||
|
||||
document.registerElement("x-foo", { prototype: p });
|
||||
|
||||
var template = document.getElementById("template");
|
||||
var adoptedFoo = document.adoptNode(template.content.firstChild);
|
||||
is(adoptedFoo.nodeName, "X-FOO");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
|
||||
-->
|
||||
<head>
|
||||
<title>Test callbacks for cloned custom elements.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test to make sure created callback is called on clones that are upgraded and clones
|
||||
// created after registering the custom element.
|
||||
|
||||
var callbackCalledOnUpgrade = false;
|
||||
var callbackCalledOnClone = false;
|
||||
|
||||
var foo = document.createElement("x-foo");
|
||||
var fooClone = foo.cloneNode(true);
|
||||
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
p.createdCallback = function() {
|
||||
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
|
||||
|
||||
if (this == fooClone) {
|
||||
// Callback called for the element created before registering the custom element.
|
||||
// Should be called on element upgrade.
|
||||
is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
|
||||
callbackCalledOnUpgrade = true;
|
||||
} else if (this != foo) {
|
||||
// Callback called for the element created after registering the custom element.
|
||||
is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
|
||||
callbackCalledOnClone = true;
|
||||
}
|
||||
|
||||
if (callbackCalledOnUpgrade && callbackCalledOnClone) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
document.registerElement("x-foo", { prototype: p });
|
||||
|
||||
var anotherFooClone = foo.cloneNode(true);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1081039
|
||||
-->
|
||||
<head>
|
||||
<title>Test callbacks for cloned extended custom elements.</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1081039">Bug 1081039</a>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Test to make sure created callback is called on clones that are upgraded and clones
|
||||
// created after registering the custom element.
|
||||
|
||||
var callbackCalledOnUpgrade = false;
|
||||
var callbackCalledOnClone = false;
|
||||
|
||||
var foo = document.createElement("button", "x-foo");
|
||||
is(foo.getAttribute("is"), "x-foo");
|
||||
|
||||
var fooClone = foo.cloneNode(true);
|
||||
|
||||
var p = Object.create(HTMLButtonElement.prototype);
|
||||
p.createdCallback = function() {
|
||||
is(this.__proto__, p, "Correct prototype should be set on custom elements.");
|
||||
|
||||
if (this == fooClone) {
|
||||
// Callback called for the element created before registering the custom element.
|
||||
// Should be called on element upgrade.
|
||||
is(callbackCalledOnUpgrade, false, "Upgrade should only be called once per clone.");
|
||||
callbackCalledOnUpgrade = true;
|
||||
} else if (this != foo) {
|
||||
// Callback called for the element created after registering the custom element.
|
||||
is(callbackCalledOnClone, false, "Upgrade should only be called once per clone.");
|
||||
callbackCalledOnClone = true;
|
||||
}
|
||||
|
||||
if (callbackCalledOnUpgrade && callbackCalledOnClone) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
document.registerElement("x-foo", { prototype: p, extends: "button" });
|
||||
|
||||
var anotherFooClone = foo.cloneNode(true);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -439,14 +439,11 @@ nsHtml5TreeOperation::CreateElement(int32_t aNs,
|
||||
value,
|
||||
false);
|
||||
|
||||
// Custom element prototype swizzling may be needed if there is an
|
||||
// "is" attribute.
|
||||
// Custom element setup may be needed if there is an "is" attribute.
|
||||
if (kNameSpaceID_None == nsuri && !prefix && nsGkAtoms::is == localName) {
|
||||
ErrorResult errorResult;
|
||||
newContent->OwnerDoc()->SwizzleCustomElement(newContent,
|
||||
value,
|
||||
newContent->GetNameSpaceID(),
|
||||
errorResult);
|
||||
newContent->OwnerDoc()->SetupCustomElement(newContent,
|
||||
newContent->GetNameSpaceID(),
|
||||
&value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,6 @@
|
||||
[Test Document.createElement() sets the element\'s IS attribute value to type, if type is not the same as localName]
|
||||
expected: FAIL
|
||||
|
||||
[Test Document.createElement() sets the element\'s IS attribute value to type, if type is not the same as localName and an element definition with matching localName, namespace, and type is not registered]
|
||||
expected: FAIL
|
||||
|
||||
[Test Document.createElementNS() sets the element\'s IS attribute value to type, if type is not the same as localName]
|
||||
expected: FAIL
|
||||
|
||||
[Test Document.createElementNS() sets the element\'s IS attribute value to type, if type is not the same as localNameand and an element definition with matching localName, namespace, and type is not registered ]
|
||||
expected: FAIL
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user