1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/CustomElementRegistry.h"
9 #include "mozilla/AsyncEventDispatcher.h"
10 #include "mozilla/CycleCollectedJSContext.h"
11 #include "mozilla/dom/CustomElementRegistryBinding.h"
12 #include "mozilla/dom/HTMLElementBinding.h"
13 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
14 #include "mozilla/dom/XULElementBinding.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/dom/WebComponentsBinding.h"
17 #include "mozilla/dom/DocGroup.h"
18 #include "mozilla/dom/CustomEvent.h"
19 #include "mozilla/dom/ShadowRoot.h"
20 #include "nsHTMLTags.h"
22 #include "js/ForOfIterator.h" // JS::ForOfIterator
23 #include "xpcprivate.h"
24 #include "nsGlobalWindow.h"
29 //-----------------------------------------------------
30 // CustomElementUpgradeReaction
32 class CustomElementUpgradeReaction final
: public CustomElementReaction
{
34 explicit CustomElementUpgradeReaction(CustomElementDefinition
* aDefinition
)
35 : mDefinition(aDefinition
) {
36 mIsUpgradeReaction
= true;
39 virtual void Traverse(
40 nsCycleCollectionTraversalCallback
& aCb
) const override
{
41 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mDefinition");
43 mDefinition
, NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition
));
46 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const override
{
47 // We don't really own mDefinition.
48 return aMallocSizeOf(this);
53 virtual void Invoke(Element
* aElement
, ErrorResult
& aRv
) override
{
54 CustomElementRegistry::Upgrade(aElement
, mDefinition
, aRv
);
57 const RefPtr
<CustomElementDefinition
> mDefinition
;
60 //-----------------------------------------------------
61 // CustomElementCallbackReaction
63 class CustomElementCallbackReaction final
: public CustomElementReaction
{
65 explicit CustomElementCallbackReaction(
66 UniquePtr
<CustomElementCallback
> aCustomElementCallback
)
67 : mCustomElementCallback(std::move(aCustomElementCallback
)) {}
69 virtual void Traverse(
70 nsCycleCollectionTraversalCallback
& aCb
) const override
{
71 mCustomElementCallback
->Traverse(aCb
);
74 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const override
{
75 size_t n
= aMallocSizeOf(this);
77 n
+= mCustomElementCallback
->SizeOfIncludingThis(aMallocSizeOf
);
83 virtual void Invoke(Element
* aElement
, ErrorResult
& aRv
) override
{
84 mCustomElementCallback
->Call();
87 UniquePtr
<CustomElementCallback
> mCustomElementCallback
;
90 //-----------------------------------------------------
91 // CustomElementCallback
93 size_t LifecycleCallbackArgs::SizeOfExcludingThis(
94 MallocSizeOf aMallocSizeOf
) const {
95 size_t n
= name
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
96 n
+= oldValue
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
97 n
+= newValue
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
98 n
+= namespaceURI
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
102 void CustomElementCallback::Call() {
104 case Document::eConnected
:
105 static_cast<LifecycleConnectedCallback
*>(mCallback
.get())
108 case Document::eDisconnected
:
109 static_cast<LifecycleDisconnectedCallback
*>(mCallback
.get())
112 case Document::eAdopted
:
113 static_cast<LifecycleAdoptedCallback
*>(mCallback
.get())
114 ->Call(mThisObject
, mAdoptedCallbackArgs
.mOldDocument
,
115 mAdoptedCallbackArgs
.mNewDocument
);
117 case Document::eAttributeChanged
:
118 static_cast<LifecycleAttributeChangedCallback
*>(mCallback
.get())
119 ->Call(mThisObject
, mArgs
.name
, mArgs
.oldValue
, mArgs
.newValue
,
122 case Document::eGetCustomInterface
:
123 MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
128 void CustomElementCallback::Traverse(
129 nsCycleCollectionTraversalCallback
& aCb
) const {
130 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mThisObject");
131 aCb
.NoteXPCOMChild(mThisObject
);
133 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mCallback");
134 aCb
.NoteXPCOMChild(mCallback
);
137 size_t CustomElementCallback::SizeOfIncludingThis(
138 MallocSizeOf aMallocSizeOf
) const {
139 size_t n
= aMallocSizeOf(this);
141 // We don't uniquely own mThisObject.
143 // We own mCallback but it doesn't have any special memory reporting we can do
144 // for it other than report its own size.
145 n
+= aMallocSizeOf(mCallback
);
147 n
+= mArgs
.SizeOfExcludingThis(aMallocSizeOf
);
149 // mAdoptedCallbackArgs doesn't really uniquely own its members.
154 CustomElementCallback::CustomElementCallback(
155 Element
* aThisObject
, Document::ElementCallbackType aCallbackType
,
156 mozilla::dom::CallbackFunction
* aCallback
)
157 : mThisObject(aThisObject
), mCallback(aCallback
), mType(aCallbackType
) {}
159 //-----------------------------------------------------
162 CustomElementData::CustomElementData(nsAtom
* aType
)
163 : CustomElementData(aType
, CustomElementData::State::eUndefined
) {}
165 CustomElementData::CustomElementData(nsAtom
* aType
, State aState
)
166 : mState(aState
), mType(aType
) {}
168 void CustomElementData::SetCustomElementDefinition(
169 CustomElementDefinition
* aDefinition
) {
170 MOZ_ASSERT(mState
== State::eCustom
);
171 MOZ_ASSERT(!mCustomElementDefinition
);
172 MOZ_ASSERT(aDefinition
->mType
== mType
);
174 mCustomElementDefinition
= aDefinition
;
177 CustomElementDefinition
* CustomElementData::GetCustomElementDefinition() {
178 MOZ_ASSERT(mCustomElementDefinition
? mState
== State::eCustom
179 : mState
!= State::eCustom
);
181 return mCustomElementDefinition
;
184 void CustomElementData::Traverse(
185 nsCycleCollectionTraversalCallback
& aCb
) const {
186 for (uint32_t i
= 0; i
< mReactionQueue
.Length(); i
++) {
187 if (mReactionQueue
[i
]) {
188 mReactionQueue
[i
]->Traverse(aCb
);
192 if (mCustomElementDefinition
) {
193 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mCustomElementDefinition");
195 mCustomElementDefinition
,
196 NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition
));
200 void CustomElementData::Unlink() {
201 mReactionQueue
.Clear();
202 mCustomElementDefinition
= nullptr;
205 size_t CustomElementData::SizeOfIncludingThis(
206 MallocSizeOf aMallocSizeOf
) const {
207 size_t n
= aMallocSizeOf(this);
209 n
+= mReactionQueue
.ShallowSizeOfExcludingThis(aMallocSizeOf
);
211 for (auto& reaction
: mReactionQueue
) {
212 // "reaction" can be null if we're being called indirectly from
213 // InvokeReactions (e.g. due to a reaction causing a memory report to be
214 // captured somehow).
216 n
+= reaction
->SizeOfIncludingThis(aMallocSizeOf
);
223 //-----------------------------------------------------
224 // CustomElementRegistry
228 class MOZ_RAII AutoConstructionStackEntry final
{
230 AutoConstructionStackEntry(nsTArray
<RefPtr
<Element
>>& aStack
,
233 MOZ_ASSERT(aElement
->IsHTMLElement() || aElement
->IsXULElement());
235 mIndex
= mStack
.Length();
236 mStack
.AppendElement(aElement
);
239 ~AutoConstructionStackEntry() {
240 MOZ_ASSERT(mIndex
== mStack
.Length() - 1,
241 "Removed element should be the last element");
242 mStack
.RemoveElementAt(mIndex
);
246 nsTArray
<RefPtr
<Element
>>& mStack
;
252 // Only needed for refcounted objects.
253 NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry
)
255 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry
)
256 tmp
->mConstructors
.clear();
257 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions
)
258 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap
)
259 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementCreationCallbacks
)
260 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow
)
261 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
262 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
264 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry
)
265 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions
)
266 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap
)
267 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementCreationCallbacks
)
268 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow
)
269 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
271 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry
)
272 for (auto iter
= tmp
->mConstructors
.iter(); !iter
.done(); iter
.next()) {
273 aCallbacks
.Trace(&iter
.get().mutableKey(), "mConstructors key", aClosure
);
275 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
276 NS_IMPL_CYCLE_COLLECTION_TRACE_END
278 NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementRegistry
)
279 NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementRegistry
)
281 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementRegistry
)
282 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
283 NS_INTERFACE_MAP_ENTRY(nsISupports
)
286 CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner
* aWindow
)
287 : mWindow(aWindow
), mIsCustomDefinitionRunning(false) {
290 mozilla::HoldJSObjects(this);
293 CustomElementRegistry::~CustomElementRegistry() {
294 mozilla::DropJSObjects(this);
298 CustomElementRegistry::RunCustomElementCreationCallback::Run() {
300 nsDependentAtomString
value(mAtom
);
301 mCallback
->Call(value
, er
);
302 MOZ_ASSERT(NS_SUCCEEDED(er
.StealNSResult()),
303 "chrome JavaScript error in the callback.");
305 RefPtr
<CustomElementDefinition
> definition
=
306 mRegistry
->mCustomDefinitions
.Get(mAtom
);
307 MOZ_ASSERT(definition
, "Callback should define the definition of type.");
308 MOZ_ASSERT(!mRegistry
->mElementCreationCallbacks
.GetWeak(mAtom
),
309 "Callback should be removed.");
311 nsAutoPtr
<nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>> elements
;
312 mRegistry
->mElementCreationCallbacksUpgradeCandidatesMap
.Remove(mAtom
,
314 MOZ_ASSERT(elements
, "There should be a list");
316 for (auto iter
= elements
->Iter(); !iter
.Done(); iter
.Next()) {
317 nsCOMPtr
<Element
> elem
= do_QueryReferent(iter
.Get()->GetKey());
322 CustomElementRegistry::Upgrade(elem
, definition
, er
);
323 MOZ_ASSERT(NS_SUCCEEDED(er
.StealNSResult()),
324 "chrome JavaScript error in custom element construction.");
330 CustomElementDefinition
* CustomElementRegistry::LookupCustomElementDefinition(
331 nsAtom
* aNameAtom
, int32_t aNameSpaceID
, nsAtom
* aTypeAtom
) {
332 CustomElementDefinition
* data
= mCustomDefinitions
.GetWeak(aTypeAtom
);
335 RefPtr
<CustomElementCreationCallback
> callback
;
336 mElementCreationCallbacks
.Get(aTypeAtom
, getter_AddRefs(callback
));
338 mElementCreationCallbacks
.Remove(aTypeAtom
);
339 mElementCreationCallbacksUpgradeCandidatesMap
.LookupOrAdd(aTypeAtom
);
340 RefPtr
<Runnable
> runnable
=
341 new RunCustomElementCreationCallback(this, aTypeAtom
, callback
);
342 nsContentUtils::AddScriptRunner(runnable
.forget());
343 data
= mCustomDefinitions
.GetWeak(aTypeAtom
);
347 if (data
&& data
->mLocalName
== aNameAtom
&&
348 data
->mNamespaceID
== aNameSpaceID
) {
355 CustomElementDefinition
* CustomElementRegistry::LookupCustomElementDefinition(
356 JSContext
* aCx
, JSObject
* aConstructor
) const {
357 // We're looking up things that tested true for JS::IsConstructor,
358 // so doing a CheckedUnwrapStatic is fine here.
359 JS::Rooted
<JSObject
*> constructor(aCx
, js::CheckedUnwrapStatic(aConstructor
));
361 const auto& ptr
= mConstructors
.lookup(constructor
);
366 CustomElementDefinition
* definition
=
367 mCustomDefinitions
.GetWeak(ptr
->value());
368 MOZ_ASSERT(definition
, "Definition must be found in mCustomDefinitions");
373 void CustomElementRegistry::RegisterUnresolvedElement(Element
* aElement
,
375 // We don't have a use-case for a Custom Element inside NAC, and continuing
376 // here causes performance issues for NAC + XBL anonymous content.
377 if (aElement
->IsInNativeAnonymousSubtree()) {
381 mozilla::dom::NodeInfo
* info
= aElement
->NodeInfo();
383 // Candidate may be a custom element through extension,
384 // in which case the custom element type name will not
385 // match the element tag name. e.g. <button is="x-button">.
386 RefPtr
<nsAtom
> typeName
= aTypeName
;
388 typeName
= info
->NameAtom();
391 if (mCustomDefinitions
.GetWeak(typeName
)) {
395 nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>* unresolved
=
396 mCandidatesMap
.LookupOrAdd(typeName
);
397 nsWeakPtr elem
= do_GetWeakReference(aElement
);
398 unresolved
->PutEntry(elem
);
401 void CustomElementRegistry::UnregisterUnresolvedElement(Element
* aElement
,
403 nsIWeakReference
* weak
= aElement
->GetExistingWeakReference();
410 nsWeakPtr weakPtr
= do_GetWeakReference(aElement
);
412 weak
== weakPtr
.get(),
413 "do_GetWeakReference should reuse the existing nsIWeakReference.");
417 nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>* candidates
= nullptr;
418 if (mCandidatesMap
.Get(aTypeName
, &candidates
)) {
419 MOZ_ASSERT(candidates
);
420 candidates
->RemoveEntry(weak
);
425 UniquePtr
<CustomElementCallback
>
426 CustomElementRegistry::CreateCustomElementCallback(
427 Document::ElementCallbackType aType
, Element
* aCustomElement
,
428 LifecycleCallbackArgs
* aArgs
,
429 LifecycleAdoptedCallbackArgs
* aAdoptedCallbackArgs
,
430 CustomElementDefinition
* aDefinition
) {
431 MOZ_ASSERT(aDefinition
, "CustomElementDefinition should not be null");
432 MOZ_ASSERT(aCustomElement
->GetCustomElementData(),
433 "CustomElementData should exist");
435 // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
436 CallbackFunction
* func
= nullptr;
438 case Document::eConnected
:
439 if (aDefinition
->mCallbacks
->mConnectedCallback
.WasPassed()) {
440 func
= aDefinition
->mCallbacks
->mConnectedCallback
.Value();
444 case Document::eDisconnected
:
445 if (aDefinition
->mCallbacks
->mDisconnectedCallback
.WasPassed()) {
446 func
= aDefinition
->mCallbacks
->mDisconnectedCallback
.Value();
450 case Document::eAdopted
:
451 if (aDefinition
->mCallbacks
->mAdoptedCallback
.WasPassed()) {
452 func
= aDefinition
->mCallbacks
->mAdoptedCallback
.Value();
456 case Document::eAttributeChanged
:
457 if (aDefinition
->mCallbacks
->mAttributeChangedCallback
.WasPassed()) {
458 func
= aDefinition
->mCallbacks
->mAttributeChangedCallback
.Value();
462 case Document::eGetCustomInterface
:
463 MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
467 // If there is no such callback, stop.
472 // Add CALLBACK to ELEMENT's callback queue.
474 MakeUnique
<CustomElementCallback
>(aCustomElement
, aType
, func
);
477 callback
->SetArgs(*aArgs
);
480 if (aAdoptedCallbackArgs
) {
481 callback
->SetAdoptedCallbackArgs(*aAdoptedCallbackArgs
);
487 void CustomElementRegistry::EnqueueLifecycleCallback(
488 Document::ElementCallbackType aType
, Element
* aCustomElement
,
489 LifecycleCallbackArgs
* aArgs
,
490 LifecycleAdoptedCallbackArgs
* aAdoptedCallbackArgs
,
491 CustomElementDefinition
* aDefinition
) {
492 CustomElementDefinition
* definition
= aDefinition
;
494 definition
= aCustomElement
->GetCustomElementDefinition();
496 definition
->mLocalName
!= aCustomElement
->NodeInfo()->NameAtom()) {
500 if (!definition
->mCallbacks
) {
501 // definition has been unlinked. Don't try to mess with it.
506 auto callback
= CreateCustomElementCallback(aType
, aCustomElement
, aArgs
,
507 aAdoptedCallbackArgs
, definition
);
512 DocGroup
* docGroup
= aCustomElement
->OwnerDoc()->GetDocGroup();
517 if (aType
== Document::eAttributeChanged
) {
518 RefPtr
<nsAtom
> attrName
= NS_Atomize(aArgs
->name
);
519 if (definition
->mObservedAttributes
.IsEmpty() ||
520 !definition
->mObservedAttributes
.Contains(attrName
)) {
525 CustomElementReactionsStack
* reactionsStack
=
526 docGroup
->CustomElementReactionsStack();
527 reactionsStack
->EnqueueCallbackReaction(aCustomElement
, std::move(callback
));
532 class CandidateFinder
{
534 CandidateFinder(nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>& aCandidates
,
536 nsTArray
<nsCOMPtr
<Element
>> OrderedCandidates();
539 nsCOMPtr
<Document
> mDoc
;
540 nsInterfaceHashtable
<nsPtrHashKey
<Element
>, Element
> mCandidates
;
543 CandidateFinder::CandidateFinder(
544 nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>& aCandidates
,
546 : mDoc(aDoc
), mCandidates(aCandidates
.Count()) {
548 for (auto iter
= aCandidates
.Iter(); !iter
.Done(); iter
.Next()) {
549 nsCOMPtr
<Element
> elem
= do_QueryReferent(iter
.Get()->GetKey());
554 Element
* key
= elem
.get();
555 mCandidates
.Put(key
, elem
.forget());
559 nsTArray
<nsCOMPtr
<Element
>> CandidateFinder::OrderedCandidates() {
560 if (mCandidates
.Count() == 1) {
561 // Fast path for one candidate.
562 for (auto iter
= mCandidates
.Iter(); !iter
.Done(); iter
.Next()) {
563 nsTArray
<nsCOMPtr
<Element
>> rval({std::move(iter
.Data())});
569 nsTArray
<nsCOMPtr
<Element
>> orderedElements(mCandidates
.Count());
570 for (nsINode
* node
: ShadowIncludingTreeIterator(*mDoc
)) {
571 Element
* element
= Element::FromNode(node
);
576 nsCOMPtr
<Element
> elem
;
577 if (mCandidates
.Remove(element
, getter_AddRefs(elem
))) {
578 orderedElements
.AppendElement(std::move(elem
));
579 if (mCandidates
.Count() == 0) {
585 return orderedElements
;
590 void CustomElementRegistry::UpgradeCandidates(
591 nsAtom
* aKey
, CustomElementDefinition
* aDefinition
, ErrorResult
& aRv
) {
592 DocGroup
* docGroup
= mWindow
->GetDocGroup();
594 aRv
.Throw(NS_ERROR_UNEXPECTED
);
598 nsAutoPtr
<nsTHashtable
<nsRefPtrHashKey
<nsIWeakReference
>>> candidates
;
599 if (mCandidatesMap
.Remove(aKey
, &candidates
)) {
600 MOZ_ASSERT(candidates
);
601 CustomElementReactionsStack
* reactionsStack
=
602 docGroup
->CustomElementReactionsStack();
604 CandidateFinder
finder(*candidates
, mWindow
->GetExtantDoc());
605 for (auto& elem
: finder
.OrderedCandidates()) {
606 reactionsStack
->EnqueueUpgradeReaction(elem
, aDefinition
);
611 JSObject
* CustomElementRegistry::WrapObject(JSContext
* aCx
,
612 JS::Handle
<JSObject
*> aGivenProto
) {
613 return CustomElementRegistry_Binding::Wrap(aCx
, this, aGivenProto
);
616 nsISupports
* CustomElementRegistry::GetParentObject() const { return mWindow
; }
618 DocGroup
* CustomElementRegistry::GetDocGroup() const {
619 return mWindow
? mWindow
->GetDocGroup() : nullptr;
622 int32_t CustomElementRegistry::InferNamespace(
623 JSContext
* aCx
, JS::Handle
<JSObject
*> constructor
) {
624 JS::Rooted
<JSObject
*> XULConstructor(
625 aCx
, XULElement_Binding::GetConstructorObject(aCx
));
627 JS::Rooted
<JSObject
*> proto(aCx
, constructor
);
629 if (proto
== XULConstructor
) {
630 return kNameSpaceID_XUL
;
633 JS_GetPrototype(aCx
, proto
, &proto
);
636 return kNameSpaceID_XHTML
;
639 // https://html.spec.whatwg.org/multipage/scripting.html#element-definition
640 void CustomElementRegistry::Define(
641 JSContext
* aCx
, const nsAString
& aName
,
642 CustomElementConstructor
& aFunctionConstructor
,
643 const ElementDefinitionOptions
& aOptions
, ErrorResult
& aRv
) {
644 JS::Rooted
<JSObject
*> constructor(aCx
, aFunctionConstructor
.CallableOrNull());
646 // We need to do a dynamic unwrap in order to throw the right exception. We
647 // could probably avoid that if we just threw MSG_NOT_CONSTRUCTOR if unwrap
650 // In any case, aCx represents the global we want to be using for the unwrap
652 JS::Rooted
<JSObject
*> constructorUnwrapped(
653 aCx
, js::CheckedUnwrapDynamic(constructor
, aCx
));
654 if (!constructorUnwrapped
) {
655 // If the caller's compartment does not have permission to access the
656 // unwrapped constructor then throw.
657 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
662 * 1. If IsConstructor(constructor) is false, then throw a TypeError and abort
665 if (!JS::IsConstructor(constructorUnwrapped
)) {
666 aRv
.ThrowTypeError
<MSG_NOT_CONSTRUCTOR
>(
667 NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
671 int32_t nameSpaceID
= InferNamespace(aCx
, constructor
);
674 * 2. If name is not a valid custom element name, then throw a "SyntaxError"
675 * DOMException and abort these steps.
677 Document
* doc
= mWindow
->GetExtantDoc();
678 RefPtr
<nsAtom
> nameAtom(NS_Atomize(aName
));
679 if (!nsContentUtils::IsCustomElementName(nameAtom
, nameSpaceID
)) {
680 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
685 * 3. If this CustomElementRegistry contains an entry with name name, then
686 * throw a "NotSupportedError" DOMException and abort these steps.
688 if (mCustomDefinitions
.GetWeak(nameAtom
)) {
689 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
694 * 4. If this CustomElementRegistry contains an entry with constructor
695 * constructor, then throw a "NotSupportedError" DOMException and abort these
698 const auto& ptr
= mConstructors
.lookup(constructorUnwrapped
);
700 MOZ_ASSERT(mCustomDefinitions
.GetWeak(ptr
->value()),
701 "Definition must be found in mCustomDefinitions");
702 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
707 * 5. Let localName be name.
708 * 6. Let extends be the value of the extends member of options, or null if
709 * no such member exists.
710 * 7. If extends is not null, then:
711 * 1. If extends is a valid custom element name, then throw a
712 * "NotSupportedError" DOMException.
713 * 2. If the element interface for extends and the HTML namespace is
714 * HTMLUnknownElement (e.g., if extends does not indicate an element
715 * definition in this specification), then throw a "NotSupportedError"
717 * 3. Set localName to extends.
719 * Special note for XUL elements:
721 * For step 7.1, we'll subject XUL to the same rules as HTML, so that a
722 * custom built-in element will not be extending from a dashed name.
723 * Step 7.2 is disregarded. But, we do check if the name is a dashed name
724 * (i.e. step 2) given that there is no reason for a custom built-in element
725 * type to take on a non-dashed name.
726 * This also ensures the name of the built-in custom element type can never
727 * be the same as the built-in element name, so we don't break the assumption
730 nsAutoString
localName(aName
);
731 if (aOptions
.mExtends
.WasPassed()) {
732 RefPtr
<nsAtom
> extendsAtom(NS_Atomize(aOptions
.mExtends
.Value()));
733 if (nsContentUtils::IsCustomElementName(extendsAtom
, kNameSpaceID_XHTML
)) {
734 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
738 if (nameSpaceID
== kNameSpaceID_XHTML
) {
739 // bgsound and multicol are unknown html element.
740 int32_t tag
= nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom
);
741 if (tag
== eHTMLTag_userdefined
|| tag
== eHTMLTag_bgsound
||
742 tag
== eHTMLTag_multicol
) {
743 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
746 } else { // kNameSpaceID_XUL
747 // As stated above, ensure the name of the customized built-in element
748 // (the one that goes to the |is| attribute) is a dashed name.
749 if (!nsContentUtils::IsNameWithDash(nameAtom
)) {
750 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
755 localName
.Assign(aOptions
.mExtends
.Value());
759 * 8. If this CustomElementRegistry's element definition is running flag is
760 * set, then throw a "NotSupportedError" DOMException and abort these steps.
762 if (mIsCustomDefinitionRunning
) {
763 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
767 auto callbacksHolder
= MakeUnique
<LifecycleCallbacks
>();
768 nsTArray
<RefPtr
<nsAtom
>> observedAttributes
;
769 { // Set mIsCustomDefinitionRunning.
771 * 9. Set this CustomElementRegistry's element definition is running flag.
773 AutoSetRunningFlag
as(this);
776 * 10.1. Let prototype be Get(constructor, "prototype"). Rethrow any
779 // The .prototype on the constructor passed could be an "expando" of a
780 // wrapper. So we should get it from wrapper instead of the underlying
782 JS::Rooted
<JS::Value
> prototype(aCx
);
783 if (!JS_GetProperty(aCx
, constructor
, "prototype", &prototype
)) {
784 aRv
.NoteJSContextException(aCx
);
789 * 10.2. If Type(prototype) is not Object, then throw a TypeError exception.
791 if (!prototype
.isObject()) {
792 aRv
.ThrowTypeError
<MSG_NOT_OBJECT
>(
793 NS_LITERAL_STRING("constructor.prototype"));
798 * 10.3. Let lifecycleCallbacks be a map with the four keys
799 * "connectedCallback", "disconnectedCallback", "adoptedCallback", and
800 * "attributeChangedCallback", each of which belongs to an entry whose
801 * value is null. The 'getCustomInterface' callback is also included
803 * 10.4. For each of the four keys callbackName in lifecycleCallbacks:
804 * 1. Let callbackValue be Get(prototype, callbackName). Rethrow any
806 * 2. If callbackValue is not undefined, then set the value of the
807 * entry in lifecycleCallbacks with key callbackName to the result
808 * of converting callbackValue to the Web IDL Function callback
809 * type. Rethrow any exceptions from the conversion.
811 if (!callbacksHolder
->Init(aCx
, prototype
)) {
812 aRv
.NoteJSContextException(aCx
);
817 * 10.5. Let observedAttributes be an empty sequence<DOMString>.
818 * 10.6. If the value of the entry in lifecycleCallbacks with key
819 * "attributeChangedCallback" is not null, then:
820 * 1. Let observedAttributesIterable be Get(constructor,
821 * "observedAttributes"). Rethrow any exceptions.
822 * 2. If observedAttributesIterable is not undefined, then set
823 * observedAttributes to the result of converting
824 * observedAttributesIterable to a sequence<DOMString>. Rethrow
825 * any exceptions from the conversion.
827 if (callbacksHolder
->mAttributeChangedCallback
.WasPassed()) {
828 JS::Rooted
<JS::Value
> observedAttributesIterable(aCx
);
830 if (!JS_GetProperty(aCx
, constructor
, "observedAttributes",
831 &observedAttributesIterable
)) {
832 aRv
.NoteJSContextException(aCx
);
836 if (!observedAttributesIterable
.isUndefined()) {
837 if (!observedAttributesIterable
.isObject()) {
838 aRv
.ThrowTypeError
<MSG_NOT_SEQUENCE
>(
839 NS_LITERAL_STRING("observedAttributes"));
843 JS::ForOfIterator
iter(aCx
);
844 if (!iter
.init(observedAttributesIterable
,
845 JS::ForOfIterator::AllowNonIterable
)) {
846 aRv
.NoteJSContextException(aCx
);
850 if (!iter
.valueIsIterable()) {
851 aRv
.ThrowTypeError
<MSG_NOT_SEQUENCE
>(
852 NS_LITERAL_STRING("observedAttributes"));
856 JS::Rooted
<JS::Value
> attribute(aCx
);
859 if (!iter
.next(&attribute
, &done
)) {
860 aRv
.NoteJSContextException(aCx
);
867 nsAutoString attrStr
;
868 if (!ConvertJSValueToString(aCx
, attribute
, eStringify
, eStringify
,
870 aRv
.NoteJSContextException(aCx
);
874 if (!observedAttributes
.AppendElement(NS_Atomize(attrStr
))) {
875 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
881 } // Unset mIsCustomDefinitionRunning
884 * 11. Let definition be a new custom element definition with name name,
885 * local name localName, constructor constructor, prototype prototype,
886 * observed attributes observedAttributes, and lifecycle callbacks
887 * lifecycleCallbacks.
889 // Associate the definition with the custom element.
890 RefPtr
<nsAtom
> localNameAtom(NS_Atomize(localName
));
893 * 12. Add definition to this CustomElementRegistry.
895 if (!mConstructors
.put(constructorUnwrapped
, nameAtom
)) {
896 aRv
.Throw(NS_ERROR_FAILURE
);
900 RefPtr
<CustomElementDefinition
> definition
= new CustomElementDefinition(
901 nameAtom
, localNameAtom
, nameSpaceID
, &aFunctionConstructor
,
902 std::move(observedAttributes
), std::move(callbacksHolder
));
904 CustomElementDefinition
* def
= definition
.get();
905 mCustomDefinitions
.Put(nameAtom
, definition
.forget());
907 MOZ_ASSERT(mCustomDefinitions
.Count() == mConstructors
.count(),
908 "Number of entries should be the same");
911 * 13. 14. 15. Upgrade candidates
913 UpgradeCandidates(nameAtom
, def
, aRv
);
916 * 16. If this CustomElementRegistry's when-defined promise map contains an
917 * entry with key name:
918 * 1. Let promise be the value of that entry.
919 * 2. Resolve promise with undefined.
920 * 3. Delete the entry with key name from this CustomElementRegistry's
921 * when-defined promise map.
923 RefPtr
<Promise
> promise
;
924 mWhenDefinedPromiseMap
.Remove(nameAtom
, getter_AddRefs(promise
));
926 promise
->MaybeResolveWithUndefined();
929 // Dispatch a "customelementdefined" event for DevTools.
931 JSString
* nameJsStr
=
932 JS_NewUCStringCopyN(aCx
, aName
.BeginReading(), aName
.Length());
934 JS::Rooted
<JS::Value
> detail(aCx
, JS::StringValue(nameJsStr
));
935 RefPtr
<CustomEvent
> event
= NS_NewDOMCustomEvent(doc
, nullptr, nullptr);
936 event
->InitCustomEvent(aCx
, NS_LITERAL_STRING("customelementdefined"),
937 /* CanBubble */ true,
938 /* Cancelable */ true, detail
);
939 event
->SetTrusted(true);
941 AsyncEventDispatcher
* dispatcher
= new AsyncEventDispatcher(doc
, event
);
942 dispatcher
->mOnlyChromeDispatch
= ChromeOnlyDispatch::eYes
;
944 dispatcher
->PostDOMEvent();
948 * Clean-up mElementCreationCallbacks (if it exists)
950 mElementCreationCallbacks
.Remove(nameAtom
);
953 void CustomElementRegistry::SetElementCreationCallback(
954 const nsAString
& aName
, CustomElementCreationCallback
& aCallback
,
956 RefPtr
<nsAtom
> nameAtom(NS_Atomize(aName
));
957 if (mElementCreationCallbacks
.GetWeak(nameAtom
) ||
958 mCustomDefinitions
.GetWeak(nameAtom
)) {
959 aRv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
963 RefPtr
<CustomElementCreationCallback
> callback
= &aCallback
;
964 mElementCreationCallbacks
.Put(nameAtom
, callback
.forget());
968 void CustomElementRegistry::Upgrade(nsINode
& aRoot
) {
969 for (nsINode
* node
: ShadowIncludingTreeIterator(aRoot
)) {
970 Element
* element
= Element::FromNode(node
);
975 CustomElementData
* ceData
= element
->GetCustomElementData();
977 NodeInfo
* nodeInfo
= element
->NodeInfo();
978 nsAtom
* typeAtom
= ceData
->GetCustomElementType();
979 CustomElementDefinition
* definition
=
980 nsContentUtils::LookupCustomElementDefinition(
981 nodeInfo
->GetDocument(), nodeInfo
->NameAtom(),
982 nodeInfo
->NamespaceID(), typeAtom
);
984 nsContentUtils::EnqueueUpgradeReaction(element
, definition
);
990 void CustomElementRegistry::Get(JSContext
* aCx
, const nsAString
& aName
,
991 JS::MutableHandle
<JS::Value
> aRetVal
) {
992 RefPtr
<nsAtom
> nameAtom(NS_Atomize(aName
));
993 CustomElementDefinition
* data
= mCustomDefinitions
.GetWeak(nameAtom
);
996 aRetVal
.setUndefined();
1000 aRetVal
.setObject(*data
->mConstructor
->Callback(aCx
));
1003 already_AddRefed
<Promise
> CustomElementRegistry::WhenDefined(
1004 const nsAString
& aName
, ErrorResult
& aRv
) {
1005 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(mWindow
);
1006 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
1012 RefPtr
<nsAtom
> nameAtom(NS_Atomize(aName
));
1013 Document
* doc
= mWindow
->GetExtantDoc();
1014 uint32_t nameSpaceID
=
1015 doc
? doc
->GetDefaultNamespaceID() : kNameSpaceID_XHTML
;
1016 if (!nsContentUtils::IsCustomElementName(nameAtom
, nameSpaceID
)) {
1017 promise
->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR
);
1018 return promise
.forget();
1021 if (mCustomDefinitions
.GetWeak(nameAtom
)) {
1022 promise
->MaybeResolve(JS::UndefinedHandleValue
);
1023 return promise
.forget();
1026 auto entry
= mWhenDefinedPromiseMap
.LookupForAdd(nameAtom
);
1028 promise
= entry
.Data();
1030 entry
.OrInsert([&promise
]() { return promise
; });
1033 return promise
.forget();
1039 static void DoUpgrade(Element
* aElement
, CustomElementConstructor
* aConstructor
,
1041 JS::Rooted
<JS::Value
> constructResult(RootingCx());
1042 // Rethrow the exception since it might actually throw the exception from the
1043 // upgrade steps back out to the caller of document.createElement.
1044 aConstructor
->Construct(&constructResult
, aRv
, "Custom Element Upgrade",
1045 CallbackFunction::eRethrowExceptions
);
1051 // constructResult is an ObjectValue because construction with a callback
1052 // always forms the return value from a JSObject.
1053 if (NS_FAILED(UNWRAP_OBJECT(Element
, &constructResult
, element
)) ||
1054 element
!= aElement
) {
1055 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1060 } // anonymous namespace
1062 // https://html.spec.whatwg.org/multipage/scripting.html#upgrades
1064 void CustomElementRegistry::Upgrade(Element
* aElement
,
1065 CustomElementDefinition
* aDefinition
,
1067 RefPtr
<CustomElementData
> data
= aElement
->GetCustomElementData();
1068 MOZ_ASSERT(data
, "CustomElementData should exist");
1070 // Step 1 and step 2.
1071 if (data
->mState
== CustomElementData::State::eCustom
||
1072 data
->mState
== CustomElementData::State::eFailed
) {
1077 if (!aDefinition
->mObservedAttributes
.IsEmpty()) {
1078 uint32_t count
= aElement
->GetAttrCount();
1079 for (uint32_t i
= 0; i
< count
; i
++) {
1080 mozilla::dom::BorrowedAttrInfo info
= aElement
->GetAttrInfoAt(i
);
1082 const nsAttrName
* name
= info
.mName
;
1083 nsAtom
* attrName
= name
->LocalName();
1085 if (aDefinition
->IsInObservedAttributeList(attrName
)) {
1086 int32_t namespaceID
= name
->NamespaceID();
1087 nsAutoString attrValue
, namespaceURI
;
1088 info
.mValue
->ToString(attrValue
);
1089 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID
,
1092 LifecycleCallbackArgs args
= {
1093 nsDependentAtomString(attrName
), VoidString(), attrValue
,
1094 (namespaceURI
.IsEmpty() ? VoidString() : namespaceURI
)};
1095 nsContentUtils::EnqueueLifecycleCallback(
1096 Document::eAttributeChanged
, aElement
, &args
, nullptr, aDefinition
);
1102 if (aElement
->IsInComposedDoc()) {
1103 nsContentUtils::EnqueueLifecycleCallback(Document::eConnected
, aElement
,
1104 nullptr, nullptr, aDefinition
);
1108 AutoConstructionStackEntry
acs(aDefinition
->mConstructionStack
, aElement
);
1110 // Step 6 and step 7.
1111 DoUpgrade(aElement
, MOZ_KnownLive(aDefinition
->mConstructor
), aRv
);
1113 data
->mState
= CustomElementData::State::eFailed
;
1114 // Empty element's custom element reaction queue.
1115 data
->mReactionQueue
.Clear();
1120 data
->mState
= CustomElementData::State::eCustom
;
1121 aElement
->SetDefined(true);
1124 aElement
->SetCustomElementDefinition(aDefinition
);
1127 already_AddRefed
<nsISupports
> CustomElementRegistry::CallGetCustomInterface(
1128 Element
* aElement
, const nsIID
& aIID
) {
1129 MOZ_ASSERT(aElement
);
1131 if (!nsContentUtils::IsChromeDoc(aElement
->OwnerDoc())) {
1135 // Try to get our GetCustomInterfaceCallback callback.
1136 CustomElementDefinition
* definition
= aElement
->GetCustomElementDefinition();
1137 if (!definition
|| !definition
->mCallbacks
||
1138 !definition
->mCallbacks
->mGetCustomInterfaceCallback
.WasPassed() ||
1139 (definition
->mLocalName
!= aElement
->NodeInfo()->NameAtom())) {
1142 LifecycleGetCustomInterfaceCallback
* func
=
1143 definition
->mCallbacks
->mGetCustomInterfaceCallback
.Value();
1145 // Initialize a AutoJSAPI to enter the compartment of the callback.
1147 JS::RootedObject
funcGlobal(RootingCx(), func
->CallbackGlobalOrNull());
1148 if (!funcGlobal
|| !jsapi
.Init(funcGlobal
)) {
1152 // Grab our JSContext.
1153 JSContext
* cx
= jsapi
.cx();
1155 // Convert our IID to a JSValue to call our callback.
1156 JS::RootedValue
jsiid(cx
);
1157 if (!xpc::ID2JSValue(cx
, aIID
, &jsiid
)) {
1161 JS::RootedObject
customInterface(cx
);
1162 func
->Call(aElement
, jsiid
, &customInterface
);
1163 if (!customInterface
) {
1167 // Wrap our JSObject into a nsISupports through XPConnect
1168 nsCOMPtr
<nsISupports
> wrapper
;
1169 nsresult rv
= nsContentUtils::XPConnect()->WrapJSAggregatedToNative(
1170 aElement
, cx
, customInterface
, aIID
, getter_AddRefs(wrapper
));
1171 if (NS_WARN_IF(NS_FAILED(rv
))) {
1175 return wrapper
.forget();
1178 //-----------------------------------------------------
1179 // CustomElementReactionsStack
1181 void CustomElementReactionsStack::CreateAndPushElementQueue() {
1182 MOZ_ASSERT(mRecursionDepth
);
1183 MOZ_ASSERT(!mIsElementQueuePushedForCurrentRecursionDepth
);
1185 // Push a new element queue onto the custom element reactions stack.
1186 mReactionsStack
.AppendElement(MakeUnique
<ElementQueue
>());
1187 mIsElementQueuePushedForCurrentRecursionDepth
= true;
1190 void CustomElementReactionsStack::PopAndInvokeElementQueue() {
1191 MOZ_ASSERT(mRecursionDepth
);
1192 MOZ_ASSERT(mIsElementQueuePushedForCurrentRecursionDepth
);
1193 MOZ_ASSERT(!mReactionsStack
.IsEmpty(), "Reaction stack shouldn't be empty");
1195 // Pop the element queue from the custom element reactions stack,
1196 // and invoke custom element reactions in that queue.
1197 const uint32_t lastIndex
= mReactionsStack
.Length() - 1;
1198 ElementQueue
* elementQueue
= mReactionsStack
.ElementAt(lastIndex
).get();
1199 // Check element queue size in order to reduce function call overhead.
1200 if (!elementQueue
->IsEmpty()) {
1201 // It is still not clear what error reporting will look like in custom
1202 // element, see https://github.com/w3c/webcomponents/issues/635.
1203 // We usually report the error to entry global in gecko, so just follow the
1204 // same behavior here.
1205 // This may be null if it's called from parser, see the case of
1206 // attributeChangedCallback in
1207 // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
1208 // In that case, the exception of callback reactions will be automatically
1209 // reported in CallSetup.
1210 nsIGlobalObject
* global
= GetEntryGlobal();
1211 InvokeReactions(elementQueue
, MOZ_KnownLive(global
));
1214 // InvokeReactions() might create other custom element reactions, but those
1215 // new reactions should be already consumed and removed at this point.
1217 lastIndex
== mReactionsStack
.Length() - 1,
1218 "reactions created by InvokeReactions() should be consumed and removed");
1220 mReactionsStack
.RemoveElementAt(lastIndex
);
1221 mIsElementQueuePushedForCurrentRecursionDepth
= false;
1224 void CustomElementReactionsStack::EnqueueUpgradeReaction(
1225 Element
* aElement
, CustomElementDefinition
* aDefinition
) {
1226 Enqueue(aElement
, new CustomElementUpgradeReaction(aDefinition
));
1229 void CustomElementReactionsStack::EnqueueCallbackReaction(
1231 UniquePtr
<CustomElementCallback
> aCustomElementCallback
) {
1233 new CustomElementCallbackReaction(std::move(aCustomElementCallback
)));
1236 void CustomElementReactionsStack::Enqueue(Element
* aElement
,
1237 CustomElementReaction
* aReaction
) {
1238 RefPtr
<CustomElementData
> elementData
= aElement
->GetCustomElementData();
1239 MOZ_ASSERT(elementData
, "CustomElementData should exist");
1241 if (mRecursionDepth
) {
1242 // If the element queue is not created for current recursion depth, create
1243 // and push an element queue to reactions stack first.
1244 if (!mIsElementQueuePushedForCurrentRecursionDepth
) {
1245 CreateAndPushElementQueue();
1248 MOZ_ASSERT(!mReactionsStack
.IsEmpty());
1249 // Add element to the current element queue.
1250 mReactionsStack
.LastElement()->AppendElement(aElement
);
1251 elementData
->mReactionQueue
.AppendElement(aReaction
);
1255 // If the custom element reactions stack is empty, then:
1256 // Add element to the backup element queue.
1257 MOZ_ASSERT(mReactionsStack
.IsEmpty(),
1258 "custom element reactions stack should be empty");
1259 mBackupQueue
.AppendElement(aElement
);
1260 elementData
->mReactionQueue
.AppendElement(aReaction
);
1262 if (mIsBackupQueueProcessing
) {
1266 CycleCollectedJSContext
* context
= CycleCollectedJSContext::Get();
1267 RefPtr
<BackupQueueMicroTask
> bqmt
= new BackupQueueMicroTask(this);
1268 context
->DispatchToMicroTask(bqmt
.forget());
1271 void CustomElementReactionsStack::InvokeBackupQueue() {
1272 // Check backup queue size in order to reduce function call overhead.
1273 if (!mBackupQueue
.IsEmpty()) {
1274 // Upgrade reactions won't be scheduled in backup queue and the exception of
1275 // callback reactions will be automatically reported in CallSetup.
1276 // If the reactions are invoked from backup queue (in microtask check
1277 // point), we don't need to pass global object for error reporting.
1278 InvokeReactions(&mBackupQueue
, nullptr);
1281 mBackupQueue
.IsEmpty(),
1282 "There are still some reactions in BackupQueue not being consumed!?!");
1285 void CustomElementReactionsStack::InvokeReactions(ElementQueue
* aElementQueue
,
1286 nsIGlobalObject
* aGlobal
) {
1287 // This is used for error reporting.
1288 Maybe
<AutoEntryScript
> aes
;
1290 aes
.emplace(aGlobal
, "custom elements reaction invocation");
1293 // Note: It's possible to re-enter this method.
1294 for (uint32_t i
= 0; i
< aElementQueue
->Length(); ++i
) {
1295 Element
* element
= aElementQueue
->ElementAt(i
);
1296 // ElementQueue hold a element's strong reference, it should not be a
1298 MOZ_ASSERT(element
);
1300 RefPtr
<CustomElementData
> elementData
= element
->GetCustomElementData();
1301 if (!elementData
|| !element
->GetOwnerGlobal()) {
1302 // This happens when the document is destroyed and the element is already
1303 // unlinked, no need to fire the callbacks in this case.
1307 auto& reactions
= elementData
->mReactionQueue
;
1308 for (uint32_t j
= 0; j
< reactions
.Length(); ++j
) {
1309 // Transfer the ownership of the entry due to reentrant invocation of
1311 auto reaction(std::move(reactions
.ElementAt(j
)));
1313 if (!aGlobal
&& reaction
->IsUpgradeReaction()) {
1314 nsIGlobalObject
* global
= element
->GetOwnerGlobal();
1316 aes
.emplace(global
, "custom elements reaction invocation");
1319 reaction
->Invoke(MOZ_KnownLive(element
), rv
);
1321 JSContext
* cx
= aes
->cx();
1322 if (rv
.MaybeSetPendingException(cx
)) {
1323 aes
->ReportException();
1325 MOZ_ASSERT(!JS_IsExceptionPending(cx
));
1326 if (!aGlobal
&& reaction
->IsUpgradeReaction()) {
1330 MOZ_ASSERT(!rv
.Failed());
1335 aElementQueue
->Clear();
1338 //-----------------------------------------------------
1339 // CustomElementDefinition
1341 NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementDefinition
)
1343 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementDefinition
)
1344 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructor
)
1345 tmp
->mCallbacks
= nullptr;
1346 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1348 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition
)
1349 mozilla::dom::LifecycleCallbacks
* callbacks
= tmp
->mCallbacks
.get();
1351 if (callbacks
->mAttributeChangedCallback
.WasPassed()) {
1352 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
1353 "mCallbacks->mAttributeChangedCallback");
1354 cb
.NoteXPCOMChild(callbacks
->mAttributeChangedCallback
.Value());
1357 if (callbacks
->mConnectedCallback
.WasPassed()) {
1358 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mCallbacks->mConnectedCallback");
1359 cb
.NoteXPCOMChild(callbacks
->mConnectedCallback
.Value());
1362 if (callbacks
->mDisconnectedCallback
.WasPassed()) {
1363 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mCallbacks->mDisconnectedCallback");
1364 cb
.NoteXPCOMChild(callbacks
->mDisconnectedCallback
.Value());
1367 if (callbacks
->mAdoptedCallback
.WasPassed()) {
1368 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mCallbacks->mAdoptedCallback");
1369 cb
.NoteXPCOMChild(callbacks
->mAdoptedCallback
.Value());
1372 if (callbacks
->mGetCustomInterfaceCallback
.WasPassed()) {
1373 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1374 cb
, "mCallbacks->mGetCustomInterfaceCallback");
1375 cb
.NoteXPCOMChild(callbacks
->mGetCustomInterfaceCallback
.Value());
1378 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mConstructor");
1379 cb
.NoteXPCOMChild(tmp
->mConstructor
);
1380 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1382 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementDefinition
)
1383 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1385 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition
, AddRef
)
1386 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition
, Release
)
1388 CustomElementDefinition::CustomElementDefinition(
1389 nsAtom
* aType
, nsAtom
* aLocalName
, int32_t aNamespaceID
,
1390 CustomElementConstructor
* aConstructor
,
1391 nsTArray
<RefPtr
<nsAtom
>>&& aObservedAttributes
,
1392 UniquePtr
<LifecycleCallbacks
>&& aCallbacks
)
1394 mLocalName(aLocalName
),
1395 mNamespaceID(aNamespaceID
),
1396 mConstructor(aConstructor
),
1397 mObservedAttributes(std::move(aObservedAttributes
)),
1398 mCallbacks(std::move(aCallbacks
)) {}
1401 } // namespace mozilla