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.cpp996
1 files changed, 667 insertions, 329 deletions
diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
index 3f202d33b..99452df65 100644
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -6,9 +6,11 @@
#include "mozilla/dom/CustomElementRegistry.h"
+#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/CustomElementRegistryBinding.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/WebComponentsBinding.h"
+#include "mozilla/dom/DocGroup.h"
#include "nsIParserService.h"
#include "jsapi.h"
@@ -18,40 +20,21 @@ namespace dom {
void
CustomElementCallback::Call()
{
- ErrorResult rv;
+ IgnoredErrorResult rv;
switch (mType) {
- case nsIDocument::eCreated:
- {
- // For the duration of this callback invocation, the element is being created
- // flag must be set to true.
- mOwnerData->mElementIsBeingCreated = true;
-
- // The callback hasn't actually been invoked yet, but we need to flip
- // this now in order to enqueue the attached callback. This is a spec
- // bug (w3c bug 27437).
- mOwnerData->mCreatedCallbackInvoked = true;
-
- // If ELEMENT is in a document and this document has a browsing context,
- // enqueue attached callback for ELEMENT.
- nsIDocument* document = mThisObject->GetComposedDoc();
- if (document && document->GetDocShell()) {
- nsContentUtils::EnqueueLifecycleCallback(
- document, nsIDocument::eAttached, mThisObject);
- }
-
- static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
- mOwnerData->mElementIsBeingCreated = false;
+ case nsIDocument::eConnected:
+ static_cast<LifecycleConnectedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
- }
- case nsIDocument::eAttached:
- static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
+ case nsIDocument::eDisconnected:
+ static_cast<LifecycleDisconnectedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
- case nsIDocument::eDetached:
- static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
+ case nsIDocument::eAdopted:
+ static_cast<LifecycleAdoptedCallback *>(mCallback.get())->Call(mThisObject,
+ mAdoptedCallbackArgs.mOldDocument, mAdoptedCallbackArgs.mNewDocument, rv);
break;
case nsIDocument::eAttributeChanged:
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
- mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
+ mArgs.name, mArgs.oldValue, mArgs.newValue, mArgs.namespaceURI, rv);
break;
}
}
@@ -68,87 +51,164 @@ CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
CustomElementCallback::CustomElementCallback(Element* aThisObject,
nsIDocument::ElementCallbackType aCallbackType,
- mozilla::dom::CallbackFunction* aCallback,
- CustomElementData* aOwnerData)
+ mozilla::dom::CallbackFunction* aCallback)
: mThisObject(aThisObject),
mCallback(aCallback),
- mType(aCallbackType),
- mOwnerData(aOwnerData)
+ mType(aCallbackType)
+{
+}
+
+//-----------------------------------------------------
+// CustomElementConstructor
+
+already_AddRefed<Element>
+CustomElementConstructor::Construct(const char* aExecutionReason,
+ ErrorResult& aRv)
{
+ CallSetup s(this, aRv, aExecutionReason,
+ CallbackFunction::eRethrowExceptions);
+
+ JSContext* cx = s.GetContext();
+ if (!cx) {
+ MOZ_ASSERT(aRv.Failed());
+ return nullptr;
+ }
+
+ JS::Rooted<JSObject*> result(cx);
+ JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
+ if (!JS::Construct(cx, constructor, JS::HandleValueArray::empty(), &result)) {
+ aRv.NoteJSContextException(cx);
+ return nullptr;
+ }
+
+ RefPtr<Element> element;
+ if (NS_FAILED(UNWRAP_OBJECT(Element, &result, element))) {
+ return nullptr;
+ }
+
+ return element.forget();
}
+//-----------------------------------------------------
+// CustomElementData
+
CustomElementData::CustomElementData(nsIAtom* aType)
- : mType(aType),
- mCurrentCallback(-1),
- mElementIsBeingCreated(false),
- mCreatedCallbackInvoked(true),
- mAssociatedMicroTask(-1)
+ : CustomElementData(aType, CustomElementData::State::eUndefined)
+{
+}
+
+CustomElementData::CustomElementData(nsIAtom* aType, State aState)
+ : mState(aState)
+ , mType(aType)
{
}
void
-CustomElementData::RunCallbackQueue()
+CustomElementData::SetCustomElementDefinition(CustomElementDefinition* aDefinition)
{
- // Note: It's possible to re-enter this method.
- while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
- mCallbackQueue[mCurrentCallback]->Call();
+ MOZ_ASSERT(mState == State::eCustom);
+ MOZ_ASSERT(!mCustomElementDefinition);
+ MOZ_ASSERT(aDefinition->mType == mType);
+
+ mCustomElementDefinition = aDefinition;
+}
+
+CustomElementDefinition*
+CustomElementData::GetCustomElementDefinition()
+{
+ MOZ_ASSERT(mCustomElementDefinition ? mState == State::eCustom
+ : mState != State::eCustom);
+
+ return mCustomElementDefinition;
+}
+
+nsIAtom*
+CustomElementData::GetCustomElementType()
+{
+ return mType;
+}
+
+void
+CustomElementData::Traverse(nsCycleCollectionTraversalCallback& aCb) const
+{
+ for (uint32_t i = 0; i < mReactionQueue.Length(); i++) {
+ if (mReactionQueue[i]) {
+ mReactionQueue[i]->Traverse(aCb);
+ }
}
- mCallbackQueue.Clear();
- mCurrentCallback = -1;
+ if (mCustomElementDefinition) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCustomElementDefinition");
+ aCb.NoteNativeChild(mCustomElementDefinition,
+ NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
+ }
}
+void
+CustomElementData::Unlink()
+{
+ mReactionQueue.Clear();
+ mCustomElementDefinition = nullptr;
+}
+
+//-----------------------------------------------------
+// CustomElementRegistry
+
+namespace {
+
+class MOZ_RAII AutoConstructionStackEntry final
+{
+public:
+ AutoConstructionStackEntry(nsTArray<RefPtr<nsGenericHTMLElement>>& aStack,
+ nsGenericHTMLElement* aElement)
+ : mStack(aStack)
+ {
+ mIndex = mStack.Length();
+ mStack.AppendElement(aElement);
+ }
+
+ ~AutoConstructionStackEntry()
+ {
+ MOZ_ASSERT(mIndex == mStack.Length() - 1,
+ "Removed element should be the last element");
+ mStack.RemoveElementAt(mIndex);
+ }
+
+private:
+ nsTArray<RefPtr<nsGenericHTMLElement>>& mStack;
+ uint32_t mIndex;
+};
+
+} // namespace anonymous
+
// Only needed for refcounted objects.
NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
- tmp->mCustomDefinitions.Clear();
+ tmp->mConstructors.clear();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
- for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
- nsAutoPtr<LifecycleCallbacks>& callbacks = iter.UserData()->mCallbacks;
-
- if (callbacks->mAttributeChangedCallback.WasPassed()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
- "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
- cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
- }
-
- if (callbacks->mCreatedCallback.WasPassed()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
- "mCustomDefinitions->mCallbacks->mCreatedCallback");
- cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
- }
-
- if (callbacks->mAttachedCallback.WasPassed()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
- "mCustomDefinitions->mCallbacks->mAttachedCallback");
- cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
- }
-
- if (callbacks->mDetachedCallback.WasPassed()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
- "mCustomDefinitions->mCallbacks->mDetachedCallback");
- cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
- }
- }
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry)
for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
- aCallbacks.Trace(&iter.UserData()->mConstructor,
- "mCustomDefinitions constructor",
- aClosure);
aCallbacks.Trace(&iter.UserData()->mPrototype,
"mCustomDefinitions prototype",
aClosure);
}
+ for (ConstructorMap::Enum iter(tmp->mConstructors); !iter.empty(); iter.popFront()) {
+ aCallbacks.Trace(&iter.front().mutableKey(),
+ "mConstructors key",
+ aClosure);
+ }
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -163,97 +223,52 @@ NS_INTERFACE_MAP_END
/* static */ bool
CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject)
{
- return Preferences::GetBool("dom.webcomponents.customelements.enabled") ||
- Preferences::GetBool("dom.webcomponents.enabled");
+ return nsContentUtils::IsCustomElementsEnabled();
}
-/* static */ already_AddRefed<CustomElementRegistry>
-CustomElementRegistry::Create(nsPIDOMWindowInner* aWindow)
+CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
+ : mWindow(aWindow)
+ , mIsCustomDefinitionRunning(false)
{
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsInnerWindow());
+ MOZ_ALWAYS_TRUE(mConstructors.init());
- if (!aWindow->GetDocShell()) {
- return nullptr;
- }
-
- if (!IsCustomElementEnabled()) {
- return nullptr;
- }
-
- RefPtr<CustomElementRegistry> customElementRegistry =
- new CustomElementRegistry(aWindow);
- return customElementRegistry.forget();
-}
-
-/* static */ void
-CustomElementRegistry::ProcessTopElementQueue()
-{
- MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
-
- nsTArray<RefPtr<CustomElementData>>& stack = *sProcessingStack;
- uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
-
- for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
- // Callback queue may have already been processed in an earlier
- // element queue or in an element queue that was popped
- // off more recently.
- if (stack[i]->mAssociatedMicroTask != -1) {
- stack[i]->RunCallbackQueue();
- stack[i]->mAssociatedMicroTask = -1;
- }
- }
-
- // If this was actually the base element queue, don't bother trying to pop
- // the first "queue" marker (sentinel).
- if (firstQueue != 0) {
- stack.SetLength(firstQueue);
- } else {
- // Don't pop sentinel for base element queue.
- stack.SetLength(1);
- }
+ mozilla::HoldJSObjects(this);
}
-/* static */ void
-CustomElementRegistry::XPCOMShutdown()
+CustomElementRegistry::~CustomElementRegistry()
{
- sProcessingStack.reset();
+ mozilla::DropJSObjects(this);
}
-/* static */ Maybe<nsTArray<RefPtr<CustomElementData>>>
-CustomElementRegistry::sProcessingStack;
-
-CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
- : mWindow(aWindow)
- , mIsCustomDefinitionRunning(false)
+CustomElementDefinition*
+CustomElementRegistry::LookupCustomElementDefinition(nsIAtom* aNameAtom,
+ nsIAtom* aTypeAtom) const
{
- mozilla::HoldJSObjects(this);
-
- if (!sProcessingStack) {
- sProcessingStack.emplace();
- // Add the base queue sentinel to the processing stack.
- sProcessingStack->AppendElement((CustomElementData*) nullptr);
+ CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom);
+ if (data && data->mLocalName == aNameAtom) {
+ return data;
}
-}
-CustomElementRegistry::~CustomElementRegistry()
-{
- mozilla::DropJSObjects(this);
+ return nullptr;
}
CustomElementDefinition*
-CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
- const nsAString* aIs) const
+CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx,
+ JSObject* aConstructor) const
{
- nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
- nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
+ JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrap(aConstructor));
- CustomElementDefinition* data = mCustomDefinitions.Get(typeAtom);
- if (data && data->mLocalName == localNameAtom) {
- return data;
+ const auto& ptr = mConstructors.lookup(constructor);
+ if (!ptr) {
+ return nullptr;
}
- return nullptr;
+ CustomElementDefinition* definition = mCustomDefinitions.GetWeak(ptr->value());
+ MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
+
+ return definition;
}
void
@@ -269,7 +284,7 @@ CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTy
typeName = info->NameAtom();
}
- if (mCustomDefinitions.Get(typeName)) {
+ if (mCustomDefinitions.GetWeak(typeName)) {
return;
}
@@ -282,171 +297,129 @@ CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTy
}
void
-CustomElementRegistry::SetupCustomElement(Element* aElement,
- const nsAString* aTypeExtension)
+CustomElementRegistry::UnregisterUnresolvedElement(Element* aElement,
+ nsIAtom* aTypeName)
{
- nsCOMPtr<nsIAtom> tagAtom = aElement->NodeInfo()->NameAtom();
- nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
- NS_Atomize(*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 = LookupCustomElementDefinition(
- aElement->NodeInfo()->LocalName(), aTypeExtension);
-
- if (!data) {
- // The type extension doesn't exist in the registry,
- // thus we don't need to enqueue callback or adjust
- // the "is" attribute, but it is possibly an upgrade candidate.
- RegisterUnresolvedElement(aElement, typeAtom);
- return;
- }
-
- if (data->mLocalName != tagAtom) {
- // The element doesn't match the local name for the
- // definition, thus the element isn't a custom element
- // and we don't need to do anything more.
- return;
+ nsTArray<nsWeakPtr>* candidates;
+ if (mCandidatesMap.Get(aTypeName, &candidates)) {
+ MOZ_ASSERT(candidates);
+ // We don't need to iterate the candidates array and remove the element from
+ // the array for performance reason. It'll be handled by bug 1396620.
+ for (size_t i = 0; i < candidates->Length(); ++i) {
+ nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
+ if (elem && elem.get() == aElement) {
+ candidates->RemoveElementAt(i);
+ }
+ }
}
-
- // 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);
}
-void
-CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
- Element* aCustomElement,
- LifecycleCallbackArgs* aArgs,
- CustomElementDefinition* aDefinition)
+/* static */ UniquePtr<CustomElementCallback>
+CustomElementRegistry::CreateCustomElementCallback(
+ nsIDocument::ElementCallbackType aType, Element* aCustomElement,
+ LifecycleCallbackArgs* aArgs,
+ LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
+ CustomElementDefinition* aDefinition)
{
- CustomElementData* elementData = aCustomElement->GetCustomElementData();
-
- // 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();
+ MOZ_ASSERT(aDefinition, "CustomElementDefinition should not be null");
- definition = mCustomDefinitions.Get(typeAtom);
- if (!definition || definition->mLocalName != info->NameAtom()) {
- // Trying to enqueue a callback for an element that is not
- // a custom element. We are done, nothing to do.
- return;
- }
- }
-
- if (!elementData) {
- // Create the custom element data the first time
- // that we try to enqueue a callback.
- elementData = new CustomElementData(definition->mType);
- // aCustomElement takes ownership of elementData
- aCustomElement->SetCustomElementData(elementData);
- MOZ_ASSERT(aType == nsIDocument::eCreated,
- "First callback should be the created callback");
- }
+ MOZ_ASSERT(aCustomElement->GetCustomElementData(),
+ "CustomElementData should exist");
// Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
CallbackFunction* func = nullptr;
switch (aType) {
- case nsIDocument::eCreated:
- if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
- func = definition->mCallbacks->mCreatedCallback.Value();
+ case nsIDocument::eConnected:
+ if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) {
+ func = aDefinition->mCallbacks->mConnectedCallback.Value();
}
break;
- case nsIDocument::eAttached:
- if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
- func = definition->mCallbacks->mAttachedCallback.Value();
+ case nsIDocument::eDisconnected:
+ if (aDefinition->mCallbacks->mDisconnectedCallback.WasPassed()) {
+ func = aDefinition->mCallbacks->mDisconnectedCallback.Value();
}
break;
- case nsIDocument::eDetached:
- if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
- func = definition->mCallbacks->mDetachedCallback.Value();
+ case nsIDocument::eAdopted:
+ if (aDefinition->mCallbacks->mAdoptedCallback.WasPassed()) {
+ func = aDefinition->mCallbacks->mAdoptedCallback.Value();
}
break;
case nsIDocument::eAttributeChanged:
- if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
- func = definition->mCallbacks->mAttributeChangedCallback.Value();
+ if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
+ func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
}
break;
}
// If there is no such callback, stop.
if (!func) {
- return;
- }
-
- if (aType == nsIDocument::eCreated) {
- elementData->mCreatedCallbackInvoked = false;
- } else if (!elementData->mCreatedCallbackInvoked) {
- // Callbacks other than created callback must not be enqueued
- // until after the created callback has been invoked.
- return;
+ return nullptr;
}
// Add CALLBACK to ELEMENT's callback queue.
- CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
- aType,
- func,
- elementData);
- // Ownership of callback is taken by mCallbackQueue.
- elementData->mCallbackQueue.AppendElement(callback);
+ auto callback =
+ MakeUnique<CustomElementCallback>(aCustomElement, aType, func);
+
if (aArgs) {
callback->SetArgs(*aArgs);
}
- if (!elementData->mElementIsBeingCreated) {
- CustomElementData* lastData =
- sProcessingStack->SafeLastElement(nullptr);
-
- // A new element queue needs to be pushed if the queue at the
- // top of the stack is associated with another microtask level.
- bool shouldPushElementQueue =
- (!lastData || lastData->mAssociatedMicroTask <
- static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
+ if (aAdoptedCallbackArgs) {
+ callback->SetAdoptedCallbackArgs(*aAdoptedCallbackArgs);
+ }
+ return Move(callback);
+}
- // Push a new element queue onto the processing stack when appropriate
- // (when we enter a new microtask).
- if (shouldPushElementQueue) {
- // Push a sentinel value on the processing stack to mark the
- // boundary between the element queues.
- sProcessingStack->AppendElement((CustomElementData*) nullptr);
+/* static */ void
+CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
+ Element* aCustomElement,
+ LifecycleCallbackArgs* aArgs,
+ LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
+ CustomElementDefinition* aDefinition)
+{
+ CustomElementDefinition* definition = aDefinition;
+ if (!definition) {
+ definition = aCustomElement->GetCustomElementDefinition();
+ if (!definition ||
+ definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) {
+ return;
}
+ }
- sProcessingStack->AppendElement(elementData);
- elementData->mAssociatedMicroTask =
- static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
-
- // Add a script runner to pop and process the element queue at
- // the top of the processing stack.
- if (shouldPushElementQueue) {
- // Lifecycle callbacks enqueued by user agent implementation
- // should be invoked prior to returning control back to script.
- // Create a script runner to process the top of the processing
- // stack as soon as it is safe to run script.
- nsCOMPtr<nsIRunnable> runnable =
- NS_NewRunnableFunction(&CustomElementRegistry::ProcessTopElementQueue);
- nsContentUtils::AddScriptRunner(runnable);
+ auto callback =
+ CreateCustomElementCallback(aType, aCustomElement, aArgs,
+ aAdoptedCallbackArgs, definition);
+ if (!callback) {
+ return;
+ }
+
+ DocGroup* docGroup = aCustomElement->OwnerDoc()->GetDocGroup();
+ if (!docGroup) {
+ 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(aCustomElement, Move(callback));
}
void
CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom,
JS::MutableHandle<JSObject*> aPrototype)
{
- mozilla::dom::CustomElementDefinition* definition = mCustomDefinitions.Get(aAtom);
+ mozilla::dom::CustomElementDefinition* definition =
+ mCustomDefinitions.GetWeak(aAtom);
if (definition) {
aPrototype.set(definition->mPrototype);
} else {
@@ -455,48 +428,29 @@ CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom,
}
void
-CustomElementRegistry::UpgradeCandidates(JSContext* aCx,
- nsIAtom* aKey,
- CustomElementDefinition* aDefinition)
+CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey,
+ CustomElementDefinition* aDefinition,
+ ErrorResult& aRv)
{
+ DocGroup* docGroup = mWindow->GetDocGroup();
+ if (!docGroup) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // TODO: Bug 1326028 - Upgrade custom element in shadow-including tree order
nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
mCandidatesMap.RemoveAndForget(aKey, candidates);
if (candidates) {
+ CustomElementReactionsStack* reactionsStack =
+ docGroup->CustomElementReactionsStack();
for (size_t i = 0; i < candidates->Length(); ++i) {
nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
if (!elem) {
continue;
}
- elem->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
-
- // Make sure that the element name matches the name in the definition.
- // (e.g. a definition for x-button extending button should match
- // <button is="x-button"> but not <x-button>.
- if (elem->NodeInfo()->NameAtom() != aDefinition->mLocalName) {
- //Skip over this element because definition does not apply.
- continue;
- }
-
- MOZ_ASSERT(elem->IsHTMLElement(aDefinition->mLocalName));
- nsWrapperCache* cache;
- CallQueryInterface(elem, &cache);
- MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
-
- // We want to set the custom prototype in the caller's comparment.
- // In the case that element is in a different compartment,
- // this will set the prototype on the element's wrapper and
- // thus only visible in the wrapper's compartment.
- JS::RootedObject wrapper(aCx);
- JS::Rooted<JSObject*> prototype(aCx, aDefinition->mPrototype);
- if ((wrapper = cache->GetWrapper()) && JS_WrapObject(aCx, &wrapper)) {
- if (!JS_SetPrototype(aCx, wrapper, prototype)) {
- continue;
- }
- }
-
- nsContentUtils::EnqueueLifecycleCallback(
- elem->OwnerDoc(), nsIDocument::eCreated, elem, nullptr, aDefinition);
+ reactionsStack->EnqueueUpgradeReaction(elem, aDefinition);
}
}
}
@@ -516,11 +470,7 @@ static const char* kLifeCycleCallbackNames[] = {
"connectedCallback",
"disconnectedCallback",
"adoptedCallback",
- "attributeChangedCallback",
- // The life cycle callbacks from v0 spec.
- "createdCallback",
- "attachedCallback",
- "detachedCallback"
+ "attributeChangedCallback"
};
static void
@@ -600,7 +550,7 @@ CustomElementRegistry::Define(const nsAString& aName,
* 3. If this CustomElementRegistry contains an entry with name name, then
* throw a "NotSupportedError" DOMException and abort these steps.
*/
- if (mCustomDefinitions.Get(nameAtom)) {
+ if (mCustomDefinitions.GetWeak(nameAtom)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
@@ -609,9 +559,13 @@ CustomElementRegistry::Define(const nsAString& aName,
* 4. If this CustomElementRegistry contains an entry with constructor constructor,
* then throw a "NotSupportedError" DOMException and abort these steps.
*/
- // TODO: Step 3 of HTMLConstructor also needs a way to look up definition by
- // using constructor. So I plans to figure out a solution to support both of
- // them in bug 1274159.
+ const auto& ptr = mConstructors.lookup(constructorUnwrapped);
+ if (ptr) {
+ MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
+ "Definition must be found in mCustomDefinitions");
+ aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return;
+ }
/**
* 5. Let localName be name.
@@ -663,6 +617,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.
@@ -724,6 +679,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
@@ -735,15 +698,54 @@ CustomElementRegistry::Define(const nsAString& aName,
* observedAttributesIterable to a sequence<DOMString>. Rethrow
* 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.Throw(NS_ERROR_FAILURE);
- return;
- }
+ if (!observedAttributesIterable.isUndefined()) {
+ if (!observedAttributesIterable.isObject()) {
+ aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("observedAttributes"));
+ return;
+ }
+
+ JS::ForOfIterator iter(cx);
+ if (!iter.init(observedAttributesIterable, JS::ForOfIterator::AllowNonIterable)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+
+ if (!iter.valueIsIterable()) {
+ aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("observedAttributes"));
+ return;
+ }
+
+ JS::Rooted<JS::Value> attribute(cx);
+ while (true) {
+ bool done;
+ if (!iter.next(&attribute, &done)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+ if (done) {
+ break;
+ }
+
+ nsAutoString attrStr;
+ if (!ConvertJSValueToString(cx, attribute, eStringify, eStringify, attrStr)) {
+ aRv.StealExceptionFromJSContext(cx);
+ return;
+ }
+ observedAttributes.AppendElement(NS_Atomize(attrStr));
+ }
+ }
+ } // Leave constructor's compartment.
} // Leave constructorProtoUnwrapped's compartment.
} // Unset mIsCustomDefinitionRunning
@@ -756,24 +758,34 @@ CustomElementRegistry::Define(const nsAString& aName,
// Associate the definition with the custom element.
nsCOMPtr<nsIAtom> localNameAtom(NS_Atomize(localName));
LifecycleCallbacks* callbacks = callbacksHolder.forget();
- CustomElementDefinition* definition =
+
+ /**
+ * 12. Add definition to this CustomElementRegistry.
+ */
+ if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ RefPtr<CustomElementDefinition> definition =
new CustomElementDefinition(nameAtom,
localNameAtom,
- constructor,
+ &aFunctionConstructor,
+ Move(observedAttributes),
constructorPrototype,
callbacks,
0 /* TODO dependent on HTML imports. Bug 877072 */);
- /**
- * 12. Add definition to this CustomElementRegistry.
- */
- mCustomDefinitions.Put(nameAtom, definition);
+ CustomElementDefinition* def = definition.get();
+ mCustomDefinitions.Put(nameAtom, definition.forget());
+
+ MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
+ "Number of entries should be the same");
/**
* 13. 14. 15. Upgrade candidates
*/
- // TODO: Bug 1299363 - Implement custom element v1 upgrade algorithm
- UpgradeCandidates(cx, nameAtom, definition);
+ UpgradeCandidates(nameAtom, def, aRv);
/**
* 16. If this CustomElementRegistry's when-defined promise map contains an
@@ -796,14 +808,14 @@ CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
JS::MutableHandle<JS::Value> aRetVal)
{
nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
- CustomElementDefinition* data = mCustomDefinitions.Get(nameAtom);
+ CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);
if (!data) {
aRetVal.setUndefined();
return;
}
- aRetVal.setObject(*data->mConstructor);
+ aRetVal.setObjectOrNull(data->mConstructor->Callable());
return;
}
@@ -823,7 +835,7 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
return promise.forget();
}
- if (mCustomDefinitions.Get(nameAtom)) {
+ if (mCustomDefinitions.GetWeak(nameAtom)) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
@@ -837,20 +849,346 @@ CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv)
return promise.forget();
}
+namespace {
+
+static void
+DoUpgrade(Element* aElement,
+ CustomElementConstructor* aConstructor,
+ ErrorResult& aRv)
+{
+ // Rethrow the exception since it might actually throw the exception from the
+ // upgrade steps back out to the caller of document.createElement.
+ RefPtr<Element> constructResult =
+ aConstructor->Construct("Custom Element Upgrade", aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ if (!constructResult || constructResult.get() != aElement) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+}
+
+} // anonymous namespace
+
+// https://html.spec.whatwg.org/multipage/scripting.html#upgrades
+/* static */ void
+CustomElementRegistry::Upgrade(Element* aElement,
+ CustomElementDefinition* aDefinition,
+ ErrorResult& aRv)
+{
+ aElement->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
+
+ RefPtr<CustomElementData> data = aElement->GetCustomElementData();
+ MOZ_ASSERT(data, "CustomElementData should exist");
+
+ // Step 1 and step 2.
+ if (data->mState == CustomElementData::State::eCustom ||
+ data->mState == CustomElementData::State::eFailed) {
+ return;
+ }
+
+ // 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,
+ (namespaceURI.IsEmpty() ? NullString() : namespaceURI)
+ };
+ nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged,
+ aElement,
+ &args, nullptr, aDefinition);
+ }
+ }
+ }
+
+ // Step 4.
+ if (aElement->IsInComposedDoc()) {
+ nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, aElement,
+ nullptr, nullptr, aDefinition);
+ }
+
+ // Step 5.
+ AutoConstructionStackEntry acs(aDefinition->mConstructionStack,
+ nsGenericHTMLElement::FromContent(aElement));
+
+ // Step 6 and step 7.
+ DoUpgrade(aElement, aDefinition->mConstructor, aRv);
+ if (aRv.Failed()) {
+ data->mState = CustomElementData::State::eFailed;
+ // Empty element's custom element reaction queue.
+ data->mReactionQueue.Clear();
+ return;
+ }
+
+ // Step 8.
+ data->mState = CustomElementData::State::eCustom;
+
+ // Step 9.
+ aElement->SetCustomElementDefinition(aDefinition);
+}
+
+//-----------------------------------------------------
+// CustomElementReactionsStack
+
+void
+CustomElementReactionsStack::CreateAndPushElementQueue()
+{
+ MOZ_ASSERT(mRecursionDepth);
+ MOZ_ASSERT(!mIsElementQueuePushedForCurrentRecursionDepth);
+
+ // Push a new element queue onto the custom element reactions stack.
+ mReactionsStack.AppendElement(MakeUnique<ElementQueue>());
+ mIsElementQueuePushedForCurrentRecursionDepth = true;
+}
+
+void
+CustomElementReactionsStack::PopAndInvokeElementQueue()
+{
+ MOZ_ASSERT(mRecursionDepth);
+ MOZ_ASSERT(mIsElementQueuePushedForCurrentRecursionDepth);
+ MOZ_ASSERT(!mReactionsStack.IsEmpty(),
+ "Reaction stack shouldn't be empty");
+
+ // Pop the element queue from the custom element reactions stack,
+ // and invoke custom element reactions in that queue.
+ const uint32_t lastIndex = mReactionsStack.Length() - 1;
+ ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get();
+ // Check element queue size in order to reduce function call overhead.
+ if (!elementQueue->IsEmpty()) {
+ // It is still not clear what error reporting will look like in custom
+ // element, see https://github.com/w3c/webcomponents/issues/635.
+ // We usually report the error to entry global in gecko, so just follow the
+ // same behavior here.
+ // This may be null if it's called from parser, see the case of
+ // attributeChangedCallback in
+ // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
+ // In that case, the exception of callback reactions will be automatically
+ // reported in CallSetup.
+ nsIGlobalObject* global = GetEntryGlobal();
+ InvokeReactions(elementQueue, global);
+ }
+
+ // InvokeReactions() might create other custom element reactions, but those
+ // new reactions should be already consumed and removed at this point.
+ MOZ_ASSERT(lastIndex == mReactionsStack.Length() - 1,
+ "reactions created by InvokeReactions() should be consumed and removed");
+
+ mReactionsStack.RemoveElementAt(lastIndex);
+ mIsElementQueuePushedForCurrentRecursionDepth = false;
+}
+
+void
+CustomElementReactionsStack::EnqueueUpgradeReaction(Element* aElement,
+ CustomElementDefinition* aDefinition)
+{
+ Enqueue(aElement, new CustomElementUpgradeReaction(aDefinition));
+}
+
+void
+CustomElementReactionsStack::EnqueueCallbackReaction(Element* aElement,
+ UniquePtr<CustomElementCallback> aCustomElementCallback)
+{
+ Enqueue(aElement, new CustomElementCallbackReaction(Move(aCustomElementCallback)));
+}
+
+void
+CustomElementReactionsStack::Enqueue(Element* aElement,
+ CustomElementReaction* aReaction)
+{
+ RefPtr<CustomElementData> elementData = aElement->GetCustomElementData();
+ MOZ_ASSERT(elementData, "CustomElementData should exist");
+
+ if (mRecursionDepth) {
+ // If the element queue is not created for current recursion depth, create
+ // and push an element queue to reactions stack first.
+ if (!mIsElementQueuePushedForCurrentRecursionDepth) {
+ CreateAndPushElementQueue();
+ }
+
+ MOZ_ASSERT(!mReactionsStack.IsEmpty());
+ // Add element to the current element queue.
+ mReactionsStack.LastElement()->AppendElement(aElement);
+ elementData->mReactionQueue.AppendElement(aReaction);
+ return;
+ }
+
+ // If the custom element reactions stack is empty, then:
+ // Add element to the backup element queue.
+ MOZ_ASSERT(mReactionsStack.IsEmpty(),
+ "custom element reactions stack should be empty");
+ MOZ_ASSERT(!aReaction->IsUpgradeReaction(),
+ "Upgrade reaction should not be scheduled to backup queue");
+ mBackupQueue.AppendElement(aElement);
+ elementData->mReactionQueue.AppendElement(aReaction);
+
+ if (mIsBackupQueueProcessing) {
+ return;
+ }
+
+ CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+ RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
+ context->DispatchMicroTaskRunnable(bqmt.forget());
+}
+
+void
+CustomElementReactionsStack::InvokeBackupQueue()
+{
+ // Check backup queue size in order to reduce function call overhead.
+ if (!mBackupQueue.IsEmpty()) {
+ // Upgrade reactions won't be scheduled in backup queue and the exception of
+ // callback reactions will be automatically reported in CallSetup.
+ // If the reactions are invoked from backup queue (in microtask check point),
+ // we don't need to pass global object for error reporting.
+ InvokeReactions(&mBackupQueue, nullptr);
+ }
+ MOZ_ASSERT(mBackupQueue.IsEmpty(),
+ "There are still some reactions in BackupQueue not being consumed!?!");
+}
+
+void
+CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue,
+ nsIGlobalObject* aGlobal)
+{
+ // This is used for error reporting.
+ Maybe<AutoEntryScript> aes;
+ if (aGlobal) {
+ aes.emplace(aGlobal, "custom elements reaction invocation");
+ }
+
+ // Note: It's possible to re-enter this method.
+ for (uint32_t i = 0; i < aElementQueue->Length(); ++i) {
+ Element* element = aElementQueue->ElementAt(i);
+
+ // ElementQueue hold a element's strong reference, it should not be a nullptr.
+ MOZ_ASSERT(element);
+
+ RefPtr<CustomElementData> elementData = element->GetCustomElementData();
+ if (!elementData) {
+ // This happens when the document is destroyed and the element is already
+ // unlinked, no need to fire the callbacks in this case.
+ continue;
+ }
+
+ auto& reactions = elementData->mReactionQueue;
+ for (uint32_t j = 0; j < reactions.Length(); ++j) {
+ // Transfer the ownership of the entry due to reentrant invocation of
+ // this funciton. The entry will be removed when bug 1379573 is landed.
+ auto reaction(Move(reactions.ElementAt(j)));
+ if (reaction) {
+ ErrorResult rv;
+ reaction->Invoke(element, rv);
+ if (aes) {
+ JSContext* cx = aes->cx();
+ if (rv.MaybeSetPendingException(cx)) {
+ aes->ReportException();
+ }
+ MOZ_ASSERT(!JS_IsExceptionPending(cx));
+ }
+ MOZ_ASSERT(!rv.Failed());
+ }
+ }
+ reactions.Clear();
+ }
+ aElementQueue->Clear();
+}
+
+//-----------------------------------------------------
+// CustomElementDefinition
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementDefinition)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementDefinition)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructor)
+ tmp->mPrototype = nullptr;
+ tmp->mCallbacks = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition)
+ mozilla::dom::LifecycleCallbacks* callbacks = tmp->mCallbacks.get();
+
+ if (callbacks->mAttributeChangedCallback.WasPassed()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+ "mCallbacks->mAttributeChangedCallback");
+ cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
+ }
+
+ if (callbacks->mConnectedCallback.WasPassed()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mConnectedCallback");
+ cb.NoteXPCOMChild(callbacks->mConnectedCallback.Value());
+ }
+
+ if (callbacks->mDisconnectedCallback.WasPassed()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mDisconnectedCallback");
+ cb.NoteXPCOMChild(callbacks->mDisconnectedCallback.Value());
+ }
+
+ if (callbacks->mAdoptedCallback.WasPassed()) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mAdoptedCallback");
+ cb.NoteXPCOMChild(callbacks->mAdoptedCallback.Value());
+ }
+
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConstructor");
+ cb.NoteXPCOMChild(tmp->mConstructor);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementDefinition)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPrototype)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release)
+
CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
nsIAtom* aLocalName,
- JSObject* aConstructor,
+ Function* aConstructor,
+ nsCOMArray<nsIAtom>&& aObservedAttributes,
JSObject* aPrototype,
LifecycleCallbacks* aCallbacks,
uint32_t aDocOrder)
: mType(aType),
mLocalName(aLocalName),
- mConstructor(aConstructor),
+ mConstructor(new CustomElementConstructor(aConstructor)),
+ mObservedAttributes(Move(aObservedAttributes)),
mPrototype(aPrototype),
mCallbacks(aCallbacks),
mDocOrder(aDocOrder)
{
}
+//-----------------------------------------------------
+// CustomElementUpgradeReaction
+
+/* virtual */ void
+CustomElementUpgradeReaction::Invoke(Element* aElement, ErrorResult& aRv)
+{
+ CustomElementRegistry::Upgrade(aElement, mDefinition, aRv);
+}
+
+//-----------------------------------------------------
+// CustomElementCallbackReaction
+
+/* virtual */ void
+CustomElementCallbackReaction::Invoke(Element* aElement, ErrorResult& aRv)
+{
+ mCustomElementCallback->Call();
+}
+
} // namespace dom
-} // namespace mozilla \ No newline at end of file
+} // namespace mozilla