/work/obj-fuzz/dist/include/mozilla/dom/CustomElementRegistry.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #ifndef mozilla_dom_CustomElementRegistry_h |
8 | | #define mozilla_dom_CustomElementRegistry_h |
9 | | |
10 | | #include "js/GCHashTable.h" |
11 | | #include "js/TypeDecls.h" |
12 | | #include "mozilla/Attributes.h" |
13 | | #include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable |
14 | | #include "mozilla/ErrorResult.h" |
15 | | #include "mozilla/dom/BindingDeclarations.h" |
16 | | #include "mozilla/dom/CustomElementRegistryBinding.h" |
17 | | #include "mozilla/dom/Element.h" |
18 | | #include "mozilla/dom/FunctionBinding.h" |
19 | | #include "mozilla/dom/WebComponentsBinding.h" |
20 | | #include "nsCycleCollectionParticipant.h" |
21 | | #include "nsGenericHTMLElement.h" |
22 | | #include "nsWrapperCache.h" |
23 | | #include "nsContentUtils.h" |
24 | | |
25 | | class nsDocument; |
26 | | |
27 | | namespace mozilla { |
28 | | namespace dom { |
29 | | |
30 | | struct CustomElementData; |
31 | | struct ElementDefinitionOptions; |
32 | | class CallbackFunction; |
33 | | class CustomElementReaction; |
34 | | class DocGroup; |
35 | | class Function; |
36 | | class Promise; |
37 | | |
38 | | struct LifecycleCallbackArgs |
39 | | { |
40 | | nsString name; |
41 | | nsString oldValue; |
42 | | nsString newValue; |
43 | | nsString namespaceURI; |
44 | | |
45 | | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; |
46 | | }; |
47 | | |
48 | | struct LifecycleAdoptedCallbackArgs |
49 | | { |
50 | | nsCOMPtr<nsIDocument> mOldDocument; |
51 | | nsCOMPtr<nsIDocument> mNewDocument; |
52 | | }; |
53 | | |
54 | | class CustomElementCallback |
55 | | { |
56 | | public: |
57 | | CustomElementCallback(Element* aThisObject, |
58 | | nsIDocument::ElementCallbackType aCallbackType, |
59 | | CallbackFunction* aCallback); |
60 | | void Traverse(nsCycleCollectionTraversalCallback& aCb) const; |
61 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
62 | | void Call(); |
63 | | void SetArgs(LifecycleCallbackArgs& aArgs) |
64 | 0 | { |
65 | 0 | MOZ_ASSERT(mType == nsIDocument::eAttributeChanged, |
66 | 0 | "Arguments are only used by attribute changed callback."); |
67 | 0 | mArgs = aArgs; |
68 | 0 | } |
69 | | |
70 | | void SetAdoptedCallbackArgs(LifecycleAdoptedCallbackArgs& aAdoptedCallbackArgs) |
71 | 0 | { |
72 | 0 | MOZ_ASSERT(mType == nsIDocument::eAdopted, |
73 | 0 | "Arguments are only used by adopted callback."); |
74 | 0 | mAdoptedCallbackArgs = aAdoptedCallbackArgs; |
75 | 0 | } |
76 | | |
77 | | private: |
78 | | // The this value to use for invocation of the callback. |
79 | | RefPtr<Element> mThisObject; |
80 | | RefPtr<CallbackFunction> mCallback; |
81 | | // The type of callback (eCreated, eAttached, etc.) |
82 | | nsIDocument::ElementCallbackType mType; |
83 | | // Arguments to be passed to the callback, |
84 | | // used by the attribute changed callback. |
85 | | LifecycleCallbackArgs mArgs; |
86 | | LifecycleAdoptedCallbackArgs mAdoptedCallbackArgs; |
87 | | }; |
88 | | |
89 | | class CustomElementConstructor final : public CallbackFunction |
90 | | { |
91 | | public: |
92 | | explicit CustomElementConstructor(CallbackFunction* aOther) |
93 | | : CallbackFunction(aOther) |
94 | 0 | { |
95 | 0 | MOZ_ASSERT(JS::IsConstructor(mCallback)); |
96 | 0 | } |
97 | | |
98 | | already_AddRefed<Element> Construct(const char* aExecutionReason, ErrorResult& aRv); |
99 | | }; |
100 | | |
101 | | // Each custom element has an associated callback queue and an element is |
102 | | // being created flag. |
103 | | struct CustomElementData |
104 | | { |
105 | | NS_INLINE_DECL_REFCOUNTING(CustomElementData) |
106 | | |
107 | | // https://dom.spec.whatwg.org/#concept-element-custom-element-state |
108 | | // CustomElementData is only created on the element which is a custom element |
109 | | // or an upgrade candidate, so the state of an element without |
110 | | // CustomElementData is "uncustomized". |
111 | | enum class State { |
112 | | eUndefined, |
113 | | eFailed, |
114 | | eCustom |
115 | | }; |
116 | | |
117 | | explicit CustomElementData(nsAtom* aType); |
118 | | CustomElementData(nsAtom* aType, State aState); |
119 | | |
120 | | // Custom element state as described in the custom element spec. |
121 | | State mState; |
122 | | // custom element reaction queue as described in the custom element spec. |
123 | | // There is 1 reaction in reaction queue, when 1) it becomes disconnected, |
124 | | // 2) it’s adopted into a new document, 3) its attributes are changed, |
125 | | // appended, removed, or replaced. |
126 | | // There are 3 reactions in reaction queue when doing upgrade operation, |
127 | | // e.g., create an element, insert a node. |
128 | | AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue; |
129 | | |
130 | | void SetCustomElementDefinition(CustomElementDefinition* aDefinition); |
131 | | CustomElementDefinition* GetCustomElementDefinition(); |
132 | | nsAtom* GetCustomElementType() const |
133 | | { |
134 | | return mType; |
135 | | } |
136 | | |
137 | | void Traverse(nsCycleCollectionTraversalCallback& aCb) const; |
138 | | void Unlink(); |
139 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
140 | | |
141 | | nsAtom* GetIs(const Element* aElement) const |
142 | | { |
143 | | // If mType isn't the same as name atom, this is a customized built-in |
144 | | // element, which has 'is' value set. |
145 | | return aElement->NodeInfo()->NameAtom() == mType ? nullptr : mType.get(); |
146 | | } |
147 | | private: |
148 | 0 | virtual ~CustomElementData() {} |
149 | | |
150 | | // Custom element type, for <button is="x-button"> or <x-button> |
151 | | // this would be x-button. |
152 | | RefPtr<nsAtom> mType; |
153 | | RefPtr<CustomElementDefinition> mCustomElementDefinition; |
154 | | }; |
155 | | |
156 | 0 | #define ALREADY_CONSTRUCTED_MARKER nullptr |
157 | | |
158 | | // The required information for a custom element as defined in: |
159 | | // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition |
160 | | struct CustomElementDefinition |
161 | | { |
162 | | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition) |
163 | | NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition) |
164 | | |
165 | | CustomElementDefinition(nsAtom* aType, |
166 | | nsAtom* aLocalName, |
167 | | int32_t aNamespaceID, |
168 | | Function* aConstructor, |
169 | | nsTArray<RefPtr<nsAtom>>&& aObservedAttributes, |
170 | | UniquePtr<LifecycleCallbacks>&& aCallbacks); |
171 | | |
172 | | // The type (name) for this custom element, for <button is="x-foo"> or <x-foo> |
173 | | // this would be x-foo. |
174 | | RefPtr<nsAtom> mType; |
175 | | |
176 | | // The localname to (e.g. <button is=type> -- this would be button). |
177 | | RefPtr<nsAtom> mLocalName; |
178 | | |
179 | | // The namespace for this custom element |
180 | | int32_t mNamespaceID; |
181 | | |
182 | | // The custom element constructor. |
183 | | RefPtr<CustomElementConstructor> mConstructor; |
184 | | |
185 | | // The list of attributes that this custom element observes. |
186 | | nsTArray<RefPtr<nsAtom>> mObservedAttributes; |
187 | | |
188 | | // The lifecycle callbacks to call for this custom element. |
189 | | UniquePtr<LifecycleCallbacks> mCallbacks; |
190 | | |
191 | | // A construction stack. Use nullptr to represent an "already constructed marker". |
192 | | nsTArray<RefPtr<Element>> mConstructionStack; |
193 | | |
194 | | // See step 6.1.10 of https://dom.spec.whatwg.org/#concept-create-element |
195 | | // which set up the prefix after a custom element is created. However, In Gecko, |
196 | | // the prefix isn't allowed to be changed in NodeInfo, so we store the prefix |
197 | | // information here and propagate to where NodeInfo is assigned to a custom |
198 | | // element instead. |
199 | | nsTArray<RefPtr<nsAtom>> mPrefixStack; |
200 | | |
201 | | // This basically is used for distinguishing the custom element constructor |
202 | | // is invoked from document.createElement or directly from JS, i.e. |
203 | | // `new CustomElementConstructor()`. |
204 | | uint32_t mConstructionDepth = 0; |
205 | | |
206 | | bool IsCustomBuiltIn() |
207 | | { |
208 | | return mType != mLocalName; |
209 | | } |
210 | | |
211 | | bool IsInObservedAttributeList(nsAtom* aName) |
212 | 0 | { |
213 | 0 | if (mObservedAttributes.IsEmpty()) { |
214 | 0 | return false; |
215 | 0 | } |
216 | 0 | |
217 | 0 | return mObservedAttributes.Contains(aName); |
218 | 0 | } |
219 | | |
220 | | private: |
221 | 0 | ~CustomElementDefinition() {} |
222 | | }; |
223 | | |
224 | | class CustomElementReaction |
225 | | { |
226 | | public: |
227 | 0 | virtual ~CustomElementReaction() = default; |
228 | | virtual void Invoke(Element* aElement, ErrorResult& aRv) = 0; |
229 | | virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const = 0; |
230 | | virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const = 0; |
231 | | |
232 | | bool IsUpgradeReaction() |
233 | 0 | { |
234 | 0 | return mIsUpgradeReaction; |
235 | 0 | } |
236 | | |
237 | | protected: |
238 | | bool mIsUpgradeReaction = false; |
239 | | }; |
240 | | |
241 | | // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-reactions-stack |
242 | | class CustomElementReactionsStack |
243 | | { |
244 | | public: |
245 | | NS_INLINE_DECL_REFCOUNTING(CustomElementReactionsStack) |
246 | | |
247 | | CustomElementReactionsStack() |
248 | | : mIsBackupQueueProcessing(false) |
249 | | , mRecursionDepth(0) |
250 | | , mIsElementQueuePushedForCurrentRecursionDepth(false) |
251 | 0 | { |
252 | 0 | } |
253 | | |
254 | | // Hold a strong reference of Element so that it does not get cycle collected |
255 | | // before the reactions in its reaction queue are invoked. |
256 | | // The element reaction queues are stored in CustomElementData. |
257 | | // We need to lookup ElementReactionQueueMap again to get relevant reaction queue. |
258 | | // The choice of 3 for the auto size here is based on running Custom Elements |
259 | | // wpt tests. |
260 | | typedef AutoTArray<RefPtr<Element>, 3> ElementQueue; |
261 | | |
262 | | /** |
263 | | * Enqueue a custom element upgrade reaction |
264 | | * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-upgrade-reaction |
265 | | */ |
266 | | void EnqueueUpgradeReaction(Element* aElement, |
267 | | CustomElementDefinition* aDefinition); |
268 | | |
269 | | /** |
270 | | * Enqueue a custom element callback reaction |
271 | | * https://html.spec.whatwg.org/multipage/scripting.html#enqueue-a-custom-element-callback-reaction |
272 | | */ |
273 | | void EnqueueCallbackReaction(Element* aElement, |
274 | | UniquePtr<CustomElementCallback> aCustomElementCallback); |
275 | | |
276 | | /** |
277 | | * [CEReactions] Before executing the algorithm's steps. |
278 | | * Increase the current recursion depth, and the element queue is pushed |
279 | | * lazily when we really enqueue reactions. |
280 | | * |
281 | | * @return true if the element queue is pushed for "previous" recursion depth. |
282 | | */ |
283 | | bool EnterCEReactions() |
284 | 0 | { |
285 | 0 | bool temp = mIsElementQueuePushedForCurrentRecursionDepth; |
286 | 0 | mRecursionDepth++; |
287 | 0 | // The is-element-queue-pushed flag is initially false when entering a new |
288 | 0 | // recursion level. The original value will be cached in AutoCEReaction |
289 | 0 | // and restored after leaving this recursion level. |
290 | 0 | mIsElementQueuePushedForCurrentRecursionDepth = false; |
291 | 0 | return temp; |
292 | 0 | } |
293 | | |
294 | | /** |
295 | | * [CEReactions] After executing the algorithm's steps. |
296 | | * Pop and invoke the element queue if it is created and pushed for current |
297 | | * recursion depth, then decrease the current recursion depth. |
298 | | * |
299 | | * @param aCx JSContext used for handling exception thrown by algorithm's |
300 | | * steps, this could be a nullptr. |
301 | | * aWasElementQueuePushed used for restoring status after leaving |
302 | | * current recursion. |
303 | | */ |
304 | | void LeaveCEReactions(JSContext* aCx, bool aWasElementQueuePushed) |
305 | 0 | { |
306 | 0 | MOZ_ASSERT(mRecursionDepth); |
307 | 0 |
|
308 | 0 | if (mIsElementQueuePushedForCurrentRecursionDepth) { |
309 | 0 | Maybe<JS::AutoSaveExceptionState> ases; |
310 | 0 | if (aCx) { |
311 | 0 | ases.emplace(aCx); |
312 | 0 | } |
313 | 0 | PopAndInvokeElementQueue(); |
314 | 0 | } |
315 | 0 | mRecursionDepth--; |
316 | 0 | // Restore the is-element-queue-pushed flag cached in AutoCEReaction when |
317 | 0 | // leaving the recursion level. |
318 | 0 | mIsElementQueuePushedForCurrentRecursionDepth = aWasElementQueuePushed; |
319 | 0 |
|
320 | 0 | MOZ_ASSERT_IF(!mRecursionDepth, mReactionsStack.IsEmpty()); |
321 | 0 | } |
322 | | |
323 | | private: |
324 | 0 | ~CustomElementReactionsStack() {}; |
325 | | |
326 | | /** |
327 | | * Push a new element queue onto the custom element reactions stack. |
328 | | */ |
329 | | void CreateAndPushElementQueue(); |
330 | | |
331 | | /** |
332 | | * Pop the element queue from the custom element reactions stack, and invoke |
333 | | * custom element reactions in that queue. |
334 | | */ |
335 | | void PopAndInvokeElementQueue(); |
336 | | |
337 | | // The choice of 8 for the auto size here is based on gut feeling. |
338 | | AutoTArray<UniquePtr<ElementQueue>, 8> mReactionsStack; |
339 | | ElementQueue mBackupQueue; |
340 | | // https://html.spec.whatwg.org/#enqueue-an-element-on-the-appropriate-element-queue |
341 | | bool mIsBackupQueueProcessing; |
342 | | |
343 | | void InvokeBackupQueue(); |
344 | | |
345 | | /** |
346 | | * Invoke custom element reactions |
347 | | * https://html.spec.whatwg.org/multipage/scripting.html#invoke-custom-element-reactions |
348 | | */ |
349 | | void InvokeReactions(ElementQueue* aElementQueue, nsIGlobalObject* aGlobal); |
350 | | |
351 | | void Enqueue(Element* aElement, CustomElementReaction* aReaction); |
352 | | |
353 | | // Current [CEReactions] recursion depth. |
354 | | uint32_t mRecursionDepth; |
355 | | // True if the element queue is pushed into reaction stack for current |
356 | | // recursion depth. This will be cached in AutoCEReaction when entering a new |
357 | | // CEReaction recursion and restored after leaving the recursion. |
358 | | bool mIsElementQueuePushedForCurrentRecursionDepth; |
359 | | |
360 | | private: |
361 | | class BackupQueueMicroTask final : public mozilla::MicroTaskRunnable { |
362 | | public: |
363 | | explicit BackupQueueMicroTask( |
364 | | CustomElementReactionsStack* aReactionStack) |
365 | | : MicroTaskRunnable() |
366 | | , mReactionStack(aReactionStack) |
367 | 0 | { |
368 | 0 | MOZ_ASSERT(!mReactionStack->mIsBackupQueueProcessing, |
369 | 0 | "mIsBackupQueueProcessing should be initially false"); |
370 | 0 | mReactionStack->mIsBackupQueueProcessing = true; |
371 | 0 | } |
372 | | |
373 | | virtual void Run(AutoSlowOperation& aAso) override |
374 | 0 | { |
375 | 0 | mReactionStack->InvokeBackupQueue(); |
376 | 0 | mReactionStack->mIsBackupQueueProcessing = false; |
377 | 0 | } |
378 | | |
379 | | private: |
380 | | RefPtr<CustomElementReactionsStack> mReactionStack; |
381 | | }; |
382 | | }; |
383 | | |
384 | | class CustomElementRegistry final : public nsISupports, |
385 | | public nsWrapperCache |
386 | | { |
387 | | // Allow nsDocument to access mCustomDefinitions and mCandidatesMap. |
388 | | friend class ::nsDocument; |
389 | | |
390 | | public: |
391 | | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
392 | | NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CustomElementRegistry) |
393 | | |
394 | | public: |
395 | | static bool IsCustomElementEnabled(JSContext* aCx, JSObject* aObject); |
396 | | static bool IsCustomElementEnabled(nsIDocument* aDoc); |
397 | | |
398 | | explicit CustomElementRegistry(nsPIDOMWindowInner* aWindow); |
399 | | |
400 | | private: |
401 | | class RunCustomElementCreationCallback : public mozilla::Runnable |
402 | | { |
403 | | public: |
404 | | NS_DECL_NSIRUNNABLE |
405 | | explicit RunCustomElementCreationCallback(CustomElementRegistry* aRegistry, |
406 | | nsAtom* aAtom, |
407 | | CustomElementCreationCallback* aCallback) |
408 | | : mozilla::Runnable("CustomElementRegistry::RunCustomElementCreationCallback") |
409 | | , mRegistry(aRegistry) |
410 | | , mAtom(aAtom) |
411 | | , mCallback(aCallback) |
412 | 0 | { |
413 | 0 | } |
414 | | private: |
415 | | RefPtr<CustomElementRegistry> mRegistry; |
416 | | RefPtr<nsAtom> mAtom; |
417 | | RefPtr<CustomElementCreationCallback> mCallback; |
418 | | }; |
419 | | |
420 | | public: |
421 | | /** |
422 | | * Returns whether there's a definition that is likely to match this type |
423 | | * atom. This is not exact, so should only be used for optimization, but it's |
424 | | * good enough to prove that the chrome code doesn't need an XBL binding. |
425 | | */ |
426 | | bool IsLikelyToBeCustomElement(nsAtom* aTypeAtom) const |
427 | 0 | { |
428 | 0 | return mCustomDefinitions.GetWeak(aTypeAtom) || |
429 | 0 | mElementCreationCallbacks.GetWeak(aTypeAtom); |
430 | 0 | } |
431 | | |
432 | | /** |
433 | | * Looking up a custom element definition. |
434 | | * https://html.spec.whatwg.org/#look-up-a-custom-element-definition |
435 | | */ |
436 | | CustomElementDefinition* LookupCustomElementDefinition( |
437 | | nsAtom* aNameAtom, int32_t aNameSpaceID, nsAtom* aTypeAtom); |
438 | | |
439 | | CustomElementDefinition* LookupCustomElementDefinition( |
440 | | JSContext* aCx, JSObject *aConstructor) const; |
441 | | |
442 | | static void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, |
443 | | Element* aCustomElement, |
444 | | LifecycleCallbackArgs* aArgs, |
445 | | LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs, |
446 | | CustomElementDefinition* aDefinition); |
447 | | |
448 | | /** |
449 | | * Upgrade an element. |
450 | | * https://html.spec.whatwg.org/multipage/scripting.html#upgrades |
451 | | */ |
452 | | static void Upgrade(Element* aElement, CustomElementDefinition* aDefinition, ErrorResult& aRv); |
453 | | |
454 | | /** |
455 | | * To allow native code to call methods of chrome-implemented custom elements, |
456 | | * a helper method may be defined in the custom element called |
457 | | * 'getCustomInterfaceCallback'. This method takes an IID and returns an |
458 | | * object which implements an XPCOM interface. |
459 | | * |
460 | | * This returns null if aElement is not from a chrome document. |
461 | | */ |
462 | | static already_AddRefed<nsISupports> CallGetCustomInterface( |
463 | | Element* aElement, const nsIID& aIID); |
464 | | |
465 | | /** |
466 | | * Registers an unresolved custom element that is a candidate for |
467 | | * upgrade. |aTypeName| is the name of the custom element type, if it is not |
468 | | * provided, then element name is used. |aTypeName| should be provided |
469 | | * when registering a custom element that extends an existing |
470 | | * element. e.g. <button is="x-button">. |
471 | | */ |
472 | | void RegisterUnresolvedElement(Element* aElement, |
473 | | nsAtom* aTypeName = nullptr); |
474 | | |
475 | | /** |
476 | | * Unregister an unresolved custom element that is a candidate for |
477 | | * upgrade when a custom element is removed from tree. |
478 | | */ |
479 | | void UnregisterUnresolvedElement(Element* aElement, |
480 | | nsAtom* aTypeName = nullptr); |
481 | | |
482 | | /** |
483 | | * Register an element to be upgraded when the custom element creation |
484 | | * callback is executed. |
485 | | * |
486 | | * To be used when LookupCustomElementDefinition() didn't return a definition, |
487 | | * but with the callback scheduled to be run. |
488 | | */ |
489 | | inline void RegisterCallbackUpgradeElement(Element* aElement, |
490 | | nsAtom* aTypeName = nullptr) |
491 | | { |
492 | | if (mElementCreationCallbacksUpgradeCandidatesMap.IsEmpty()) { |
493 | | return; |
494 | | } |
495 | | |
496 | | RefPtr<nsAtom> typeName = aTypeName; |
497 | | if (!typeName) { |
498 | | typeName = aElement->NodeInfo()->NameAtom(); |
499 | | } |
500 | | |
501 | | nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>* elements = |
502 | | mElementCreationCallbacksUpgradeCandidatesMap.Get(typeName); |
503 | | |
504 | | // If there isn't a table, there won't be a definition added by the callback. |
505 | | if (!elements) { |
506 | | return; |
507 | | } |
508 | | |
509 | | nsWeakPtr elem = do_GetWeakReference(aElement); |
510 | | elements->PutEntry(elem); |
511 | | } |
512 | | |
513 | | private: |
514 | | ~CustomElementRegistry(); |
515 | | |
516 | | static UniquePtr<CustomElementCallback> CreateCustomElementCallback( |
517 | | nsIDocument::ElementCallbackType aType, Element* aCustomElement, |
518 | | LifecycleCallbackArgs* aArgs, |
519 | | LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs, |
520 | | CustomElementDefinition* aDefinition); |
521 | | |
522 | | void UpgradeCandidates(nsAtom* aKey, |
523 | | CustomElementDefinition* aDefinition, |
524 | | ErrorResult& aRv); |
525 | | |
526 | | typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, CustomElementDefinition> |
527 | | DefinitionMap; |
528 | | typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, CustomElementCreationCallback> |
529 | | ElementCreationCallbackMap; |
530 | | typedef nsClassHashtable<nsRefPtrHashKey<nsAtom>, |
531 | | nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>> |
532 | | CandidateMap; |
533 | | typedef JS::GCHashMap<JS::Heap<JSObject*>, |
534 | | RefPtr<nsAtom>, |
535 | | js::MovableCellHasher<JS::Heap<JSObject*>>, |
536 | | js::SystemAllocPolicy> ConstructorMap; |
537 | | |
538 | | // Hashtable for custom element definitions in web components. |
539 | | // Custom prototypes are stored in the compartment where definition was |
540 | | // defined. |
541 | | DefinitionMap mCustomDefinitions; |
542 | | |
543 | | // Hashtable for chrome-only callbacks that is called *before* we return |
544 | | // a CustomElementDefinition, when the typeAtom matches. |
545 | | // The callbacks are registered with the setElementCreationCallback method. |
546 | | ElementCreationCallbackMap mElementCreationCallbacks; |
547 | | |
548 | | // Hashtable for looking up definitions by using constructor as key. |
549 | | // Custom elements' name are stored here and we need to lookup |
550 | | // mCustomDefinitions again to get definitions. |
551 | | ConstructorMap mConstructors; |
552 | | |
553 | | typedef nsRefPtrHashtable<nsRefPtrHashKey<nsAtom>, Promise> |
554 | | WhenDefinedPromiseMap; |
555 | | WhenDefinedPromiseMap mWhenDefinedPromiseMap; |
556 | | |
557 | | // The "upgrade candidates map" from the web components spec. Maps from a |
558 | | // namespace id and local name to a list of elements to upgrade if that |
559 | | // element is registered as a custom element. |
560 | | CandidateMap mCandidatesMap; |
561 | | |
562 | | // If an element creation callback is found, the nsTHashtable for the |
563 | | // type is created here, and elements will later be upgraded. |
564 | | CandidateMap mElementCreationCallbacksUpgradeCandidatesMap; |
565 | | |
566 | | nsCOMPtr<nsPIDOMWindowInner> mWindow; |
567 | | |
568 | | // It is used to prevent reentrant invocations of element definition. |
569 | | bool mIsCustomDefinitionRunning; |
570 | | |
571 | | private: |
572 | | class MOZ_RAII AutoSetRunningFlag final { |
573 | | public: |
574 | | explicit AutoSetRunningFlag(CustomElementRegistry* aRegistry) |
575 | | : mRegistry(aRegistry) |
576 | 0 | { |
577 | 0 | MOZ_ASSERT(!mRegistry->mIsCustomDefinitionRunning, |
578 | 0 | "IsCustomDefinitionRunning flag should be initially false"); |
579 | 0 | mRegistry->mIsCustomDefinitionRunning = true; |
580 | 0 | } |
581 | | |
582 | 0 | ~AutoSetRunningFlag() { |
583 | 0 | mRegistry->mIsCustomDefinitionRunning = false; |
584 | 0 | } |
585 | | |
586 | | private: |
587 | | CustomElementRegistry* mRegistry; |
588 | | }; |
589 | | |
590 | | int32_t InferNamespace(JSContext* aCx, JS::Handle<JSObject*> constructor); |
591 | | |
592 | | public: |
593 | | nsISupports* GetParentObject() const; |
594 | | |
595 | | DocGroup* GetDocGroup() const; |
596 | | |
597 | | virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; |
598 | | |
599 | | void Define(JSContext* aCx, const nsAString& aName, |
600 | | Function& aFunctionConstructor, |
601 | | const ElementDefinitionOptions& aOptions, ErrorResult& aRv); |
602 | | |
603 | | void Get(JSContext* cx, const nsAString& name, |
604 | | JS::MutableHandle<JS::Value> aRetVal); |
605 | | |
606 | | already_AddRefed<Promise> WhenDefined(const nsAString& aName, ErrorResult& aRv); |
607 | | |
608 | | // Chrome-only method that give JS an opportunity to only load the custom |
609 | | // element definition script when needed. |
610 | | void SetElementCreationCallback(const nsAString& aName, CustomElementCreationCallback& aCallback, ErrorResult& aRv); |
611 | | |
612 | | void Upgrade(nsINode& aRoot); |
613 | | }; |
614 | | |
615 | | class MOZ_RAII AutoCEReaction final { |
616 | | public: |
617 | | // JSContext is allowed to be a nullptr if we are guaranteeing that we're |
618 | | // not doing something that might throw but not finish reporting a JS |
619 | | // exception during the lifetime of the AutoCEReaction. |
620 | | AutoCEReaction(CustomElementReactionsStack* aReactionsStack, JSContext* aCx) |
621 | | : mReactionsStack(aReactionsStack) |
622 | | , mCx(aCx) |
623 | 0 | { |
624 | 0 | mIsElementQueuePushedForPreviousRecursionDepth = |
625 | 0 | mReactionsStack->EnterCEReactions(); |
626 | 0 | } |
627 | | |
628 | | ~AutoCEReaction() |
629 | 0 | { |
630 | 0 | mReactionsStack->LeaveCEReactions( |
631 | 0 | mCx, mIsElementQueuePushedForPreviousRecursionDepth); |
632 | 0 | } |
633 | | |
634 | | private: |
635 | | RefPtr<CustomElementReactionsStack> mReactionsStack; |
636 | | JSContext* mCx; |
637 | | bool mIsElementQueuePushedForPreviousRecursionDepth; |
638 | | }; |
639 | | |
640 | | } // namespace dom |
641 | | } // namespace mozilla |
642 | | |
643 | | |
644 | | #endif // mozilla_dom_CustomElementRegistry_h |