/src/mozilla-central/dom/base/CustomElementRegistry.cpp
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 | | #include "mozilla/dom/CustomElementRegistry.h" |
8 | | |
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/XULElementBinding.h" |
14 | | #include "mozilla/dom/Promise.h" |
15 | | #include "mozilla/dom/WebComponentsBinding.h" |
16 | | #include "mozilla/dom/DocGroup.h" |
17 | | #include "mozilla/dom/CustomEvent.h" |
18 | | #include "mozilla/dom/ShadowRoot.h" |
19 | | #include "nsHTMLTags.h" |
20 | | #include "jsapi.h" |
21 | | #include "xpcprivate.h" |
22 | | #include "nsGlobalWindow.h" |
23 | | |
24 | | namespace mozilla { |
25 | | namespace dom { |
26 | | |
27 | | //----------------------------------------------------- |
28 | | // CustomElementUpgradeReaction |
29 | | |
30 | | class CustomElementUpgradeReaction final : public CustomElementReaction |
31 | | { |
32 | | public: |
33 | | explicit CustomElementUpgradeReaction(CustomElementDefinition* aDefinition) |
34 | | : mDefinition(aDefinition) |
35 | 0 | { |
36 | 0 | mIsUpgradeReaction = true; |
37 | 0 | } |
38 | | |
39 | | virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override |
40 | 0 | { |
41 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mDefinition"); |
42 | 0 | aCb.NoteNativeChild(mDefinition, |
43 | 0 | NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition)); |
44 | 0 | } |
45 | | |
46 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override |
47 | 0 | { |
48 | 0 | // We don't really own mDefinition. |
49 | 0 | return aMallocSizeOf(this); |
50 | 0 | } |
51 | | |
52 | | private: |
53 | | virtual void Invoke(Element* aElement, ErrorResult& aRv) override |
54 | 0 | { |
55 | 0 | CustomElementRegistry::Upgrade(aElement, mDefinition, aRv); |
56 | 0 | } |
57 | | |
58 | | RefPtr<CustomElementDefinition> mDefinition; |
59 | | }; |
60 | | |
61 | | //----------------------------------------------------- |
62 | | // CustomElementCallbackReaction |
63 | | |
64 | | class CustomElementCallbackReaction final : public CustomElementReaction |
65 | | { |
66 | | public: |
67 | | explicit CustomElementCallbackReaction(UniquePtr<CustomElementCallback> aCustomElementCallback) |
68 | | : mCustomElementCallback(std::move(aCustomElementCallback)) |
69 | 0 | { |
70 | 0 | } |
71 | | |
72 | | virtual void Traverse(nsCycleCollectionTraversalCallback& aCb) const override |
73 | 0 | { |
74 | 0 | mCustomElementCallback->Traverse(aCb); |
75 | 0 | } |
76 | | |
77 | | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override |
78 | 0 | { |
79 | 0 | size_t n = aMallocSizeOf(this); |
80 | 0 |
|
81 | 0 | n += mCustomElementCallback->SizeOfIncludingThis(aMallocSizeOf); |
82 | 0 |
|
83 | 0 | return n; |
84 | 0 | } |
85 | | |
86 | | private: |
87 | | virtual void Invoke(Element* aElement, ErrorResult& aRv) override |
88 | 0 | { |
89 | 0 | mCustomElementCallback->Call(); |
90 | 0 | } |
91 | | |
92 | | UniquePtr<CustomElementCallback> mCustomElementCallback; |
93 | | }; |
94 | | |
95 | | //----------------------------------------------------- |
96 | | // CustomElementCallback |
97 | | |
98 | | size_t |
99 | | LifecycleCallbackArgs::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
100 | 0 | { |
101 | 0 | size_t n = name.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
102 | 0 | n += oldValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
103 | 0 | n += newValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
104 | 0 | n += namespaceURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
105 | 0 | return n; |
106 | 0 | } |
107 | | |
108 | | void |
109 | | CustomElementCallback::Call() |
110 | 0 | { |
111 | 0 | switch (mType) { |
112 | 0 | case nsIDocument::eConnected: |
113 | 0 | static_cast<LifecycleConnectedCallback *>(mCallback.get())->Call(mThisObject); |
114 | 0 | break; |
115 | 0 | case nsIDocument::eDisconnected: |
116 | 0 | static_cast<LifecycleDisconnectedCallback *>(mCallback.get())->Call(mThisObject); |
117 | 0 | break; |
118 | 0 | case nsIDocument::eAdopted: |
119 | 0 | static_cast<LifecycleAdoptedCallback *>(mCallback.get())->Call(mThisObject, |
120 | 0 | mAdoptedCallbackArgs.mOldDocument, mAdoptedCallbackArgs.mNewDocument); |
121 | 0 | break; |
122 | 0 | case nsIDocument::eAttributeChanged: |
123 | 0 | static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject, |
124 | 0 | mArgs.name, mArgs.oldValue, mArgs.newValue, mArgs.namespaceURI); |
125 | 0 | break; |
126 | 0 | case nsIDocument::eGetCustomInterface: |
127 | 0 | MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback"); |
128 | 0 | break; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | void |
133 | | CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const |
134 | 0 | { |
135 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject"); |
136 | 0 | aCb.NoteXPCOMChild(mThisObject); |
137 | 0 |
|
138 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback"); |
139 | 0 | aCb.NoteXPCOMChild(mCallback); |
140 | 0 | } |
141 | | |
142 | | size_t |
143 | | CustomElementCallback::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
144 | 0 | { |
145 | 0 | size_t n = aMallocSizeOf(this); |
146 | 0 |
|
147 | 0 | // We don't uniquely own mThisObject. |
148 | 0 |
|
149 | 0 | // We own mCallback but it doesn't have any special memory reporting we can do |
150 | 0 | // for it other than report its own size. |
151 | 0 | n += aMallocSizeOf(mCallback); |
152 | 0 |
|
153 | 0 | n += mArgs.SizeOfExcludingThis(aMallocSizeOf); |
154 | 0 |
|
155 | 0 | // mAdoptedCallbackArgs doesn't really uniquely own its members. |
156 | 0 |
|
157 | 0 | return n; |
158 | 0 | } |
159 | | |
160 | | CustomElementCallback::CustomElementCallback(Element* aThisObject, |
161 | | nsIDocument::ElementCallbackType aCallbackType, |
162 | | mozilla::dom::CallbackFunction* aCallback) |
163 | | : mThisObject(aThisObject), |
164 | | mCallback(aCallback), |
165 | | mType(aCallbackType) |
166 | 0 | { |
167 | 0 | } |
168 | | //----------------------------------------------------- |
169 | | // CustomElementConstructor |
170 | | |
171 | | already_AddRefed<Element> |
172 | | CustomElementConstructor::Construct(const char* aExecutionReason, |
173 | | ErrorResult& aRv) |
174 | 0 | { |
175 | 0 | CallSetup s(this, aRv, aExecutionReason, |
176 | 0 | CallbackFunction::eRethrowExceptions); |
177 | 0 |
|
178 | 0 | JSContext* cx = s.GetContext(); |
179 | 0 | if (!cx) { |
180 | 0 | MOZ_ASSERT(aRv.Failed()); |
181 | 0 | return nullptr; |
182 | 0 | } |
183 | 0 |
|
184 | 0 | JS::Rooted<JSObject*> result(cx); |
185 | 0 | JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback)); |
186 | 0 | if (!JS::Construct(cx, constructor, JS::HandleValueArray::empty(), &result)) { |
187 | 0 | aRv.NoteJSContextException(cx); |
188 | 0 | return nullptr; |
189 | 0 | } |
190 | 0 | |
191 | 0 | RefPtr<Element> element; |
192 | 0 | if (NS_FAILED(UNWRAP_OBJECT(Element, &result, element))) { |
193 | 0 | return nullptr; |
194 | 0 | } |
195 | 0 | |
196 | 0 | return element.forget(); |
197 | 0 | } |
198 | | |
199 | | //----------------------------------------------------- |
200 | | // CustomElementData |
201 | | |
202 | | CustomElementData::CustomElementData(nsAtom* aType) |
203 | | : CustomElementData(aType, CustomElementData::State::eUndefined) |
204 | 0 | { |
205 | 0 | } |
206 | | |
207 | | CustomElementData::CustomElementData(nsAtom* aType, State aState) |
208 | | : mState(aState) |
209 | | , mType(aType) |
210 | 0 | { |
211 | 0 | } |
212 | | |
213 | | void |
214 | | CustomElementData::SetCustomElementDefinition(CustomElementDefinition* aDefinition) |
215 | 0 | { |
216 | 0 | MOZ_ASSERT(mState == State::eCustom); |
217 | 0 | MOZ_ASSERT(!mCustomElementDefinition); |
218 | 0 | MOZ_ASSERT(aDefinition->mType == mType); |
219 | 0 |
|
220 | 0 | mCustomElementDefinition = aDefinition; |
221 | 0 | } |
222 | | |
223 | | CustomElementDefinition* |
224 | | CustomElementData::GetCustomElementDefinition() |
225 | 0 | { |
226 | 0 | MOZ_ASSERT(mCustomElementDefinition ? mState == State::eCustom |
227 | 0 | : mState != State::eCustom); |
228 | 0 |
|
229 | 0 | return mCustomElementDefinition; |
230 | 0 | } |
231 | | |
232 | | void |
233 | | CustomElementData::Traverse(nsCycleCollectionTraversalCallback& aCb) const |
234 | 0 | { |
235 | 0 | for (uint32_t i = 0; i < mReactionQueue.Length(); i++) { |
236 | 0 | if (mReactionQueue[i]) { |
237 | 0 | mReactionQueue[i]->Traverse(aCb); |
238 | 0 | } |
239 | 0 | } |
240 | 0 |
|
241 | 0 | if (mCustomElementDefinition) { |
242 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCustomElementDefinition"); |
243 | 0 | aCb.NoteNativeChild(mCustomElementDefinition, |
244 | 0 | NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition)); |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | void |
249 | | CustomElementData::Unlink() |
250 | 0 | { |
251 | 0 | mReactionQueue.Clear(); |
252 | 0 | mCustomElementDefinition = nullptr; |
253 | 0 | } |
254 | | |
255 | | size_t |
256 | | CustomElementData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
257 | 0 | { |
258 | 0 | size_t n = aMallocSizeOf(this); |
259 | 0 |
|
260 | 0 | n += mReactionQueue.ShallowSizeOfExcludingThis(aMallocSizeOf); |
261 | 0 |
|
262 | 0 | for (auto& reaction : mReactionQueue) { |
263 | 0 | n += reaction->SizeOfIncludingThis(aMallocSizeOf); |
264 | 0 | } |
265 | 0 |
|
266 | 0 | return n; |
267 | 0 | } |
268 | | |
269 | | //----------------------------------------------------- |
270 | | // CustomElementRegistry |
271 | | |
272 | | namespace { |
273 | | |
274 | | class MOZ_RAII AutoConstructionStackEntry final |
275 | | { |
276 | | public: |
277 | | AutoConstructionStackEntry(nsTArray<RefPtr<Element>>& aStack, |
278 | | Element* aElement) |
279 | | : mStack(aStack) |
280 | 0 | { |
281 | 0 | MOZ_ASSERT(aElement->IsHTMLElement() || aElement->IsXULElement()); |
282 | 0 |
|
283 | 0 | mIndex = mStack.Length(); |
284 | 0 | mStack.AppendElement(aElement); |
285 | 0 | } |
286 | | |
287 | | ~AutoConstructionStackEntry() |
288 | 0 | { |
289 | 0 | MOZ_ASSERT(mIndex == mStack.Length() - 1, |
290 | 0 | "Removed element should be the last element"); |
291 | 0 | mStack.RemoveElementAt(mIndex); |
292 | 0 | } |
293 | | |
294 | | private: |
295 | | nsTArray<RefPtr<Element>>& mStack; |
296 | | uint32_t mIndex; |
297 | | }; |
298 | | |
299 | | } // namespace anonymous |
300 | | |
301 | | // Only needed for refcounted objects. |
302 | | NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry) |
303 | | |
304 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry) |
305 | 0 | tmp->mConstructors.clear(); |
306 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions) |
307 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap) |
308 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementCreationCallbacks) |
309 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) |
310 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
311 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
312 | | |
313 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry) |
314 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions) |
315 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap) |
316 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementCreationCallbacks) |
317 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) |
318 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
319 | | |
320 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry) |
321 | 0 | for (auto iter = tmp->mConstructors.iter(); !iter.done(); iter.next()) { |
322 | 0 | aCallbacks.Trace(&iter.get().mutableKey(), |
323 | 0 | "mConstructors key", |
324 | 0 | aClosure); |
325 | 0 | } |
326 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
327 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
328 | | |
329 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementRegistry) |
330 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementRegistry) |
331 | | |
332 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementRegistry) |
333 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
334 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
335 | 0 | NS_INTERFACE_MAP_END |
336 | | |
337 | | CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow) |
338 | | : mWindow(aWindow) |
339 | | , mIsCustomDefinitionRunning(false) |
340 | 0 | { |
341 | 0 | MOZ_ASSERT(aWindow); |
342 | 0 |
|
343 | 0 | mozilla::HoldJSObjects(this); |
344 | 0 | } |
345 | | |
346 | | CustomElementRegistry::~CustomElementRegistry() |
347 | 0 | { |
348 | 0 | mozilla::DropJSObjects(this); |
349 | 0 | } |
350 | | |
351 | | bool |
352 | | CustomElementRegistry::IsCustomElementEnabled(JSContext* aCx, JSObject* aObject) |
353 | 0 | { |
354 | 0 | if (nsContentUtils::IsCustomElementsEnabled()) { |
355 | 0 | return true; |
356 | 0 | } |
357 | 0 | |
358 | 0 | if (!XRE_IsParentProcess()) { |
359 | 0 | return false; |
360 | 0 | } |
361 | 0 | |
362 | 0 | nsIPrincipal* principal = |
363 | 0 | nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject)); |
364 | 0 | return nsContentUtils::AllowXULXBLForPrincipal(principal); |
365 | 0 | } |
366 | | |
367 | | bool |
368 | | CustomElementRegistry::IsCustomElementEnabled(nsIDocument* aDoc) |
369 | 0 | { |
370 | 0 | if (nsContentUtils::IsCustomElementsEnabled()) { |
371 | 0 | return true; |
372 | 0 | } |
373 | 0 | |
374 | 0 | return XRE_IsParentProcess() && aDoc->AllowXULXBL(); |
375 | 0 | } |
376 | | |
377 | | NS_IMETHODIMP |
378 | | CustomElementRegistry::RunCustomElementCreationCallback::Run() |
379 | 0 | { |
380 | 0 | ErrorResult er; |
381 | 0 | nsDependentAtomString value(mAtom); |
382 | 0 | mCallback->Call(value, er); |
383 | 0 | MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()), |
384 | 0 | "chrome JavaScript error in the callback."); |
385 | 0 |
|
386 | 0 | CustomElementDefinition* definition = |
387 | 0 | mRegistry->mCustomDefinitions.GetWeak(mAtom); |
388 | 0 | MOZ_ASSERT(definition, "Callback should define the definition of type."); |
389 | 0 | MOZ_ASSERT(!mRegistry->mElementCreationCallbacks.GetWeak(mAtom), |
390 | 0 | "Callback should be removed."); |
391 | 0 |
|
392 | 0 | nsAutoPtr<nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>> elements; |
393 | 0 | mRegistry->mElementCreationCallbacksUpgradeCandidatesMap.Remove(mAtom, &elements); |
394 | 0 | MOZ_ASSERT(elements, "There should be a list"); |
395 | 0 |
|
396 | 0 | for (auto iter = elements->Iter(); !iter.Done(); iter.Next()) { |
397 | 0 | nsCOMPtr<Element> elem = do_QueryReferent(iter.Get()->GetKey()); |
398 | 0 | if (!elem) { |
399 | 0 | continue; |
400 | 0 | } |
401 | 0 | |
402 | 0 | CustomElementRegistry::Upgrade(elem, definition, er); |
403 | 0 | MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()), |
404 | 0 | "chrome JavaScript error in custom element construction."); |
405 | 0 | } |
406 | 0 |
|
407 | 0 | return NS_OK; |
408 | 0 | } |
409 | | |
410 | | CustomElementDefinition* |
411 | | CustomElementRegistry::LookupCustomElementDefinition(nsAtom* aNameAtom, |
412 | | int32_t aNameSpaceID, |
413 | | nsAtom* aTypeAtom) |
414 | 0 | { |
415 | 0 | CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom); |
416 | 0 |
|
417 | 0 | if (!data) { |
418 | 0 | RefPtr<CustomElementCreationCallback> callback; |
419 | 0 | mElementCreationCallbacks.Get(aTypeAtom, getter_AddRefs(callback)); |
420 | 0 | if (callback) { |
421 | 0 | mElementCreationCallbacks.Remove(aTypeAtom); |
422 | 0 | mElementCreationCallbacksUpgradeCandidatesMap.LookupOrAdd(aTypeAtom); |
423 | 0 | RefPtr<Runnable> runnable = |
424 | 0 | new RunCustomElementCreationCallback(this, aTypeAtom, callback); |
425 | 0 | nsContentUtils::AddScriptRunner(runnable.forget()); |
426 | 0 | data = mCustomDefinitions.GetWeak(aTypeAtom); |
427 | 0 | } |
428 | 0 | } |
429 | 0 |
|
430 | 0 | if (data && data->mLocalName == aNameAtom && data->mNamespaceID == aNameSpaceID) { |
431 | 0 | return data; |
432 | 0 | } |
433 | 0 | |
434 | 0 | return nullptr; |
435 | 0 | } |
436 | | |
437 | | CustomElementDefinition* |
438 | | CustomElementRegistry::LookupCustomElementDefinition(JSContext* aCx, |
439 | | JSObject* aConstructor) const |
440 | 0 | { |
441 | 0 | JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrap(aConstructor)); |
442 | 0 |
|
443 | 0 | const auto& ptr = mConstructors.lookup(constructor); |
444 | 0 | if (!ptr) { |
445 | 0 | return nullptr; |
446 | 0 | } |
447 | 0 | |
448 | 0 | CustomElementDefinition* definition = mCustomDefinitions.GetWeak(ptr->value()); |
449 | 0 | MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions"); |
450 | 0 |
|
451 | 0 | return definition; |
452 | 0 | } |
453 | | |
454 | | void |
455 | | CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsAtom* aTypeName) |
456 | 0 | { |
457 | 0 | // We don't have a use-case for a Custom Element inside NAC, and continuing |
458 | 0 | // here causes performance issues for NAC + XBL anonymous content. |
459 | 0 | if (aElement->IsInNativeAnonymousSubtree()) { |
460 | 0 | return; |
461 | 0 | } |
462 | 0 | |
463 | 0 | mozilla::dom::NodeInfo* info = aElement->NodeInfo(); |
464 | 0 |
|
465 | 0 | // Candidate may be a custom element through extension, |
466 | 0 | // in which case the custom element type name will not |
467 | 0 | // match the element tag name. e.g. <button is="x-button">. |
468 | 0 | RefPtr<nsAtom> typeName = aTypeName; |
469 | 0 | if (!typeName) { |
470 | 0 | typeName = info->NameAtom(); |
471 | 0 | } |
472 | 0 |
|
473 | 0 | if (mCustomDefinitions.GetWeak(typeName)) { |
474 | 0 | return; |
475 | 0 | } |
476 | 0 | |
477 | 0 | nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>* unresolved = |
478 | 0 | mCandidatesMap.LookupOrAdd(typeName); |
479 | 0 | nsWeakPtr elem = do_GetWeakReference(aElement); |
480 | 0 | unresolved->PutEntry(elem); |
481 | 0 | } |
482 | | |
483 | | void |
484 | | CustomElementRegistry::UnregisterUnresolvedElement(Element* aElement, |
485 | | nsAtom* aTypeName) |
486 | 0 | { |
487 | 0 | nsIWeakReference* weak = aElement->GetExistingWeakReference(); |
488 | 0 | if (!weak) { |
489 | 0 | return; |
490 | 0 | } |
491 | 0 | |
492 | | #ifdef DEBUG |
493 | | { |
494 | | nsWeakPtr weakPtr = do_GetWeakReference(aElement); |
495 | | MOZ_ASSERT(weak == weakPtr.get(), |
496 | | "do_GetWeakReference should reuse the existing nsIWeakReference."); |
497 | | } |
498 | | #endif |
499 | | |
500 | 0 | nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>* candidates = nullptr; |
501 | 0 | if (mCandidatesMap.Get(aTypeName, &candidates)) { |
502 | 0 | MOZ_ASSERT(candidates); |
503 | 0 | candidates->RemoveEntry(weak); |
504 | 0 | } |
505 | 0 | } |
506 | | |
507 | | /* static */ UniquePtr<CustomElementCallback> |
508 | | CustomElementRegistry::CreateCustomElementCallback( |
509 | | nsIDocument::ElementCallbackType aType, Element* aCustomElement, |
510 | | LifecycleCallbackArgs* aArgs, |
511 | | LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs, |
512 | | CustomElementDefinition* aDefinition) |
513 | 0 | { |
514 | 0 | MOZ_ASSERT(aDefinition, "CustomElementDefinition should not be null"); |
515 | 0 | MOZ_ASSERT(aCustomElement->GetCustomElementData(), |
516 | 0 | "CustomElementData should exist"); |
517 | 0 |
|
518 | 0 | // Let CALLBACK be the callback associated with the key NAME in CALLBACKS. |
519 | 0 | CallbackFunction* func = nullptr; |
520 | 0 | switch (aType) { |
521 | 0 | case nsIDocument::eConnected: |
522 | 0 | if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) { |
523 | 0 | func = aDefinition->mCallbacks->mConnectedCallback.Value(); |
524 | 0 | } |
525 | 0 | break; |
526 | 0 |
|
527 | 0 | case nsIDocument::eDisconnected: |
528 | 0 | if (aDefinition->mCallbacks->mDisconnectedCallback.WasPassed()) { |
529 | 0 | func = aDefinition->mCallbacks->mDisconnectedCallback.Value(); |
530 | 0 | } |
531 | 0 | break; |
532 | 0 |
|
533 | 0 | case nsIDocument::eAdopted: |
534 | 0 | if (aDefinition->mCallbacks->mAdoptedCallback.WasPassed()) { |
535 | 0 | func = aDefinition->mCallbacks->mAdoptedCallback.Value(); |
536 | 0 | } |
537 | 0 | break; |
538 | 0 |
|
539 | 0 | case nsIDocument::eAttributeChanged: |
540 | 0 | if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) { |
541 | 0 | func = aDefinition->mCallbacks->mAttributeChangedCallback.Value(); |
542 | 0 | } |
543 | 0 | break; |
544 | 0 |
|
545 | 0 | case nsIDocument::eGetCustomInterface: |
546 | 0 | MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback"); |
547 | 0 | break; |
548 | 0 | } |
549 | 0 |
|
550 | 0 | // If there is no such callback, stop. |
551 | 0 | if (!func) { |
552 | 0 | return nullptr; |
553 | 0 | } |
554 | 0 | |
555 | 0 | // Add CALLBACK to ELEMENT's callback queue. |
556 | 0 | auto callback = |
557 | 0 | MakeUnique<CustomElementCallback>(aCustomElement, aType, func); |
558 | 0 |
|
559 | 0 | if (aArgs) { |
560 | 0 | callback->SetArgs(*aArgs); |
561 | 0 | } |
562 | 0 |
|
563 | 0 | if (aAdoptedCallbackArgs) { |
564 | 0 | callback->SetAdoptedCallbackArgs(*aAdoptedCallbackArgs); |
565 | 0 | } |
566 | 0 | return callback; |
567 | 0 | } |
568 | | |
569 | | /* static */ void |
570 | | CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType, |
571 | | Element* aCustomElement, |
572 | | LifecycleCallbackArgs* aArgs, |
573 | | LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs, |
574 | | CustomElementDefinition* aDefinition) |
575 | 0 | { |
576 | 0 | CustomElementDefinition* definition = aDefinition; |
577 | 0 | if (!definition) { |
578 | 0 | definition = aCustomElement->GetCustomElementDefinition(); |
579 | 0 | if (!definition || |
580 | 0 | definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) { |
581 | 0 | return; |
582 | 0 | } |
583 | 0 | |
584 | 0 | if (!definition->mCallbacks) { |
585 | 0 | // definition has been unlinked. Don't try to mess with it. |
586 | 0 | return; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | |
590 | 0 | auto callback = |
591 | 0 | CreateCustomElementCallback(aType, aCustomElement, aArgs, |
592 | 0 | aAdoptedCallbackArgs, definition); |
593 | 0 | if (!callback) { |
594 | 0 | return; |
595 | 0 | } |
596 | 0 | |
597 | 0 | DocGroup* docGroup = aCustomElement->OwnerDoc()->GetDocGroup(); |
598 | 0 | if (!docGroup) { |
599 | 0 | return; |
600 | 0 | } |
601 | 0 | |
602 | 0 | if (aType == nsIDocument::eAttributeChanged) { |
603 | 0 | RefPtr<nsAtom> attrName = NS_Atomize(aArgs->name); |
604 | 0 | if (definition->mObservedAttributes.IsEmpty() || |
605 | 0 | !definition->mObservedAttributes.Contains(attrName)) { |
606 | 0 | return; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | |
610 | 0 | CustomElementReactionsStack* reactionsStack = |
611 | 0 | docGroup->CustomElementReactionsStack(); |
612 | 0 | reactionsStack->EnqueueCallbackReaction(aCustomElement, std::move(callback)); |
613 | 0 | } |
614 | | |
615 | | namespace { |
616 | | |
617 | | class CandidateFinder |
618 | | { |
619 | | public: |
620 | | CandidateFinder(nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates, |
621 | | nsIDocument* aDoc); |
622 | | nsTArray<nsCOMPtr<Element>> OrderedCandidates(); |
623 | | |
624 | | private: |
625 | | bool Traverse(Element* aRoot, nsTArray<nsCOMPtr<Element>>& aOrderedElements); |
626 | | |
627 | | nsCOMPtr<nsIDocument> mDoc; |
628 | | nsInterfaceHashtable<nsPtrHashKey<Element>, Element> mCandidates; |
629 | | }; |
630 | | |
631 | | CandidateFinder::CandidateFinder(nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>& aCandidates, |
632 | | nsIDocument* aDoc) |
633 | | : mDoc(aDoc) |
634 | | , mCandidates(aCandidates.Count()) |
635 | 0 | { |
636 | 0 | MOZ_ASSERT(mDoc); |
637 | 0 | for (auto iter = aCandidates.Iter(); !iter.Done(); iter.Next()) { |
638 | 0 | nsCOMPtr<Element> elem = do_QueryReferent(iter.Get()->GetKey()); |
639 | 0 | if (!elem) { |
640 | 0 | continue; |
641 | 0 | } |
642 | 0 | |
643 | 0 | Element* key = elem.get(); |
644 | 0 | mCandidates.Put(key, elem.forget()); |
645 | 0 | } |
646 | 0 | } |
647 | | |
648 | | nsTArray<nsCOMPtr<Element>> |
649 | | CandidateFinder::OrderedCandidates() |
650 | 0 | { |
651 | 0 | if (mCandidates.Count() == 1) { |
652 | 0 | // Fast path for one candidate. |
653 | 0 | for (auto iter = mCandidates.Iter(); !iter.Done(); iter.Next()) { |
654 | 0 | nsTArray<nsCOMPtr<Element>> rval({ std::move(iter.Data()) }); |
655 | 0 | iter.Remove(); |
656 | 0 | return rval; |
657 | 0 | } |
658 | 0 | } |
659 | 0 |
|
660 | 0 | nsTArray<nsCOMPtr<Element>> orderedElements(mCandidates.Count()); |
661 | 0 | for (Element* child = mDoc->GetFirstElementChild(); child; child = child->GetNextElementSibling()) { |
662 | 0 | if (!Traverse(child, orderedElements)) { |
663 | 0 | break; |
664 | 0 | } |
665 | 0 | } |
666 | 0 |
|
667 | 0 | return orderedElements; |
668 | 0 | } |
669 | | |
670 | | bool |
671 | | CandidateFinder::Traverse(Element* aRoot, nsTArray<nsCOMPtr<Element>>& aOrderedElements) |
672 | 0 | { |
673 | 0 | nsCOMPtr<Element> elem; |
674 | 0 | if (mCandidates.Remove(aRoot, getter_AddRefs(elem))) { |
675 | 0 | aOrderedElements.AppendElement(std::move(elem)); |
676 | 0 | if (mCandidates.Count() == 0) { |
677 | 0 | return false; |
678 | 0 | } |
679 | 0 | } |
680 | 0 | |
681 | 0 | if (ShadowRoot* root = aRoot->GetShadowRoot()) { |
682 | 0 | // First iterate the children of the shadow root if aRoot is a shadow host. |
683 | 0 | for (Element* child = root->GetFirstElementChild(); child; |
684 | 0 | child = child->GetNextElementSibling()) { |
685 | 0 | if (!Traverse(child, aOrderedElements)) { |
686 | 0 | return false; |
687 | 0 | } |
688 | 0 | } |
689 | 0 | } |
690 | 0 |
|
691 | 0 | // Iterate the explicit children of aRoot. |
692 | 0 | for (Element* child = aRoot->GetFirstElementChild(); child; |
693 | 0 | child = child->GetNextElementSibling()) { |
694 | 0 | if (!Traverse(child, aOrderedElements)) { |
695 | 0 | return false; |
696 | 0 | } |
697 | 0 | } |
698 | 0 |
|
699 | 0 | return true; |
700 | 0 | } |
701 | | |
702 | | } |
703 | | |
704 | | void |
705 | | CustomElementRegistry::UpgradeCandidates(nsAtom* aKey, |
706 | | CustomElementDefinition* aDefinition, |
707 | | ErrorResult& aRv) |
708 | 0 | { |
709 | 0 | DocGroup* docGroup = mWindow->GetDocGroup(); |
710 | 0 | if (!docGroup) { |
711 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
712 | 0 | return; |
713 | 0 | } |
714 | 0 | |
715 | 0 | nsAutoPtr<nsTHashtable<nsRefPtrHashKey<nsIWeakReference>>> candidates; |
716 | 0 | if (mCandidatesMap.Remove(aKey, &candidates)) { |
717 | 0 | MOZ_ASSERT(candidates); |
718 | 0 | CustomElementReactionsStack* reactionsStack = |
719 | 0 | docGroup->CustomElementReactionsStack(); |
720 | 0 |
|
721 | 0 | CandidateFinder finder(*candidates, mWindow->GetExtantDoc()); |
722 | 0 | for (auto& elem : finder.OrderedCandidates()) { |
723 | 0 | reactionsStack->EnqueueUpgradeReaction(elem, aDefinition); |
724 | 0 | } |
725 | 0 | } |
726 | 0 | } |
727 | | |
728 | | JSObject* |
729 | | CustomElementRegistry::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
730 | 0 | { |
731 | 0 | return CustomElementRegistry_Binding::Wrap(aCx, this, aGivenProto); |
732 | 0 | } |
733 | | |
734 | | nsISupports* CustomElementRegistry::GetParentObject() const |
735 | 0 | { |
736 | 0 | return mWindow; |
737 | 0 | } |
738 | | |
739 | | DocGroup* |
740 | | CustomElementRegistry::GetDocGroup() const |
741 | 0 | { |
742 | 0 | return mWindow ? mWindow->GetDocGroup() : nullptr; |
743 | 0 | } |
744 | | |
745 | | int32_t |
746 | | CustomElementRegistry::InferNamespace(JSContext* aCx, |
747 | | JS::Handle<JSObject*> constructor) |
748 | 0 | { |
749 | 0 | JS::Rooted<JSObject*> XULConstructor(aCx, XULElement_Binding::GetConstructorObject(aCx)); |
750 | 0 |
|
751 | 0 | JS::Rooted<JSObject*> proto(aCx, constructor); |
752 | 0 | while (proto) { |
753 | 0 | if (proto == XULConstructor) { |
754 | 0 | return kNameSpaceID_XUL; |
755 | 0 | } |
756 | 0 |
|
757 | 0 | JS_GetPrototype(aCx, proto, &proto); |
758 | 0 | } |
759 | 0 |
|
760 | 0 | return kNameSpaceID_XHTML; |
761 | 0 | } |
762 | | |
763 | | // https://html.spec.whatwg.org/multipage/scripting.html#element-definition |
764 | | void |
765 | | CustomElementRegistry::Define(JSContext* aCx, |
766 | | const nsAString& aName, |
767 | | Function& aFunctionConstructor, |
768 | | const ElementDefinitionOptions& aOptions, |
769 | | ErrorResult& aRv) |
770 | 0 | { |
771 | 0 | // Note: No calls that might run JS or trigger CC before this point, or |
772 | 0 | // there's a (vanishingly small) chance of our constructor being nulled |
773 | 0 | // before we access it. |
774 | 0 | JS::Rooted<JSObject*> constructor(aCx, aFunctionConstructor.CallableOrNull()); |
775 | 0 |
|
776 | 0 | JS::Rooted<JSObject*> constructorUnwrapped(aCx, js::CheckedUnwrap(constructor)); |
777 | 0 | if (!constructorUnwrapped) { |
778 | 0 | // If the caller's compartment does not have permission to access the |
779 | 0 | // unwrapped constructor then throw. |
780 | 0 | aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); |
781 | 0 | return; |
782 | 0 | } |
783 | 0 | |
784 | 0 | /** |
785 | 0 | * 1. If IsConstructor(constructor) is false, then throw a TypeError and abort |
786 | 0 | * these steps. |
787 | 0 | */ |
788 | 0 | if (!JS::IsConstructor(constructorUnwrapped)) { |
789 | 0 | aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>(NS_LITERAL_STRING("Argument 2 of CustomElementRegistry.define")); |
790 | 0 | return; |
791 | 0 | } |
792 | 0 |
|
793 | 0 | int32_t nameSpaceID = InferNamespace(aCx, constructor); |
794 | 0 |
|
795 | 0 | /** |
796 | 0 | * 2. If name is not a valid custom element name, then throw a "SyntaxError" |
797 | 0 | * DOMException and abort these steps. |
798 | 0 | */ |
799 | 0 | nsIDocument* doc = mWindow->GetExtantDoc(); |
800 | 0 | RefPtr<nsAtom> nameAtom(NS_Atomize(aName)); |
801 | 0 | if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) { |
802 | 0 | aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
803 | 0 | return; |
804 | 0 | } |
805 | 0 | |
806 | 0 | /** |
807 | 0 | * 3. If this CustomElementRegistry contains an entry with name name, then |
808 | 0 | * throw a "NotSupportedError" DOMException and abort these steps. |
809 | 0 | */ |
810 | 0 | if (mCustomDefinitions.GetWeak(nameAtom)) { |
811 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
812 | 0 | return; |
813 | 0 | } |
814 | 0 | |
815 | 0 | /** |
816 | 0 | * 4. If this CustomElementRegistry contains an entry with constructor constructor, |
817 | 0 | * then throw a "NotSupportedError" DOMException and abort these steps. |
818 | 0 | */ |
819 | 0 | const auto& ptr = mConstructors.lookup(constructorUnwrapped); |
820 | 0 | if (ptr) { |
821 | 0 | MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()), |
822 | 0 | "Definition must be found in mCustomDefinitions"); |
823 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
824 | 0 | return; |
825 | 0 | } |
826 | 0 |
|
827 | 0 | /** |
828 | 0 | * 5. Let localName be name. |
829 | 0 | * 6. Let extends be the value of the extends member of options, or null if |
830 | 0 | * no such member exists. |
831 | 0 | * 7. If extends is not null, then: |
832 | 0 | * 1. If extends is a valid custom element name, then throw a |
833 | 0 | * "NotSupportedError" DOMException. |
834 | 0 | * 2. If the element interface for extends and the HTML namespace is |
835 | 0 | * HTMLUnknownElement (e.g., if extends does not indicate an element |
836 | 0 | * definition in this specification), then throw a "NotSupportedError" |
837 | 0 | * DOMException. |
838 | 0 | * 3. Set localName to extends. |
839 | 0 | * |
840 | 0 | * Special note for XUL elements: |
841 | 0 | * |
842 | 0 | * For step 7.1, we'll subject XUL to the same rules as HTML, so that a |
843 | 0 | * custom built-in element will not be extending from a dashed name. |
844 | 0 | * Step 7.2 is disregarded. But, we do check if the name is a dashed name |
845 | 0 | * (i.e. step 2) given that there is no reason for a custom built-in element |
846 | 0 | * type to take on a non-dashed name. |
847 | 0 | * This also ensures the name of the built-in custom element type can never |
848 | 0 | * be the same as the built-in element name, so we don't break the assumption |
849 | 0 | * elsewhere. |
850 | 0 | */ |
851 | 0 | nsAutoString localName(aName); |
852 | 0 | if (aOptions.mExtends.WasPassed()) { |
853 | 0 | RefPtr<nsAtom> extendsAtom(NS_Atomize(aOptions.mExtends.Value())); |
854 | 0 | if (nsContentUtils::IsCustomElementName(extendsAtom, kNameSpaceID_XHTML)) { |
855 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
856 | 0 | return; |
857 | 0 | } |
858 | 0 | |
859 | 0 | if (nameSpaceID == kNameSpaceID_XHTML) { |
860 | 0 | // bgsound and multicol are unknown html element. |
861 | 0 | int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom); |
862 | 0 | if (tag == eHTMLTag_userdefined || |
863 | 0 | tag == eHTMLTag_bgsound || |
864 | 0 | tag == eHTMLTag_multicol) { |
865 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
866 | 0 | return; |
867 | 0 | } |
868 | 0 | } else { // kNameSpaceID_XUL |
869 | 0 | // As stated above, ensure the name of the customized built-in element |
870 | 0 | // (the one that goes to the |is| attribute) is a dashed name. |
871 | 0 | if (!nsContentUtils::IsNameWithDash(nameAtom)) { |
872 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
873 | 0 | return; |
874 | 0 | } |
875 | 0 | } |
876 | 0 | |
877 | 0 | localName.Assign(aOptions.mExtends.Value()); |
878 | 0 | } |
879 | 0 |
|
880 | 0 | /** |
881 | 0 | * 8. If this CustomElementRegistry's element definition is running flag is set, |
882 | 0 | * then throw a "NotSupportedError" DOMException and abort these steps. |
883 | 0 | */ |
884 | 0 | if (mIsCustomDefinitionRunning) { |
885 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
886 | 0 | return; |
887 | 0 | } |
888 | 0 | |
889 | 0 | auto callbacksHolder = MakeUnique<LifecycleCallbacks>(); |
890 | 0 | nsTArray<RefPtr<nsAtom>> observedAttributes; |
891 | 0 | { // Set mIsCustomDefinitionRunning. |
892 | 0 | /** |
893 | 0 | * 9. Set this CustomElementRegistry's element definition is running flag. |
894 | 0 | */ |
895 | 0 | AutoSetRunningFlag as(this); |
896 | 0 |
|
897 | 0 | /** |
898 | 0 | * 10.1. Let prototype be Get(constructor, "prototype"). Rethrow any exceptions. |
899 | 0 | */ |
900 | 0 | // The .prototype on the constructor passed could be an "expando" of a |
901 | 0 | // wrapper. So we should get it from wrapper instead of the underlying |
902 | 0 | // object. |
903 | 0 | JS::Rooted<JS::Value> prototype(aCx); |
904 | 0 | if (!JS_GetProperty(aCx, constructor, "prototype", &prototype)) { |
905 | 0 | aRv.NoteJSContextException(aCx); |
906 | 0 | return; |
907 | 0 | } |
908 | 0 | |
909 | 0 | /** |
910 | 0 | * 10.2. If Type(prototype) is not Object, then throw a TypeError exception. |
911 | 0 | */ |
912 | 0 | if (!prototype.isObject()) { |
913 | 0 | aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("constructor.prototype")); |
914 | 0 | return; |
915 | 0 | } |
916 | 0 |
|
917 | 0 | /** |
918 | 0 | * 10.3. Let lifecycleCallbacks be a map with the four keys |
919 | 0 | * "connectedCallback", "disconnectedCallback", "adoptedCallback", and |
920 | 0 | * "attributeChangedCallback", each of which belongs to an entry whose |
921 | 0 | * value is null. The 'getCustomInterface' callback is also included |
922 | 0 | * for chrome usage. |
923 | 0 | * 10.4. For each of the four keys callbackName in lifecycleCallbacks: |
924 | 0 | * 1. Let callbackValue be Get(prototype, callbackName). Rethrow any |
925 | 0 | * exceptions. |
926 | 0 | * 2. If callbackValue is not undefined, then set the value of the |
927 | 0 | * entry in lifecycleCallbacks with key callbackName to the result |
928 | 0 | * of converting callbackValue to the Web IDL Function callback type. |
929 | 0 | * Rethrow any exceptions from the conversion. |
930 | 0 | */ |
931 | 0 | if (!callbacksHolder->Init(aCx, prototype)) { |
932 | 0 | aRv.NoteJSContextException(aCx); |
933 | 0 | return; |
934 | 0 | } |
935 | 0 | |
936 | 0 | /** |
937 | 0 | * 10.5. Let observedAttributes be an empty sequence<DOMString>. |
938 | 0 | * 10.6. If the value of the entry in lifecycleCallbacks with key |
939 | 0 | * "attributeChangedCallback" is not null, then: |
940 | 0 | * 1. Let observedAttributesIterable be Get(constructor, |
941 | 0 | * "observedAttributes"). Rethrow any exceptions. |
942 | 0 | * 2. If observedAttributesIterable is not undefined, then set |
943 | 0 | * observedAttributes to the result of converting |
944 | 0 | * observedAttributesIterable to a sequence<DOMString>. Rethrow |
945 | 0 | * any exceptions from the conversion. |
946 | 0 | */ |
947 | 0 | if (callbacksHolder->mAttributeChangedCallback.WasPassed()) { |
948 | 0 | JS::Rooted<JS::Value> observedAttributesIterable(aCx); |
949 | 0 |
|
950 | 0 | if (!JS_GetProperty(aCx, constructor, "observedAttributes", |
951 | 0 | &observedAttributesIterable)) { |
952 | 0 | aRv.NoteJSContextException(aCx); |
953 | 0 | return; |
954 | 0 | } |
955 | 0 | |
956 | 0 | if (!observedAttributesIterable.isUndefined()) { |
957 | 0 | if (!observedAttributesIterable.isObject()) { |
958 | 0 | aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("observedAttributes")); |
959 | 0 | return; |
960 | 0 | } |
961 | 0 |
|
962 | 0 | JS::ForOfIterator iter(aCx); |
963 | 0 | if (!iter.init(observedAttributesIterable, JS::ForOfIterator::AllowNonIterable)) { |
964 | 0 | aRv.NoteJSContextException(aCx); |
965 | 0 | return; |
966 | 0 | } |
967 | 0 | |
968 | 0 | if (!iter.valueIsIterable()) { |
969 | 0 | aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(NS_LITERAL_STRING("observedAttributes")); |
970 | 0 | return; |
971 | 0 | } |
972 | 0 |
|
973 | 0 | JS::Rooted<JS::Value> attribute(aCx); |
974 | 0 | while (true) { |
975 | 0 | bool done; |
976 | 0 | if (!iter.next(&attribute, &done)) { |
977 | 0 | aRv.NoteJSContextException(aCx); |
978 | 0 | return; |
979 | 0 | } |
980 | 0 | if (done) { |
981 | 0 | break; |
982 | 0 | } |
983 | 0 | |
984 | 0 | nsAutoString attrStr; |
985 | 0 | if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify, attrStr)) { |
986 | 0 | aRv.NoteJSContextException(aCx); |
987 | 0 | return; |
988 | 0 | } |
989 | 0 | |
990 | 0 | if (!observedAttributes.AppendElement(NS_Atomize(attrStr))) { |
991 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
992 | 0 | return; |
993 | 0 | } |
994 | 0 | } |
995 | 0 | } |
996 | 0 | } |
997 | 0 | } // Unset mIsCustomDefinitionRunning |
998 | 0 |
|
999 | 0 | /** |
1000 | 0 | * 11. Let definition be a new custom element definition with name name, |
1001 | 0 | * local name localName, constructor constructor, prototype prototype, |
1002 | 0 | * observed attributes observedAttributes, and lifecycle callbacks |
1003 | 0 | * lifecycleCallbacks. |
1004 | 0 | */ |
1005 | 0 | // Associate the definition with the custom element. |
1006 | 0 | RefPtr<nsAtom> localNameAtom(NS_Atomize(localName)); |
1007 | 0 |
|
1008 | 0 | /** |
1009 | 0 | * 12. Add definition to this CustomElementRegistry. |
1010 | 0 | */ |
1011 | 0 | if (!mConstructors.put(constructorUnwrapped, nameAtom)) { |
1012 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
1013 | 0 | return; |
1014 | 0 | } |
1015 | 0 | |
1016 | 0 | RefPtr<CustomElementDefinition> definition = |
1017 | 0 | new CustomElementDefinition(nameAtom, |
1018 | 0 | localNameAtom, |
1019 | 0 | nameSpaceID, |
1020 | 0 | &aFunctionConstructor, |
1021 | 0 | std::move(observedAttributes), |
1022 | 0 | std::move(callbacksHolder)); |
1023 | 0 |
|
1024 | 0 | CustomElementDefinition* def = definition.get(); |
1025 | 0 | mCustomDefinitions.Put(nameAtom, definition.forget()); |
1026 | 0 |
|
1027 | 0 | MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(), |
1028 | 0 | "Number of entries should be the same"); |
1029 | 0 |
|
1030 | 0 | /** |
1031 | 0 | * 13. 14. 15. Upgrade candidates |
1032 | 0 | */ |
1033 | 0 | UpgradeCandidates(nameAtom, def, aRv); |
1034 | 0 |
|
1035 | 0 | /** |
1036 | 0 | * 16. If this CustomElementRegistry's when-defined promise map contains an |
1037 | 0 | * entry with key name: |
1038 | 0 | * 1. Let promise be the value of that entry. |
1039 | 0 | * 2. Resolve promise with undefined. |
1040 | 0 | * 3. Delete the entry with key name from this CustomElementRegistry's |
1041 | 0 | * when-defined promise map. |
1042 | 0 | */ |
1043 | 0 | RefPtr<Promise> promise; |
1044 | 0 | mWhenDefinedPromiseMap.Remove(nameAtom, getter_AddRefs(promise)); |
1045 | 0 | if (promise) { |
1046 | 0 | promise->MaybeResolveWithUndefined(); |
1047 | 0 | } |
1048 | 0 |
|
1049 | 0 | // Dispatch a "customelementdefined" event for DevTools. |
1050 | 0 | { |
1051 | 0 | JSString* nameJsStr = JS_NewUCStringCopyN(aCx, |
1052 | 0 | aName.BeginReading(), |
1053 | 0 | aName.Length()); |
1054 | 0 |
|
1055 | 0 | JS::Rooted<JS::Value> detail(aCx, JS::StringValue(nameJsStr)); |
1056 | 0 | RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr); |
1057 | 0 | event->InitCustomEvent(aCx, |
1058 | 0 | NS_LITERAL_STRING("customelementdefined"), |
1059 | 0 | /* CanBubble */ true, |
1060 | 0 | /* Cancelable */ true, |
1061 | 0 | detail); |
1062 | 0 | event->SetTrusted(true); |
1063 | 0 |
|
1064 | 0 | AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(doc, event); |
1065 | 0 | dispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes; |
1066 | 0 |
|
1067 | 0 | dispatcher->PostDOMEvent(); |
1068 | 0 | } |
1069 | 0 |
|
1070 | 0 | /** |
1071 | 0 | * Clean-up mElementCreationCallbacks (if it exists) |
1072 | 0 | */ |
1073 | 0 | mElementCreationCallbacks.Remove(nameAtom); |
1074 | 0 |
|
1075 | 0 | } |
1076 | | |
1077 | | void |
1078 | | CustomElementRegistry::SetElementCreationCallback(const nsAString& aName, |
1079 | | CustomElementCreationCallback& aCallback, |
1080 | | ErrorResult& aRv) |
1081 | 0 | { |
1082 | 0 | RefPtr<nsAtom> nameAtom(NS_Atomize(aName)); |
1083 | 0 | if (mElementCreationCallbacks.GetWeak(nameAtom) || |
1084 | 0 | mCustomDefinitions.GetWeak(nameAtom)) { |
1085 | 0 | aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
1086 | 0 | return; |
1087 | 0 | } |
1088 | 0 | |
1089 | 0 | RefPtr<CustomElementCreationCallback> callback = &aCallback; |
1090 | 0 | mElementCreationCallbacks.Put(nameAtom, callback.forget()); |
1091 | 0 | return; |
1092 | 0 | } |
1093 | | |
1094 | | static void |
1095 | | TryUpgrade(nsINode& aNode) |
1096 | 0 | { |
1097 | 0 | Element* element = aNode.IsElement() ? aNode.AsElement() : nullptr; |
1098 | 0 | if (element) { |
1099 | 0 | CustomElementData* ceData = element->GetCustomElementData(); |
1100 | 0 | if (ceData) { |
1101 | 0 | NodeInfo* nodeInfo = element->NodeInfo(); |
1102 | 0 | nsAtom* typeAtom = ceData->GetCustomElementType(); |
1103 | 0 | CustomElementDefinition* definition = |
1104 | 0 | nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(), |
1105 | 0 | nodeInfo->NameAtom(), |
1106 | 0 | nodeInfo->NamespaceID(), |
1107 | 0 | typeAtom); |
1108 | 0 | if (definition) { |
1109 | 0 | nsContentUtils::EnqueueUpgradeReaction(element, definition); |
1110 | 0 | } |
1111 | 0 | } |
1112 | 0 |
|
1113 | 0 | if (ShadowRoot* root = element->GetShadowRoot()) { |
1114 | 0 | for (Element* child = root->GetFirstElementChild(); child; |
1115 | 0 | child = child->GetNextElementSibling()) { |
1116 | 0 | TryUpgrade(*child); |
1117 | 0 | } |
1118 | 0 | } |
1119 | 0 | } |
1120 | 0 |
|
1121 | 0 | for (Element* child = aNode.GetFirstElementChild(); child; |
1122 | 0 | child = child->GetNextElementSibling()) { |
1123 | 0 | TryUpgrade(*child); |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | void |
1128 | | CustomElementRegistry::Upgrade(nsINode& aRoot) |
1129 | 0 | { |
1130 | 0 | TryUpgrade(aRoot); |
1131 | 0 | } |
1132 | | |
1133 | | void |
1134 | | CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName, |
1135 | | JS::MutableHandle<JS::Value> aRetVal) |
1136 | 0 | { |
1137 | 0 | RefPtr<nsAtom> nameAtom(NS_Atomize(aName)); |
1138 | 0 | CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom); |
1139 | 0 |
|
1140 | 0 | if (!data) { |
1141 | 0 | aRetVal.setUndefined(); |
1142 | 0 | return; |
1143 | 0 | } |
1144 | 0 | |
1145 | 0 | aRetVal.setObject(*data->mConstructor->Callback(aCx)); |
1146 | 0 | } |
1147 | | |
1148 | | already_AddRefed<Promise> |
1149 | | CustomElementRegistry::WhenDefined(const nsAString& aName, ErrorResult& aRv) |
1150 | 0 | { |
1151 | 0 | nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow); |
1152 | 0 | RefPtr<Promise> promise = Promise::Create(global, aRv); |
1153 | 0 |
|
1154 | 0 | if (aRv.Failed()) { |
1155 | 0 | return nullptr; |
1156 | 0 | } |
1157 | 0 | |
1158 | 0 | RefPtr<nsAtom> nameAtom(NS_Atomize(aName)); |
1159 | 0 | nsIDocument* doc = mWindow->GetExtantDoc(); |
1160 | 0 | uint32_t nameSpaceID = doc ? doc->GetDefaultNamespaceID() : kNameSpaceID_XHTML; |
1161 | 0 | if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) { |
1162 | 0 | promise->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR); |
1163 | 0 | return promise.forget(); |
1164 | 0 | } |
1165 | 0 | |
1166 | 0 | if (mCustomDefinitions.GetWeak(nameAtom)) { |
1167 | 0 | promise->MaybeResolve(JS::UndefinedHandleValue); |
1168 | 0 | return promise.forget(); |
1169 | 0 | } |
1170 | 0 | |
1171 | 0 | auto entry = mWhenDefinedPromiseMap.LookupForAdd(nameAtom); |
1172 | 0 | if (entry) { |
1173 | 0 | promise = entry.Data(); |
1174 | 0 | } else { |
1175 | 0 | entry.OrInsert([&promise](){ return promise; }); |
1176 | 0 | } |
1177 | 0 |
|
1178 | 0 | return promise.forget(); |
1179 | 0 | } |
1180 | | |
1181 | | namespace { |
1182 | | |
1183 | | static void |
1184 | | DoUpgrade(Element* aElement, |
1185 | | CustomElementConstructor* aConstructor, |
1186 | | ErrorResult& aRv) |
1187 | 0 | { |
1188 | 0 | // Rethrow the exception since it might actually throw the exception from the |
1189 | 0 | // upgrade steps back out to the caller of document.createElement. |
1190 | 0 | RefPtr<Element> constructResult = |
1191 | 0 | aConstructor->Construct("Custom Element Upgrade", aRv); |
1192 | 0 | if (aRv.Failed()) { |
1193 | 0 | return; |
1194 | 0 | } |
1195 | 0 | |
1196 | 0 | if (!constructResult || constructResult.get() != aElement) { |
1197 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
1198 | 0 | return; |
1199 | 0 | } |
1200 | 0 | } |
1201 | | |
1202 | | } // anonymous namespace |
1203 | | |
1204 | | // https://html.spec.whatwg.org/multipage/scripting.html#upgrades |
1205 | | /* static */ void |
1206 | | CustomElementRegistry::Upgrade(Element* aElement, |
1207 | | CustomElementDefinition* aDefinition, |
1208 | | ErrorResult& aRv) |
1209 | 0 | { |
1210 | 0 | RefPtr<CustomElementData> data = aElement->GetCustomElementData(); |
1211 | 0 | MOZ_ASSERT(data, "CustomElementData should exist"); |
1212 | 0 |
|
1213 | 0 | // Step 1 and step 2. |
1214 | 0 | if (data->mState == CustomElementData::State::eCustom || |
1215 | 0 | data->mState == CustomElementData::State::eFailed) { |
1216 | 0 | return; |
1217 | 0 | } |
1218 | 0 | |
1219 | 0 | // Step 3. |
1220 | 0 | if (!aDefinition->mObservedAttributes.IsEmpty()) { |
1221 | 0 | uint32_t count = aElement->GetAttrCount(); |
1222 | 0 | for (uint32_t i = 0; i < count; i++) { |
1223 | 0 | mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(i); |
1224 | 0 |
|
1225 | 0 | const nsAttrName* name = info.mName; |
1226 | 0 | nsAtom* attrName = name->LocalName(); |
1227 | 0 |
|
1228 | 0 | if (aDefinition->IsInObservedAttributeList(attrName)) { |
1229 | 0 | int32_t namespaceID = name->NamespaceID(); |
1230 | 0 | nsAutoString attrValue, namespaceURI; |
1231 | 0 | info.mValue->ToString(attrValue); |
1232 | 0 | nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, |
1233 | 0 | namespaceURI); |
1234 | 0 |
|
1235 | 0 | LifecycleCallbackArgs args = { |
1236 | 0 | nsDependentAtomString(attrName), |
1237 | 0 | VoidString(), |
1238 | 0 | attrValue, |
1239 | 0 | (namespaceURI.IsEmpty() ? VoidString() : namespaceURI) |
1240 | 0 | }; |
1241 | 0 | nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eAttributeChanged, |
1242 | 0 | aElement, |
1243 | 0 | &args, nullptr, aDefinition); |
1244 | 0 | } |
1245 | 0 | } |
1246 | 0 | } |
1247 | 0 |
|
1248 | 0 | // Step 4. |
1249 | 0 | if (aElement->IsInComposedDoc()) { |
1250 | 0 | nsContentUtils::EnqueueLifecycleCallback(nsIDocument::eConnected, aElement, |
1251 | 0 | nullptr, nullptr, aDefinition); |
1252 | 0 | } |
1253 | 0 |
|
1254 | 0 | // Step 5. |
1255 | 0 | AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement); |
1256 | 0 |
|
1257 | 0 | // Step 6 and step 7. |
1258 | 0 | DoUpgrade(aElement, aDefinition->mConstructor, aRv); |
1259 | 0 | if (aRv.Failed()) { |
1260 | 0 | data->mState = CustomElementData::State::eFailed; |
1261 | 0 | // Empty element's custom element reaction queue. |
1262 | 0 | data->mReactionQueue.Clear(); |
1263 | 0 | return; |
1264 | 0 | } |
1265 | 0 | |
1266 | 0 | // Step 8. |
1267 | 0 | data->mState = CustomElementData::State::eCustom; |
1268 | 0 | aElement->SetDefined(true); |
1269 | 0 |
|
1270 | 0 | // Step 9. |
1271 | 0 | aElement->SetCustomElementDefinition(aDefinition); |
1272 | 0 | } |
1273 | | |
1274 | | already_AddRefed<nsISupports> |
1275 | | CustomElementRegistry::CallGetCustomInterface(Element* aElement, |
1276 | | const nsIID& aIID) |
1277 | 0 | { |
1278 | 0 | MOZ_ASSERT(aElement); |
1279 | 0 |
|
1280 | 0 | if (nsContentUtils::IsChromeDoc(aElement->OwnerDoc())) { |
1281 | 0 | CustomElementDefinition* definition = aElement->GetCustomElementDefinition(); |
1282 | 0 | if (definition && definition->mCallbacks && |
1283 | 0 | definition->mCallbacks->mGetCustomInterfaceCallback.WasPassed() && |
1284 | 0 | definition->mLocalName == aElement->NodeInfo()->NameAtom()) { |
1285 | 0 |
|
1286 | 0 | LifecycleGetCustomInterfaceCallback* func = |
1287 | 0 | definition->mCallbacks->mGetCustomInterfaceCallback.Value(); |
1288 | 0 | JS::Rooted<JSObject*> customInterface(RootingCx()); |
1289 | 0 |
|
1290 | 0 | nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aIID); |
1291 | 0 | func->Call(aElement, iid, &customInterface); |
1292 | 0 | JS::Rooted<JSObject*> funcGlobal(RootingCx(), func->CallbackGlobalOrNull()); |
1293 | 0 | if (customInterface && funcGlobal) { |
1294 | 0 | AutoJSAPI jsapi; |
1295 | 0 | if (jsapi.Init(funcGlobal)) { |
1296 | 0 | nsIXPConnect *xpConnect = nsContentUtils::XPConnect(); |
1297 | 0 | JSContext* cx = jsapi.cx(); |
1298 | 0 |
|
1299 | 0 | nsCOMPtr<nsISupports> wrapper; |
1300 | 0 | nsresult rv = xpConnect->WrapJSAggregatedToNative(aElement, cx, customInterface, |
1301 | 0 | aIID, getter_AddRefs(wrapper)); |
1302 | 0 | if (NS_SUCCEEDED(rv)) { |
1303 | 0 | return wrapper.forget(); |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | } |
1307 | 0 | } |
1308 | 0 | } |
1309 | 0 | |
1310 | 0 | return nullptr; |
1311 | 0 | } |
1312 | | |
1313 | | //----------------------------------------------------- |
1314 | | // CustomElementReactionsStack |
1315 | | |
1316 | | void |
1317 | | CustomElementReactionsStack::CreateAndPushElementQueue() |
1318 | 0 | { |
1319 | 0 | MOZ_ASSERT(mRecursionDepth); |
1320 | 0 | MOZ_ASSERT(!mIsElementQueuePushedForCurrentRecursionDepth); |
1321 | 0 |
|
1322 | 0 | // Push a new element queue onto the custom element reactions stack. |
1323 | 0 | mReactionsStack.AppendElement(MakeUnique<ElementQueue>()); |
1324 | 0 | mIsElementQueuePushedForCurrentRecursionDepth = true; |
1325 | 0 | } |
1326 | | |
1327 | | void |
1328 | | CustomElementReactionsStack::PopAndInvokeElementQueue() |
1329 | 0 | { |
1330 | 0 | MOZ_ASSERT(mRecursionDepth); |
1331 | 0 | MOZ_ASSERT(mIsElementQueuePushedForCurrentRecursionDepth); |
1332 | 0 | MOZ_ASSERT(!mReactionsStack.IsEmpty(), |
1333 | 0 | "Reaction stack shouldn't be empty"); |
1334 | 0 |
|
1335 | 0 | // Pop the element queue from the custom element reactions stack, |
1336 | 0 | // and invoke custom element reactions in that queue. |
1337 | 0 | const uint32_t lastIndex = mReactionsStack.Length() - 1; |
1338 | 0 | ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get(); |
1339 | 0 | // Check element queue size in order to reduce function call overhead. |
1340 | 0 | if (!elementQueue->IsEmpty()) { |
1341 | 0 | // It is still not clear what error reporting will look like in custom |
1342 | 0 | // element, see https://github.com/w3c/webcomponents/issues/635. |
1343 | 0 | // We usually report the error to entry global in gecko, so just follow the |
1344 | 0 | // same behavior here. |
1345 | 0 | // This may be null if it's called from parser, see the case of |
1346 | 0 | // attributeChangedCallback in |
1347 | 0 | // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token |
1348 | 0 | // In that case, the exception of callback reactions will be automatically |
1349 | 0 | // reported in CallSetup. |
1350 | 0 | nsIGlobalObject* global = GetEntryGlobal(); |
1351 | 0 | InvokeReactions(elementQueue, global); |
1352 | 0 | } |
1353 | 0 |
|
1354 | 0 | // InvokeReactions() might create other custom element reactions, but those |
1355 | 0 | // new reactions should be already consumed and removed at this point. |
1356 | 0 | MOZ_ASSERT(lastIndex == mReactionsStack.Length() - 1, |
1357 | 0 | "reactions created by InvokeReactions() should be consumed and removed"); |
1358 | 0 |
|
1359 | 0 | mReactionsStack.RemoveElementAt(lastIndex); |
1360 | 0 | mIsElementQueuePushedForCurrentRecursionDepth = false; |
1361 | 0 | } |
1362 | | |
1363 | | void |
1364 | | CustomElementReactionsStack::EnqueueUpgradeReaction(Element* aElement, |
1365 | | CustomElementDefinition* aDefinition) |
1366 | 0 | { |
1367 | 0 | Enqueue(aElement, new CustomElementUpgradeReaction(aDefinition)); |
1368 | 0 | } |
1369 | | |
1370 | | void |
1371 | | CustomElementReactionsStack::EnqueueCallbackReaction(Element* aElement, |
1372 | | UniquePtr<CustomElementCallback> aCustomElementCallback) |
1373 | 0 | { |
1374 | 0 | Enqueue(aElement, new CustomElementCallbackReaction(std::move(aCustomElementCallback))); |
1375 | 0 | } |
1376 | | |
1377 | | void |
1378 | | CustomElementReactionsStack::Enqueue(Element* aElement, |
1379 | | CustomElementReaction* aReaction) |
1380 | 0 | { |
1381 | 0 | RefPtr<CustomElementData> elementData = aElement->GetCustomElementData(); |
1382 | 0 | MOZ_ASSERT(elementData, "CustomElementData should exist"); |
1383 | 0 |
|
1384 | 0 | if (mRecursionDepth) { |
1385 | 0 | // If the element queue is not created for current recursion depth, create |
1386 | 0 | // and push an element queue to reactions stack first. |
1387 | 0 | if (!mIsElementQueuePushedForCurrentRecursionDepth) { |
1388 | 0 | CreateAndPushElementQueue(); |
1389 | 0 | } |
1390 | 0 |
|
1391 | 0 | MOZ_ASSERT(!mReactionsStack.IsEmpty()); |
1392 | 0 | // Add element to the current element queue. |
1393 | 0 | mReactionsStack.LastElement()->AppendElement(aElement); |
1394 | 0 | elementData->mReactionQueue.AppendElement(aReaction); |
1395 | 0 | return; |
1396 | 0 | } |
1397 | 0 |
|
1398 | 0 | // If the custom element reactions stack is empty, then: |
1399 | 0 | // Add element to the backup element queue. |
1400 | 0 | MOZ_ASSERT(mReactionsStack.IsEmpty(), |
1401 | 0 | "custom element reactions stack should be empty"); |
1402 | 0 | mBackupQueue.AppendElement(aElement); |
1403 | 0 | elementData->mReactionQueue.AppendElement(aReaction); |
1404 | 0 |
|
1405 | 0 | if (mIsBackupQueueProcessing) { |
1406 | 0 | return; |
1407 | 0 | } |
1408 | 0 | |
1409 | 0 | CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); |
1410 | 0 | RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this); |
1411 | 0 | context->DispatchToMicroTask(bqmt.forget()); |
1412 | 0 | } |
1413 | | |
1414 | | void |
1415 | | CustomElementReactionsStack::InvokeBackupQueue() |
1416 | 0 | { |
1417 | 0 | // Check backup queue size in order to reduce function call overhead. |
1418 | 0 | if (!mBackupQueue.IsEmpty()) { |
1419 | 0 | // Upgrade reactions won't be scheduled in backup queue and the exception of |
1420 | 0 | // callback reactions will be automatically reported in CallSetup. |
1421 | 0 | // If the reactions are invoked from backup queue (in microtask check point), |
1422 | 0 | // we don't need to pass global object for error reporting. |
1423 | 0 | InvokeReactions(&mBackupQueue, nullptr); |
1424 | 0 | } |
1425 | 0 | MOZ_ASSERT(mBackupQueue.IsEmpty(), |
1426 | 0 | "There are still some reactions in BackupQueue not being consumed!?!"); |
1427 | 0 | } |
1428 | | |
1429 | | void |
1430 | | CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue, |
1431 | | nsIGlobalObject* aGlobal) |
1432 | 0 | { |
1433 | 0 | // This is used for error reporting. |
1434 | 0 | Maybe<AutoEntryScript> aes; |
1435 | 0 | if (aGlobal) { |
1436 | 0 | aes.emplace(aGlobal, "custom elements reaction invocation"); |
1437 | 0 | } |
1438 | 0 |
|
1439 | 0 | // Note: It's possible to re-enter this method. |
1440 | 0 | for (uint32_t i = 0; i < aElementQueue->Length(); ++i) { |
1441 | 0 | Element* element = aElementQueue->ElementAt(i); |
1442 | 0 | // ElementQueue hold a element's strong reference, it should not be a nullptr. |
1443 | 0 | MOZ_ASSERT(element); |
1444 | 0 |
|
1445 | 0 | RefPtr<CustomElementData> elementData = element->GetCustomElementData(); |
1446 | 0 | if (!elementData || !element->GetOwnerGlobal()) { |
1447 | 0 | // This happens when the document is destroyed and the element is already |
1448 | 0 | // unlinked, no need to fire the callbacks in this case. |
1449 | 0 | continue; |
1450 | 0 | } |
1451 | 0 | |
1452 | 0 | auto& reactions = elementData->mReactionQueue; |
1453 | 0 | for (uint32_t j = 0; j < reactions.Length(); ++j) { |
1454 | 0 | // Transfer the ownership of the entry due to reentrant invocation of |
1455 | 0 | // this function. |
1456 | 0 | auto reaction(std::move(reactions.ElementAt(j))); |
1457 | 0 | if (reaction) { |
1458 | 0 | if (!aGlobal && reaction->IsUpgradeReaction()) { |
1459 | 0 | nsIGlobalObject* global = element->GetOwnerGlobal(); |
1460 | 0 | MOZ_ASSERT(!aes); |
1461 | 0 | aes.emplace(global, "custom elements reaction invocation"); |
1462 | 0 | } |
1463 | 0 | ErrorResult rv; |
1464 | 0 | reaction->Invoke(element, rv); |
1465 | 0 | if (aes) { |
1466 | 0 | JSContext* cx = aes->cx(); |
1467 | 0 | if (rv.MaybeSetPendingException(cx)) { |
1468 | 0 | aes->ReportException(); |
1469 | 0 | } |
1470 | 0 | MOZ_ASSERT(!JS_IsExceptionPending(cx)); |
1471 | 0 | if (!aGlobal && reaction->IsUpgradeReaction()) { |
1472 | 0 | aes.reset(); |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | MOZ_ASSERT(!rv.Failed()); |
1476 | 0 | } |
1477 | 0 | } |
1478 | 0 | reactions.Clear(); |
1479 | 0 | } |
1480 | 0 | aElementQueue->Clear(); |
1481 | 0 | } |
1482 | | |
1483 | | //----------------------------------------------------- |
1484 | | // CustomElementDefinition |
1485 | | |
1486 | | NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementDefinition) |
1487 | | |
1488 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementDefinition) |
1489 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructor) |
1490 | 0 | tmp->mCallbacks = nullptr; |
1491 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
1492 | | |
1493 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition) |
1494 | 0 | mozilla::dom::LifecycleCallbacks* callbacks = tmp->mCallbacks.get(); |
1495 | 0 |
|
1496 | 0 | if (callbacks->mAttributeChangedCallback.WasPassed()) { |
1497 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, |
1498 | 0 | "mCallbacks->mAttributeChangedCallback"); |
1499 | 0 | cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value()); |
1500 | 0 | } |
1501 | 0 |
|
1502 | 0 | if (callbacks->mConnectedCallback.WasPassed()) { |
1503 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mConnectedCallback"); |
1504 | 0 | cb.NoteXPCOMChild(callbacks->mConnectedCallback.Value()); |
1505 | 0 | } |
1506 | 0 |
|
1507 | 0 | if (callbacks->mDisconnectedCallback.WasPassed()) { |
1508 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mDisconnectedCallback"); |
1509 | 0 | cb.NoteXPCOMChild(callbacks->mDisconnectedCallback.Value()); |
1510 | 0 | } |
1511 | 0 |
|
1512 | 0 | if (callbacks->mAdoptedCallback.WasPassed()) { |
1513 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mAdoptedCallback"); |
1514 | 0 | cb.NoteXPCOMChild(callbacks->mAdoptedCallback.Value()); |
1515 | 0 | } |
1516 | 0 |
|
1517 | 0 | if (callbacks->mGetCustomInterfaceCallback.WasPassed()) { |
1518 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mGetCustomInterfaceCallback"); |
1519 | 0 | cb.NoteXPCOMChild(callbacks->mGetCustomInterfaceCallback.Value()); |
1520 | 0 | } |
1521 | 0 |
|
1522 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConstructor"); |
1523 | 0 | cb.NoteXPCOMChild(tmp->mConstructor); |
1524 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
1525 | | |
1526 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementDefinition) |
1527 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
1528 | | |
1529 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef) |
1530 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release) |
1531 | | |
1532 | | |
1533 | | CustomElementDefinition::CustomElementDefinition(nsAtom* aType, |
1534 | | nsAtom* aLocalName, |
1535 | | int32_t aNamespaceID, |
1536 | | Function* aConstructor, |
1537 | | nsTArray<RefPtr<nsAtom>>&& aObservedAttributes, |
1538 | | UniquePtr<LifecycleCallbacks>&& aCallbacks) |
1539 | | : mType(aType), |
1540 | | mLocalName(aLocalName), |
1541 | | mNamespaceID(aNamespaceID), |
1542 | | mConstructor(new CustomElementConstructor(aConstructor)), |
1543 | | mObservedAttributes(std::move(aObservedAttributes)), |
1544 | | mCallbacks(std::move(aCallbacks)) |
1545 | 0 | { |
1546 | 0 | } |
1547 | | |
1548 | | } // namespace dom |
1549 | | } // namespace mozilla |