Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / CustomElementRegistry.cpp
blob1ebdea55ef0a9cca3f6deacef3e0e65d50c3587f
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"
21 #include "jsapi.h"
22 #include "js/ForOfIterator.h" // JS::ForOfIterator
23 #include "xpcprivate.h"
24 #include "nsGlobalWindow.h"
26 namespace mozilla {
27 namespace dom {
29 //-----------------------------------------------------
30 // CustomElementUpgradeReaction
32 class CustomElementUpgradeReaction final : public CustomElementReaction {
33 public:
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");
42 aCb.NoteNativeChild(
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);
51 private:
52 MOZ_CAN_RUN_SCRIPT
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 {
64 public:
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);
79 return n;
82 private:
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);
99 return n;
102 void CustomElementCallback::Call() {
103 switch (mType) {
104 case Document::eConnected:
105 static_cast<LifecycleConnectedCallback*>(mCallback.get())
106 ->Call(mThisObject);
107 break;
108 case Document::eDisconnected:
109 static_cast<LifecycleDisconnectedCallback*>(mCallback.get())
110 ->Call(mThisObject);
111 break;
112 case Document::eAdopted:
113 static_cast<LifecycleAdoptedCallback*>(mCallback.get())
114 ->Call(mThisObject, mAdoptedCallbackArgs.mOldDocument,
115 mAdoptedCallbackArgs.mNewDocument);
116 break;
117 case Document::eAttributeChanged:
118 static_cast<LifecycleAttributeChangedCallback*>(mCallback.get())
119 ->Call(mThisObject, mArgs.name, mArgs.oldValue, mArgs.newValue,
120 mArgs.namespaceURI);
121 break;
122 case Document::eGetCustomInterface:
123 MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
124 break;
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.
151 return n;
154 CustomElementCallback::CustomElementCallback(
155 Element* aThisObject, Document::ElementCallbackType aCallbackType,
156 mozilla::dom::CallbackFunction* aCallback)
157 : mThisObject(aThisObject), mCallback(aCallback), mType(aCallbackType) {}
159 //-----------------------------------------------------
160 // CustomElementData
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");
194 aCb.NoteNativeChild(
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).
215 if (reaction) {
216 n += reaction->SizeOfIncludingThis(aMallocSizeOf);
220 return n;
223 //-----------------------------------------------------
224 // CustomElementRegistry
226 namespace {
228 class MOZ_RAII AutoConstructionStackEntry final {
229 public:
230 AutoConstructionStackEntry(nsTArray<RefPtr<Element>>& aStack,
231 Element* aElement)
232 : mStack(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);
245 private:
246 nsTArray<RefPtr<Element>>& mStack;
247 uint32_t mIndex;
250 } // namespace
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)
284 NS_INTERFACE_MAP_END
286 CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow)
287 : mWindow(aWindow), mIsCustomDefinitionRunning(false) {
288 MOZ_ASSERT(aWindow);
290 mozilla::HoldJSObjects(this);
293 CustomElementRegistry::~CustomElementRegistry() {
294 mozilla::DropJSObjects(this);
297 NS_IMETHODIMP
298 CustomElementRegistry::RunCustomElementCreationCallback::Run() {
299 ErrorResult er;
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,
313 &elements);
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());
318 if (!elem) {
319 continue;
322 CustomElementRegistry::Upgrade(elem, definition, er);
323 MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()),
324 "chrome JavaScript error in custom element construction.");
327 return NS_OK;
330 CustomElementDefinition* CustomElementRegistry::LookupCustomElementDefinition(
331 nsAtom* aNameAtom, int32_t aNameSpaceID, nsAtom* aTypeAtom) {
332 CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom);
334 if (!data) {
335 RefPtr<CustomElementCreationCallback> callback;
336 mElementCreationCallbacks.Get(aTypeAtom, getter_AddRefs(callback));
337 if (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) {
349 return data;
352 return nullptr;
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);
362 if (!ptr) {
363 return nullptr;
366 CustomElementDefinition* definition =
367 mCustomDefinitions.GetWeak(ptr->value());
368 MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
370 return definition;
373 void CustomElementRegistry::RegisterUnresolvedElement(Element* aElement,
374 nsAtom* aTypeName) {
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()) {
378 return;
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;
387 if (!typeName) {
388 typeName = info->NameAtom();
391 if (mCustomDefinitions.GetWeak(typeName)) {
392 return;
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,
402 nsAtom* aTypeName) {
403 nsIWeakReference* weak = aElement->GetExistingWeakReference();
404 if (!weak) {
405 return;
408 #ifdef DEBUG
410 nsWeakPtr weakPtr = do_GetWeakReference(aElement);
411 MOZ_ASSERT(
412 weak == weakPtr.get(),
413 "do_GetWeakReference should reuse the existing nsIWeakReference.");
415 #endif
417 nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>* candidates = nullptr;
418 if (mCandidatesMap.Get(aTypeName, &candidates)) {
419 MOZ_ASSERT(candidates);
420 candidates->RemoveEntry(weak);
424 /* static */
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;
437 switch (aType) {
438 case Document::eConnected:
439 if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) {
440 func = aDefinition->mCallbacks->mConnectedCallback.Value();
442 break;
444 case Document::eDisconnected:
445 if (aDefinition->mCallbacks->mDisconnectedCallback.WasPassed()) {
446 func = aDefinition->mCallbacks->mDisconnectedCallback.Value();
448 break;
450 case Document::eAdopted:
451 if (aDefinition->mCallbacks->mAdoptedCallback.WasPassed()) {
452 func = aDefinition->mCallbacks->mAdoptedCallback.Value();
454 break;
456 case Document::eAttributeChanged:
457 if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
458 func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
460 break;
462 case Document::eGetCustomInterface:
463 MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
464 break;
467 // If there is no such callback, stop.
468 if (!func) {
469 return nullptr;
472 // Add CALLBACK to ELEMENT's callback queue.
473 auto callback =
474 MakeUnique<CustomElementCallback>(aCustomElement, aType, func);
476 if (aArgs) {
477 callback->SetArgs(*aArgs);
480 if (aAdoptedCallbackArgs) {
481 callback->SetAdoptedCallbackArgs(*aAdoptedCallbackArgs);
483 return callback;
486 /* static */
487 void CustomElementRegistry::EnqueueLifecycleCallback(
488 Document::ElementCallbackType aType, Element* aCustomElement,
489 LifecycleCallbackArgs* aArgs,
490 LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
491 CustomElementDefinition* aDefinition) {
492 CustomElementDefinition* definition = aDefinition;
493 if (!definition) {
494 definition = aCustomElement->GetCustomElementDefinition();
495 if (!definition ||
496 definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) {
497 return;
500 if (!definition->mCallbacks) {
501 // definition has been unlinked. Don't try to mess with it.
502 return;
506 auto callback = CreateCustomElementCallback(aType, aCustomElement, aArgs,
507 aAdoptedCallbackArgs, definition);
508 if (!callback) {
509 return;
512 DocGroup* docGroup = aCustomElement->OwnerDoc()->GetDocGroup();
513 if (!docGroup) {
514 return;
517 if (aType == Document::eAttributeChanged) {
518 RefPtr<nsAtom> attrName = NS_Atomize(aArgs->name);
519 if (definition->mObservedAttributes.IsEmpty() ||
520 !definition->mObservedAttributes.Contains(attrName)) {
521 return;
525 CustomElementReactionsStack* reactionsStack =
526 docGroup->CustomElementReactionsStack();
527 reactionsStack->EnqueueCallbackReaction(aCustomElement, std::move(callback));
530 namespace {
532 class CandidateFinder {
533 public:
534 CandidateFinder(nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates,
535 Document* aDoc);
536 nsTArray<nsCOMPtr<Element>> OrderedCandidates();
538 private:
539 nsCOMPtr<Document> mDoc;
540 nsInterfaceHashtable<nsPtrHashKey<Element>, Element> mCandidates;
543 CandidateFinder::CandidateFinder(
544 nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates,
545 Document* aDoc)
546 : mDoc(aDoc), mCandidates(aCandidates.Count()) {
547 MOZ_ASSERT(mDoc);
548 for (auto iter = aCandidates.Iter(); !iter.Done(); iter.Next()) {
549 nsCOMPtr<Element> elem = do_QueryReferent(iter.Get()->GetKey());
550 if (!elem) {
551 continue;
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())});
564 iter.Remove();
565 return rval;
569 nsTArray<nsCOMPtr<Element>> orderedElements(mCandidates.Count());
570 for (nsINode* node : ShadowIncludingTreeIterator(*mDoc)) {
571 Element* element = Element::FromNode(node);
572 if (!element) {
573 continue;
576 nsCOMPtr<Element> elem;
577 if (mCandidates.Remove(element, getter_AddRefs(elem))) {
578 orderedElements.AppendElement(std::move(elem));
579 if (mCandidates.Count() == 0) {
580 break;
585 return orderedElements;
588 } // namespace
590 void CustomElementRegistry::UpgradeCandidates(
591 nsAtom* aKey, CustomElementDefinition* aDefinition, ErrorResult& aRv) {
592 DocGroup* docGroup = mWindow->GetDocGroup();
593 if (!docGroup) {
594 aRv.Throw(NS_ERROR_UNEXPECTED);
595 return;
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);
628 while (proto) {
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
648 // fails.
650 // In any case, aCx represents the global we want to be using for the unwrap
651 // here.
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);
658 return;
662 * 1. If IsConstructor(constructor) is false, then throw a TypeError and abort
663 * these steps.
665 if (!JS::IsConstructor(constructorUnwrapped)) {
666 aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(
667 NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define"));
668 return;
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);
681 return;
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);
690 return;
694 * 4. If this CustomElementRegistry contains an entry with constructor
695 * constructor, then throw a "NotSupportedError" DOMException and abort these
696 * steps.
698 const auto& ptr = mConstructors.lookup(constructorUnwrapped);
699 if (ptr) {
700 MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
701 "Definition must be found in mCustomDefinitions");
702 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
703 return;
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"
716 * DOMException.
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
728 * elsewhere.
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);
735 return;
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);
744 return;
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);
751 return;
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);
764 return;
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
777 * exceptions.
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
781 // object.
782 JS::Rooted<JS::Value> prototype(aCx);
783 if (!JS_GetProperty(aCx, constructor, "prototype", &prototype)) {
784 aRv.NoteJSContextException(aCx);
785 return;
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"));
794 return;
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
802 * for chrome usage.
803 * 10.4. For each of the four keys callbackName in lifecycleCallbacks:
804 * 1. Let callbackValue be Get(prototype, callbackName). Rethrow any
805 * exceptions.
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);
813 return;
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);
833 return;
836 if (!observedAttributesIterable.isUndefined()) {
837 if (!observedAttributesIterable.isObject()) {
838 aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
839 NS_LITERAL_STRING("observedAttributes"));
840 return;
843 JS::ForOfIterator iter(aCx);
844 if (!iter.init(observedAttributesIterable,
845 JS::ForOfIterator::AllowNonIterable)) {
846 aRv.NoteJSContextException(aCx);
847 return;
850 if (!iter.valueIsIterable()) {
851 aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
852 NS_LITERAL_STRING("observedAttributes"));
853 return;
856 JS::Rooted<JS::Value> attribute(aCx);
857 while (true) {
858 bool done;
859 if (!iter.next(&attribute, &done)) {
860 aRv.NoteJSContextException(aCx);
861 return;
863 if (done) {
864 break;
867 nsAutoString attrStr;
868 if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
869 attrStr)) {
870 aRv.NoteJSContextException(aCx);
871 return;
874 if (!observedAttributes.AppendElement(NS_Atomize(attrStr))) {
875 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
876 return;
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);
897 return;
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));
925 if (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,
955 ErrorResult& aRv) {
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);
960 return;
963 RefPtr<CustomElementCreationCallback> callback = &aCallback;
964 mElementCreationCallbacks.Put(nameAtom, callback.forget());
965 return;
968 void CustomElementRegistry::Upgrade(nsINode& aRoot) {
969 for (nsINode* node : ShadowIncludingTreeIterator(aRoot)) {
970 Element* element = Element::FromNode(node);
971 if (!element) {
972 continue;
975 CustomElementData* ceData = element->GetCustomElementData();
976 if (ceData) {
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);
983 if (definition) {
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);
995 if (!data) {
996 aRetVal.setUndefined();
997 return;
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);
1008 if (aRv.Failed()) {
1009 return nullptr;
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);
1027 if (entry) {
1028 promise = entry.Data();
1029 } else {
1030 entry.OrInsert([&promise]() { return promise; });
1033 return promise.forget();
1036 namespace {
1038 MOZ_CAN_RUN_SCRIPT
1039 static void DoUpgrade(Element* aElement, CustomElementConstructor* aConstructor,
1040 ErrorResult& aRv) {
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);
1046 if (aRv.Failed()) {
1047 return;
1050 Element* element;
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);
1056 return;
1060 } // anonymous namespace
1062 // https://html.spec.whatwg.org/multipage/scripting.html#upgrades
1063 /* static */
1064 void CustomElementRegistry::Upgrade(Element* aElement,
1065 CustomElementDefinition* aDefinition,
1066 ErrorResult& aRv) {
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) {
1073 return;
1076 // Step 3.
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,
1090 namespaceURI);
1092 LifecycleCallbackArgs args = {
1093 nsDependentAtomString(attrName), VoidString(), attrValue,
1094 (namespaceURI.IsEmpty() ? VoidString() : namespaceURI)};
1095 nsContentUtils::EnqueueLifecycleCallback(
1096 Document::eAttributeChanged, aElement, &args, nullptr, aDefinition);
1101 // Step 4.
1102 if (aElement->IsInComposedDoc()) {
1103 nsContentUtils::EnqueueLifecycleCallback(Document::eConnected, aElement,
1104 nullptr, nullptr, aDefinition);
1107 // Step 5.
1108 AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement);
1110 // Step 6 and step 7.
1111 DoUpgrade(aElement, MOZ_KnownLive(aDefinition->mConstructor), aRv);
1112 if (aRv.Failed()) {
1113 data->mState = CustomElementData::State::eFailed;
1114 // Empty element's custom element reaction queue.
1115 data->mReactionQueue.Clear();
1116 return;
1119 // Step 8.
1120 data->mState = CustomElementData::State::eCustom;
1121 aElement->SetDefined(true);
1123 // Step 9.
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())) {
1132 return nullptr;
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())) {
1140 return nullptr;
1142 LifecycleGetCustomInterfaceCallback* func =
1143 definition->mCallbacks->mGetCustomInterfaceCallback.Value();
1145 // Initialize a AutoJSAPI to enter the compartment of the callback.
1146 AutoJSAPI jsapi;
1147 JS::RootedObject funcGlobal(RootingCx(), func->CallbackGlobalOrNull());
1148 if (!funcGlobal || !jsapi.Init(funcGlobal)) {
1149 return nullptr;
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)) {
1158 return nullptr;
1161 JS::RootedObject customInterface(cx);
1162 func->Call(aElement, jsiid, &customInterface);
1163 if (!customInterface) {
1164 return nullptr;
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))) {
1172 return nullptr;
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.
1216 MOZ_ASSERT(
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(
1230 Element* aElement,
1231 UniquePtr<CustomElementCallback> aCustomElementCallback) {
1232 Enqueue(aElement,
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);
1252 return;
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) {
1263 return;
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);
1280 MOZ_ASSERT(
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;
1289 if (aGlobal) {
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
1297 // nullptr.
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.
1304 continue;
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
1310 // this function.
1311 auto reaction(std::move(reactions.ElementAt(j)));
1312 if (reaction) {
1313 if (!aGlobal && reaction->IsUpgradeReaction()) {
1314 nsIGlobalObject* global = element->GetOwnerGlobal();
1315 MOZ_ASSERT(!aes);
1316 aes.emplace(global, "custom elements reaction invocation");
1318 ErrorResult rv;
1319 reaction->Invoke(MOZ_KnownLive(element), rv);
1320 if (aes) {
1321 JSContext* cx = aes->cx();
1322 if (rv.MaybeSetPendingException(cx)) {
1323 aes->ReportException();
1325 MOZ_ASSERT(!JS_IsExceptionPending(cx));
1326 if (!aGlobal && reaction->IsUpgradeReaction()) {
1327 aes.reset();
1330 MOZ_ASSERT(!rv.Failed());
1333 reactions.Clear();
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)
1393 : mType(aType),
1394 mLocalName(aLocalName),
1395 mNamespaceID(aNamespaceID),
1396 mConstructor(aConstructor),
1397 mObservedAttributes(std::move(aObservedAttributes)),
1398 mCallbacks(std::move(aCallbacks)) {}
1400 } // namespace dom
1401 } // namespace mozilla