summaryrefslogtreecommitdiffstats
path: root/dom/base/CustomElementRegistry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/CustomElementRegistry.cpp')
-rw-r--r--dom/base/CustomElementRegistry.cpp119
1 files changed, 108 insertions, 11 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index cc6264b03..34ad9549d 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -434,8 +434,27 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
LifecycleCallbackArgs* aArgs,
CustomElementDefinition* aDefinition)
{
+ RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
+ MOZ_ASSERT(elementData, "CustomElementData should exist");
+
+ // Let DEFINITION be ELEMENT's definition
+ CustomElementDefinition* definition = aDefinition;
+ if (!definition) {
+ mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
+
+ // Make sure we get the correct definition in case the element
+ // is a extended custom element e.g. <button is="x-button">.
+ nsCOMPtr<nsIAtom> typeAtom = elementData ?
+ elementData->mType.get() : info->NameAtom();
+
+ definition = mCustomDefinitions.Get(typeAtom);
+ if (!definition || definition->mLocalName != info->NameAtom()) {
+ return;
+ }
+ }
+
auto callback =
- CreateCustomElementCallback(aType, aCustomElement, aArgs, aDefinition);
+ CreateCustomElementCallback(aType, aCustomElement, aArgs, definition);
if (!callback) {
return;
}
@@ -445,9 +464,17 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType
return;
}
+ if (aType == nsIDocument::eAttributeChanged) {
+ nsCOMPtr<nsIAtom> attrName = NS_Atomize(aArgs->name);
+ if (definition->mObservedAttributes.IsEmpty() ||
+ !definition->mObservedAttributes.Contains(attrName)) {
+ return;
+ }
+ }
+
CustomElementReactionsStack* reactionsStack =
docGroup->CustomElementReactionsStack();
- reactionsStack->EnqueueCallbackReaction(this, aCustomElement, aDefinition,
+ reactionsStack->EnqueueCallbackReaction(this, aCustomElement, definition,
Move(callback));
}
@@ -657,6 +684,7 @@ CustomElementRegistry::Define(const nsAString& aName,
JS::Rooted<JSObject*> constructorPrototype(cx);
nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
+ nsCOMArray<nsIAtom> observedAttributes;
{ // Set mIsCustomDefinitionRunning.
/**
* 9. Set this CustomElementRegistry's element definition is running flag.
@@ -718,6 +746,14 @@ CustomElementRegistry::Define(const nsAString& aName,
return;
}
+ // Note: We call the init from the constructorProtoUnwrapped's compartment
+ // here.
+ JS::RootedValue rootedv(cx, JS::ObjectValue(*constructorProtoUnwrapped));
+ if (!JS_WrapValue(cx, &rootedv) || !callbacksHolder->Init(cx, rootedv)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+
/**
* 10.5. Let observedAttributes be an empty sequence<DOMString>.
* 10.6. If the value of the entry in lifecycleCallbacks with key
@@ -730,14 +766,45 @@ CustomElementRegistry::Define(const nsAString& aName,
* any exceptions from the conversion.
*/
// TODO: Bug 1293921 - Implement connected/disconnected/adopted/attributeChanged lifecycle callbacks for custom elements
+ if (callbacksHolder->mAttributeChangedCallback.WasPassed()) {
+ // Enter constructor's compartment.
+ JSAutoCompartment ac(cx, constructor);
+ JS::Rooted<JS::Value> observedAttributesIterable(cx);
+
+ if (!JS_GetProperty(cx, constructor, "observedAttributes",
+ &observedAttributesIterable)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
- // Note: We call the init from the constructorProtoUnwrapped's compartment
- // here.
- JS::RootedValue rootedv(cx, JS::ObjectValue(*constructorProtoUnwrapped));
- if (!JS_WrapValue(cx, &rootedv) || !callbacksHolder->Init(cx, rootedv)) {
- aRv.StealExceptionFromJSContext(cx);
- return;
- }
+ if (!observedAttributesIterable.isUndefined()) {
+ JS::ForOfIterator iter(cx);
+ if (!iter.init(observedAttributesIterable)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+
+ JS::Rooted<JS::Value> attribute(cx);
+ while (true) {
+ bool done;
+ if (!iter.next(&attribute, &done)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+ if (done) {
+ break;
+ }
+
+ JSString *attrJSStr = attribute.toString();
+ nsAutoJSString attrStr;
+ if (!attrStr.init(cx, attrJSStr)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+ observedAttributes.AppendElement(NS_Atomize(attrStr));
+ }
+ }
+ } // Leave constructor's compartment.
} // Leave constructorProtoUnwrapped's compartment.
} // Unset mIsCustomDefinitionRunning
@@ -754,6 +821,7 @@ CustomElementRegistry::Define(const nsAString& aName,
new CustomElementDefinition(nameAtom,
localNameAtom,
&aFunctionConstructor,
+ Move(observedAttributes),
constructorPrototype,
callbacks,
0 /* TODO dependent on HTML imports. Bug 877072 */);
@@ -878,8 +946,35 @@ CustomElementRegistry::Upgrade(Element* aElement,
return;
}
- // Step 3 and Step 4.
- // TODO: Bug 1334051 - Implement list of observed attributes for custom elements' attributeChanged callbacks
+ // Step 3.
+ if (!aDefinition->mObservedAttributes.IsEmpty()) {
+ uint32_t count = aElement->GetAttrCount();
+ for (uint32_t i = 0; i < count; i++) {
+ mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(i);
+
+ const nsAttrName* name = info.mName;
+ nsIAtom* attrName = name->LocalName();
+
+ if (aDefinition->IsInObservedAttributeList(attrName)) {
+ int32_t namespaceID = name->NamespaceID();
+ nsAutoString attrValue, namespaceURI;
+ info.mValue->ToString(attrValue);
+ nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID,
+ namespaceURI);
+
+ LifecycleCallbackArgs args = {
+ nsDependentAtomString(attrName),
+ NullString(),
+ (attrValue.IsEmpty() ? NullString() : attrValue),
+ (namespaceURI.IsEmpty() ? NullString() : namespaceURI)
+ };
+ EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, aElement,
+ &args, aDefinition);
+ }
+ }
+ }
+
+ // Step 4.
// TODO: Bug 1334043 - Implement connected lifecycle callbacks for custom elements
// Step 5.
@@ -1051,12 +1146,14 @@ CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue,
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
Function* aConstructor,
+ nsCOMArray<nsIAtom>&& aObservedAttributes,
JSObject* aPrototype,
LifecycleCallbacks* aCallbacks,
uint32_t aDocOrder)
: mType(aType),
mLocalName(aLocalName),
mConstructor(new CustomElementConstructor(aConstructor)),
+ mObservedAttributes(Move(aObservedAttributes)),
mPrototype(aPrototype),
mCallbacks(aCallbacks),
mDocOrder(aDocOrder)