Bug 1842509 - Remove media.webvtt.regions.enabled pref r=alwu,webidl,smaug,peterv
[gecko.git] / dom / bindings / BindingUtils.h
blobaed06397bcbbae56d53f4efea9af1391bf957c65
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_BindingUtils_h__
8 #define mozilla_dom_BindingUtils_h__
10 #include <type_traits>
12 #include "jsfriendapi.h"
13 #include "js/CharacterEncoding.h"
14 #include "js/Conversions.h"
15 #include "js/experimental/JitInfo.h" // JSJitGetterOp, JSJitInfo
16 #include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy, js::ToWindowProxyIfWindow
17 #include "js/MemoryFunctions.h"
18 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
19 #include "js/RealmOptions.h"
20 #include "js/String.h" // JS::GetLatin1LinearStringChars, JS::GetTwoByteLinearStringChars, JS::GetLinearStringLength, JS::LinearStringHasLatin1Chars, JS::StringHasLatin1Chars
21 #include "js/Wrapper.h"
22 #include "js/Zone.h"
23 #include "mozilla/ArrayUtils.h"
24 #include "mozilla/Array.h"
25 #include "mozilla/Assertions.h"
26 #include "mozilla/DeferredFinalize.h"
27 #include "mozilla/UniquePtr.h"
28 #include "mozilla/dom/BindingCallContext.h"
29 #include "mozilla/dom/BindingDeclarations.h"
30 #include "mozilla/dom/DOMJSClass.h"
31 #include "mozilla/dom/DOMJSProxyHandler.h"
32 #include "mozilla/dom/JSSlots.h"
33 #include "mozilla/dom/NonRefcountedDOMObject.h"
34 #include "mozilla/dom/Nullable.h"
35 #include "mozilla/dom/PrototypeList.h"
36 #include "mozilla/dom/RemoteObjectProxy.h"
37 #include "mozilla/SegmentedVector.h"
38 #include "mozilla/ErrorResult.h"
39 #include "mozilla/Likely.h"
40 #include "mozilla/MemoryReporting.h"
41 #include "nsIGlobalObject.h"
42 #include "nsJSUtils.h"
43 #include "nsISupportsImpl.h"
44 #include "xpcObjectHelper.h"
45 #include "xpcpublic.h"
46 #include "nsIVariant.h"
47 #include "mozilla/dom/FakeString.h"
49 #include "nsWrapperCacheInlines.h"
51 class nsGlobalWindowInner;
52 class nsGlobalWindowOuter;
53 class nsIInterfaceRequestor;
55 namespace mozilla {
57 enum UseCounter : int16_t;
58 enum class UseCounterWorker : int16_t;
60 namespace dom {
61 class CustomElementReactionsStack;
62 class Document;
63 class EventTarget;
64 class MessageManagerGlobal;
65 class ObservableArrayProxyHandler;
66 class DedicatedWorkerGlobalScope;
67 template <typename KeyType, typename ValueType>
68 class Record;
69 class WindowProxyHolder;
71 enum class DeprecatedOperations : uint16_t;
73 nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src,
74 const nsIID& iid, void** ppArg);
76 /** Convert a jsval to an XPCOM pointer. Caller must not assume that src will
77 keep the XPCOM pointer rooted. */
78 template <class Interface>
79 inline nsresult UnwrapArg(JSContext* cx, JS::Handle<JSObject*> src,
80 Interface** ppArg) {
81 return UnwrapArgImpl(cx, src, NS_GET_TEMPLATE_IID(Interface),
82 reinterpret_cast<void**>(ppArg));
85 nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
86 WindowProxyHolder& ppArg);
88 // Returns true if the JSClass is used for DOM objects.
89 inline bool IsDOMClass(const JSClass* clasp) {
90 return clasp->flags & JSCLASS_IS_DOMJSCLASS;
93 // Return true if the JSClass is used for non-proxy DOM objects.
94 inline bool IsNonProxyDOMClass(const JSClass* clasp) {
95 return IsDOMClass(clasp) && clasp->isNativeObject();
98 // Returns true if the JSClass is used for DOM interface and interface
99 // prototype objects.
100 inline bool IsDOMIfaceAndProtoClass(const JSClass* clasp) {
101 return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
104 static_assert(DOM_OBJECT_SLOT == 0,
105 "DOM_OBJECT_SLOT doesn't match the proxy private slot. "
106 "Expect bad things");
107 template <class T>
108 inline T* UnwrapDOMObject(JSObject* obj) {
109 MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
110 "Don't pass non-DOM objects to this function");
112 JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
113 return static_cast<T*>(val.toPrivate());
116 template <class T>
117 inline T* UnwrapPossiblyNotInitializedDOMObject(JSObject* obj) {
118 // This is used by the OjectMoved JSClass hook which can be called before
119 // JS_NewObject has returned and so before we have a chance to set
120 // DOM_OBJECT_SLOT to anything useful.
122 MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
123 "Don't pass non-DOM objects to this function");
125 JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
126 if (val.isUndefined()) {
127 return nullptr;
129 return static_cast<T*>(val.toPrivate());
132 inline const DOMJSClass* GetDOMClass(const JSClass* clasp) {
133 return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
136 inline const DOMJSClass* GetDOMClass(JSObject* obj) {
137 return GetDOMClass(JS::GetClass(obj));
140 inline nsISupports* UnwrapDOMObjectToISupports(JSObject* aObject) {
141 const DOMJSClass* clasp = GetDOMClass(aObject);
142 if (!clasp || !clasp->mDOMObjectIsISupports) {
143 return nullptr;
146 return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
149 inline bool IsDOMObject(JSObject* obj) { return IsDOMClass(JS::GetClass(obj)); }
151 // There are two valid ways to use UNWRAP_OBJECT: Either obj needs to
152 // be a MutableHandle<JSObject*>, or value needs to be a strong-reference
153 // smart pointer type (OwningNonNull or RefPtr or nsCOMPtr), in which case obj
154 // can be anything that converts to JSObject*.
156 // This can't be used with Window, EventTarget, or Location as the "Interface"
157 // argument (and will fail a static_assert if you try to do that). Use
158 // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT to unwrap to those interfaces.
159 #define UNWRAP_OBJECT(Interface, obj, value) \
160 mozilla::dom::binding_detail::UnwrapObjectWithCrossOriginAsserts< \
161 mozilla::dom::prototypes::id::Interface, \
162 mozilla::dom::Interface##_Binding::NativeType>(obj, value)
164 // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT is just like UNWRAP_OBJECT but requires a
165 // JSContext in a Realm that represents "who is doing the unwrapping?" to
166 // properly unwrap the object.
167 #define UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Interface, obj, value, cx) \
168 mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface, \
169 mozilla::dom::Interface##_Binding::NativeType>( \
170 obj, value, cx)
172 // Test whether the given object is an instance of the given interface.
173 #define IS_INSTANCE_OF(Interface, obj) \
174 mozilla::dom::IsInstanceOf<mozilla::dom::prototypes::id::Interface, \
175 mozilla::dom::Interface##_Binding::NativeType>( \
176 obj)
178 // Unwrap the given non-wrapper object. This can be used with any obj that
179 // converts to JSObject*; as long as that JSObject* is live the return value
180 // will be valid.
181 #define UNWRAP_NON_WRAPPER_OBJECT(Interface, obj, value) \
182 mozilla::dom::UnwrapNonWrapperObject< \
183 mozilla::dom::prototypes::id::Interface, \
184 mozilla::dom::Interface##_Binding::NativeType>(obj, value)
186 // Some callers don't want to set an exception when unwrapping fails
187 // (for example, overload resolution uses unwrapping to tell what sort
188 // of thing it's looking at).
189 // U must be something that a T* can be assigned to (e.g. T* or an RefPtr<T>).
191 // The obj argument will be mutated to point to CheckedUnwrap of itself if the
192 // passed-in value is not a DOM object and CheckedUnwrap succeeds.
194 // If mayBeWrapper is true, there are three valid ways to invoke
195 // UnwrapObjectInternal: Either obj needs to be a class wrapping a
196 // MutableHandle<JSObject*>, with an assignment operator that sets the handle to
197 // the given object, or U needs to be a strong-reference smart pointer type
198 // (OwningNonNull or RefPtr or nsCOMPtr), or the value being stored in "value"
199 // must not escape past being tested for falsiness immediately after the
200 // UnwrapObjectInternal call.
202 // If mayBeWrapper is false, obj can just be a JSObject*, and U anything that a
203 // T* can be assigned to.
205 // The cx arg is in practice allowed to be either nullptr or JSContext* or a
206 // BindingCallContext reference. If it's nullptr we will do a
207 // CheckedUnwrapStatic and it's the caller's responsibility to make sure they're
208 // not trying to work with Window or Location objects. Otherwise we'll do a
209 // CheckedUnwrapDynamic. This all only matters if mayBeWrapper is true; if it's
210 // false just pass nullptr for the cx arg.
211 namespace binding_detail {
212 template <class T, bool mayBeWrapper, typename U, typename V, typename CxType>
213 MOZ_ALWAYS_INLINE nsresult UnwrapObjectInternal(V& obj, U& value,
214 prototypes::ID protoID,
215 uint32_t protoDepth,
216 const CxType& cx) {
217 static_assert(std::is_same_v<CxType, JSContext*> ||
218 std::is_same_v<CxType, BindingCallContext> ||
219 std::is_same_v<CxType, decltype(nullptr)>,
220 "Unexpected CxType");
222 /* First check to see whether we have a DOM object */
223 const DOMJSClass* domClass = GetDOMClass(obj);
224 if (domClass) {
225 /* This object is a DOM object. Double-check that it is safely
226 castable to T by checking whether it claims to inherit from the
227 class identified by protoID. */
228 if (domClass->mInterfaceChain[protoDepth] == protoID) {
229 value = UnwrapDOMObject<T>(obj);
230 return NS_OK;
234 /* Maybe we have a security wrapper or outer window? */
235 if (!mayBeWrapper || !js::IsWrapper(obj)) {
236 // For non-cross-origin-accessible methods and properties, remote object
237 // proxies should behave the same as opaque wrappers.
238 if (IsRemoteObjectProxy(obj)) {
239 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
242 /* Not a DOM object, not a wrapper, just bail */
243 return NS_ERROR_XPC_BAD_CONVERT_JS;
246 JSObject* unwrappedObj;
247 if (std::is_same_v<CxType, decltype(nullptr)>) {
248 unwrappedObj = js::CheckedUnwrapStatic(obj);
249 } else {
250 unwrappedObj =
251 js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
253 if (!unwrappedObj) {
254 return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
257 if (std::is_same_v<CxType, decltype(nullptr)>) {
258 // We might still have a windowproxy here. But it shouldn't matter, because
259 // that's not what the caller is looking for, so we're going to fail out
260 // anyway below once we do the recursive call to ourselves with wrapper
261 // unwrapping disabled.
262 MOZ_ASSERT(!js::IsWrapper(unwrappedObj) || js::IsWindowProxy(unwrappedObj));
263 } else {
264 // We shouldn't have a wrapper by now.
265 MOZ_ASSERT(!js::IsWrapper(unwrappedObj));
268 // Recursive call is OK, because now we're using false for mayBeWrapper and
269 // we never reach this code if that boolean is false, so can't keep calling
270 // ourselves.
272 // Unwrap into a temporary pointer, because in general unwrapping into
273 // something of type U might trigger GC (e.g. release the value currently
274 // stored in there, with arbitrary consequences) and invalidate the
275 // "unwrappedObj" pointer.
276 T* tempValue = nullptr;
277 nsresult rv = UnwrapObjectInternal<T, false>(unwrappedObj, tempValue, protoID,
278 protoDepth, nullptr);
279 if (NS_SUCCEEDED(rv)) {
280 // Suppress a hazard related to keeping tempValue alive across
281 // UnwrapObjectInternal, because the analysis can't tell that this function
282 // will not GC if maybeWrapped=False and we've already gone through a level
283 // of unwrapping so unwrappedObj will be !IsWrapper.
284 JS::AutoSuppressGCAnalysis suppress;
286 // It's very important to not update "obj" with the "unwrappedObj" value
287 // until we know the unwrap has succeeded. Otherwise, in a situation in
288 // which we have an overload of object and primitive we could end up
289 // converting to the primitive from the unwrappedObj, whereas we want to do
290 // it from the original object.
291 obj = unwrappedObj;
292 // And now assign to "value"; at this point we don't care if a GC happens
293 // and invalidates unwrappedObj.
294 value = tempValue;
295 return NS_OK;
298 /* It's the wrong sort of DOM object */
299 return NS_ERROR_XPC_BAD_CONVERT_JS;
302 struct MutableObjectHandleWrapper {
303 explicit MutableObjectHandleWrapper(JS::MutableHandle<JSObject*> aHandle)
304 : mHandle(aHandle) {}
306 void operator=(JSObject* aObject) {
307 MOZ_ASSERT(aObject);
308 mHandle.set(aObject);
311 operator JSObject*() const { return mHandle; }
313 private:
314 JS::MutableHandle<JSObject*> mHandle;
317 struct MutableValueHandleWrapper {
318 explicit MutableValueHandleWrapper(JS::MutableHandle<JS::Value> aHandle)
319 : mHandle(aHandle) {}
321 void operator=(JSObject* aObject) {
322 MOZ_ASSERT(aObject);
323 #ifdef ENABLE_RECORD_TUPLE
324 MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*aObject));
325 #endif
326 mHandle.setObject(*aObject);
329 operator JSObject*() const { return &mHandle.toObject(); }
331 private:
332 JS::MutableHandle<JS::Value> mHandle;
335 } // namespace binding_detail
337 // UnwrapObject overloads that ensure we have a MutableHandle to keep it alive.
338 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
339 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JSObject*> obj,
340 U& value, const CxType& cx) {
341 binding_detail::MutableObjectHandleWrapper wrapper(obj);
342 return binding_detail::UnwrapObjectInternal<T, true>(
343 wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
346 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
347 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JS::Value> obj,
348 U& value, const CxType& cx) {
349 MOZ_ASSERT(obj.isObject());
350 binding_detail::MutableValueHandleWrapper wrapper(obj);
351 return binding_detail::UnwrapObjectInternal<T, true>(
352 wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
355 // UnwrapObject overloads that ensure we have a strong ref to keep it alive.
356 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
357 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, RefPtr<U>& value,
358 const CxType& cx) {
359 return binding_detail::UnwrapObjectInternal<T, true>(
360 obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
363 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
364 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, nsCOMPtr<U>& value,
365 const CxType& cx) {
366 return binding_detail::UnwrapObjectInternal<T, true>(
367 obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
370 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
371 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull<U>& value,
372 const CxType& cx) {
373 return binding_detail::UnwrapObjectInternal<T, true>(
374 obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
377 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
378 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, NonNull<U>& value,
379 const CxType& cx) {
380 return binding_detail::UnwrapObjectInternal<T, true>(
381 obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
384 // An UnwrapObject overload that just calls one of the JSObject* ones.
385 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
386 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
387 const CxType& cx) {
388 MOZ_ASSERT(obj.isObject());
389 return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
392 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
393 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj,
394 NonNull<U>& value, const CxType& cx) {
395 MOZ_ASSERT(obj.isObject());
396 return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
399 template <prototypes::ID PrototypeID>
400 MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() {
401 static_assert(PrototypeID != prototypes::id::Window,
402 "Can't do static unwrap of WindowProxy; use "
403 "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
404 "aware version of IS_INSTANCE_OF");
405 static_assert(PrototypeID != prototypes::id::EventTarget,
406 "Can't do static unwrap of WindowProxy (which an EventTarget "
407 "might be); use UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a "
408 "cross-origin-object aware version of IS_INSTANCE_OF");
409 static_assert(PrototypeID != prototypes::id::Location,
410 "Can't do static unwrap of Location; use "
411 "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
412 "aware version of IS_INSTANCE_OF");
415 namespace binding_detail {
416 // This function is just here so we can do some static asserts in a centralized
417 // place instead of putting them in every single UnwrapObject overload.
418 template <prototypes::ID PrototypeID, class T, typename U, typename V>
419 MOZ_ALWAYS_INLINE nsresult UnwrapObjectWithCrossOriginAsserts(V&& obj,
420 U& value) {
421 AssertStaticUnwrapOK<PrototypeID>();
422 return UnwrapObject<PrototypeID, T>(obj, value, nullptr);
424 } // namespace binding_detail
426 template <prototypes::ID PrototypeID, class T>
427 MOZ_ALWAYS_INLINE bool IsInstanceOf(JSObject* obj) {
428 AssertStaticUnwrapOK<PrototypeID>();
429 void* ignored;
430 nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
431 obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
432 return NS_SUCCEEDED(unwrapped);
435 template <prototypes::ID PrototypeID, class T, typename U>
436 MOZ_ALWAYS_INLINE nsresult UnwrapNonWrapperObject(JSObject* obj, U& value) {
437 MOZ_ASSERT(!js::IsWrapper(obj));
438 return binding_detail::UnwrapObjectInternal<T, false>(
439 obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
442 MOZ_ALWAYS_INLINE bool IsConvertibleToDictionary(JS::Handle<JS::Value> val) {
443 return val.isNullOrUndefined() || val.isObject();
446 // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
447 // constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
448 // The end of the prototype objects should be the start of the interface
449 // objects, and the end of the interface objects should be the start of the
450 // named properties objects.
451 static_assert((size_t)constructors::id::_ID_Start ==
452 (size_t)prototypes::id::_ID_Count &&
453 (size_t)namedpropertiesobjects::id::_ID_Start ==
454 (size_t)constructors::id::_ID_Count,
455 "Overlapping or discontiguous indexes.");
456 const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;
458 class ProtoAndIfaceCache {
459 // The caching strategy we use depends on what sort of global we're dealing
460 // with. For a window-like global, we want everything to be as fast as
461 // possible, so we use a flat array, indexed by prototype/constructor ID.
462 // For everything else (e.g. globals for JSMs), space is more important than
463 // speed, so we use a two-level lookup table.
465 class ArrayCache
466 : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount> {
467 public:
468 bool HasEntryInSlot(size_t i) {
469 // Do an explicit call to the Heap<…> bool conversion operator. Because
470 // that operator is marked explicit we'd otherwise end up doing an
471 // implicit cast to JSObject* first, causing an unnecessary call to
472 // exposeToActiveJS().
473 return bool((*this)[i]);
476 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { return (*this)[i]; }
478 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { return (*this)[i]; }
480 void Trace(JSTracer* aTracer) {
481 for (size_t i = 0; i < ArrayLength(*this); ++i) {
482 JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
486 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
487 return aMallocSizeOf(this);
491 class PageTableCache {
492 public:
493 PageTableCache() { memset(mPages.begin(), 0, sizeof(mPages)); }
495 ~PageTableCache() {
496 for (size_t i = 0; i < ArrayLength(mPages); ++i) {
497 delete mPages[i];
501 bool HasEntryInSlot(size_t i) {
502 MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
503 size_t pageIndex = i / kPageSize;
504 size_t leafIndex = i % kPageSize;
505 Page* p = mPages[pageIndex];
506 if (!p) {
507 return false;
509 // Do an explicit call to the Heap<…> bool conversion operator. Because
510 // that operator is marked explicit we'd otherwise end up doing an
511 // implicit cast to JSObject* first, causing an unnecessary call to
512 // exposeToActiveJS().
513 return bool((*p)[leafIndex]);
516 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
517 MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
518 size_t pageIndex = i / kPageSize;
519 size_t leafIndex = i % kPageSize;
520 Page* p = mPages[pageIndex];
521 if (!p) {
522 p = new Page;
523 mPages[pageIndex] = p;
525 return (*p)[leafIndex];
528 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
529 MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
530 size_t pageIndex = i / kPageSize;
531 size_t leafIndex = i % kPageSize;
532 Page* p = mPages[pageIndex];
533 MOZ_ASSERT(p);
534 return (*p)[leafIndex];
537 void Trace(JSTracer* trc) {
538 for (size_t i = 0; i < ArrayLength(mPages); ++i) {
539 Page* p = mPages[i];
540 if (p) {
541 for (size_t j = 0; j < ArrayLength(*p); ++j) {
542 JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
548 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
549 size_t n = aMallocSizeOf(this);
550 for (size_t i = 0; i < ArrayLength(mPages); ++i) {
551 n += aMallocSizeOf(mPages[i]);
553 return n;
556 private:
557 static const size_t kPageSize = 16;
558 typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
559 static const size_t kNPages =
560 kProtoAndIfaceCacheCount / kPageSize +
561 size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
562 Array<Page*, kNPages> mPages;
565 public:
566 enum Kind { WindowLike, NonWindowLike };
568 explicit ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
569 MOZ_COUNT_CTOR(ProtoAndIfaceCache);
570 if (aKind == WindowLike) {
571 mArrayCache = new ArrayCache();
572 } else {
573 mPageTableCache = new PageTableCache();
577 ~ProtoAndIfaceCache() {
578 if (mKind == WindowLike) {
579 delete mArrayCache;
580 } else {
581 delete mPageTableCache;
583 MOZ_COUNT_DTOR(ProtoAndIfaceCache);
586 #define FORWARD_OPERATION(opName, args) \
587 do { \
588 if (mKind == WindowLike) { \
589 return mArrayCache->opName args; \
590 } else { \
591 return mPageTableCache->opName args; \
593 } while (0)
595 // Return whether slot i contains an object. This doesn't return the object
596 // itself because in practice consumers just want to know whether it's there
597 // or not, and that doesn't require barriering, which returning the object
598 // pointer does.
599 bool HasEntryInSlot(size_t i) { FORWARD_OPERATION(HasEntryInSlot, (i)); }
601 // Return a reference to slot i, creating it if necessary. There
602 // may not be an object in the returned slot.
603 JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
604 FORWARD_OPERATION(EntrySlotOrCreate, (i));
607 // Return a reference to slot i, which is guaranteed to already
608 // exist. There may not be an object in the slot, if prototype and
609 // constructor initialization for one of our bindings failed.
610 JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
611 FORWARD_OPERATION(EntrySlotMustExist, (i));
614 void Trace(JSTracer* aTracer) { FORWARD_OPERATION(Trace, (aTracer)); }
616 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
617 size_t n = aMallocSizeOf(this);
618 n += (mKind == WindowLike
619 ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
620 : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
621 return n;
623 #undef FORWARD_OPERATION
625 private:
626 union {
627 ArrayCache* mArrayCache;
628 PageTableCache* mPageTableCache;
630 Kind mKind;
633 inline void AllocateProtoAndIfaceCache(JSObject* obj,
634 ProtoAndIfaceCache::Kind aKind) {
635 MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
636 MOZ_ASSERT(JS::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
638 ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
640 JS::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
641 JS::PrivateValue(protoAndIfaceCache));
644 #ifdef DEBUG
645 struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer {
646 bool ok;
648 explicit VerifyTraceProtoAndIfaceCacheCalledTracer(JSContext* cx)
649 : JS::CallbackTracer(cx, JS::TracerKind::VerifyTraceProtoAndIface),
650 ok(false) {}
652 void onChild(JS::GCCellPtr, const char* name) override {
653 // We don't do anything here, we only want to verify that
654 // TraceProtoAndIfaceCache was called.
657 #endif
659 inline void TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj) {
660 MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
662 #ifdef DEBUG
663 if (trc->kind() == JS::TracerKind::VerifyTraceProtoAndIface) {
664 // We don't do anything here, we only want to verify that
665 // TraceProtoAndIfaceCache was called.
666 static_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok = true;
667 return;
669 #endif
671 if (!DOMGlobalHasProtoAndIFaceCache(obj)) return;
672 ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
673 protoAndIfaceCache->Trace(trc);
676 inline void DestroyProtoAndIfaceCache(JSObject* obj) {
677 MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
679 if (!DOMGlobalHasProtoAndIFaceCache(obj)) {
680 return;
683 ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
685 delete protoAndIfaceCache;
689 * Add constants to an object.
691 bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
692 const ConstantSpec* cs);
694 struct JSNativeHolder {
695 JSNative mNative;
696 const NativePropertyHooks* mPropertyHooks;
699 struct LegacyFactoryFunction {
700 const char* mName;
701 const JSNativeHolder mHolder;
702 unsigned mNargs;
705 // clang-format off
707 * Create a DOM interface object (if constructorClass is non-null) and/or a
708 * DOM interface prototype object (if protoClass is non-null).
710 * global is used as the parent of the interface object and the interface
711 * prototype object
712 * protoProto is the prototype to use for the interface prototype object.
713 * interfaceProto is the prototype to use for the interface object. This can be
714 * null if both constructorClass and constructor are null (as in,
715 * if we're not creating an interface object at all).
716 * protoClass is the JSClass to use for the interface prototype object.
717 * This is null if we should not create an interface prototype
718 * object.
719 * protoCache a pointer to a JSObject pointer where we should cache the
720 * interface prototype object. This must be null if protoClass is and
721 * vice versa.
722 * constructorClass is the JSClass to use for the interface object.
723 * This is null if we should not create an interface object or
724 * if it should be a function object.
725 * constructor holds the JSNative to back the interface object which should be a
726 * Function, unless constructorClass is non-null in which case it is
727 * ignored. If this is null and constructorClass is also null then
728 * we should not create an interface object at all.
729 * ctorNargs is the length of the constructor function; 0 if no constructor
730 * isConstructorChromeOnly if true, the constructor is ChromeOnly.
731 * constructorCache a pointer to a JSObject pointer where we should cache the
732 * interface object. This must be null if both constructorClass
733 * and constructor are null, and non-null otherwise.
734 * properties contains the methods, attributes and constants to be defined on
735 * objects in any compartment.
736 * chromeProperties contains the methods, attributes and constants to be defined
737 * on objects in chrome compartments. This must be null if the
738 * interface doesn't have any ChromeOnly properties or if the
739 * object is being created in non-chrome compartment.
740 * name the name to use for 1) the WebIDL class string, which is the value
741 * that's used for @@toStringTag, 2) the name property for interface
742 * objects and 3) the property on the global object that would be set to
743 * the interface object. In general this is the interface identifier.
744 * LegacyNamespace would expect something different for 1), but we don't
745 * support that. The class string for default iterator objects is not
746 * usable as 2) or 3), but default iterator objects don't have an interface
747 * object.
748 * defineOnGlobal controls whether properties should be defined on the given
749 * global for the interface object (if any) and named
750 * constructors (if any) for this interface. This can be
751 * false in situations where we want the properties to only
752 * appear on privileged Xrays but not on the unprivileged
753 * underlying global.
754 * unscopableNames if not null it points to a null-terminated list of const
755 * char* names of the unscopable properties for this interface.
756 * isGlobal if true, we're creating interface objects for a [Global] interface,
757 * and hence shouldn't define properties on the prototype object.
758 * legacyWindowAliases if not null it points to a null-terminated list of const
759 * char* names of the legacy window aliases for this
760 * interface.
762 * At least one of protoClass, constructorClass or constructor should be
763 * non-null. If constructorClass or constructor are non-null, the resulting
764 * interface object will be defined on the given global with property name
765 * |name|, which must also be non-null.
767 // clang-format on
768 void CreateInterfaceObjects(
769 JSContext* cx, JS::Handle<JSObject*> global,
770 JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
771 JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> constructorProto,
772 const DOMIfaceJSClass* constructorClass, unsigned ctorNargs,
773 bool isConstructorChromeOnly,
774 const LegacyFactoryFunction* legacyFactoryFunctions,
775 JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
776 const NativeProperties* chromeOnlyProperties, const char* name,
777 bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
778 const char* const* legacyWindowAliases, bool isNamespace);
781 * Define the properties (regular and chrome-only) on obj.
783 * obj the object to install the properties on. This should be the interface
784 * prototype object for regular interfaces and the instance object for
785 * interfaces marked with Global.
786 * properties contains the methods, attributes and constants to be defined on
787 * objects in any compartment.
788 * chromeProperties contains the methods, attributes and constants to be defined
789 * on objects in chrome compartments. This must be null if the
790 * interface doesn't have any ChromeOnly properties or if the
791 * object is being created in non-chrome compartment.
793 bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
794 const NativeProperties* properties,
795 const NativeProperties* chromeOnlyProperties);
798 * Define the legacy unforgeable methods on an object.
800 bool DefineLegacyUnforgeableMethods(
801 JSContext* cx, JS::Handle<JSObject*> obj,
802 const Prefable<const JSFunctionSpec>* props);
805 * Define the legacy unforgeable attributes on an object.
807 bool DefineLegacyUnforgeableAttributes(
808 JSContext* cx, JS::Handle<JSObject*> obj,
809 const Prefable<const JSPropertySpec>* props);
811 #define HAS_MEMBER_TYPEDEFS \
812 private: \
813 typedef char yes[1]; \
814 typedef char no[2]
816 #ifdef _MSC_VER
817 # define HAS_MEMBER_CHECK(_name) \
818 template <typename V> \
819 static yes& Check##_name(char(*)[(&V::_name == 0) + 1])
820 #else
821 # define HAS_MEMBER_CHECK(_name) \
822 template <typename V> \
823 static yes& Check##_name(char(*)[sizeof(&V::_name) + 1])
824 #endif
826 #define HAS_MEMBER(_memberName, _valueName) \
827 private: \
828 HAS_MEMBER_CHECK(_memberName); \
829 template <typename V> \
830 static no& Check##_memberName(...); \
832 public: \
833 static bool const _valueName = \
834 sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)
836 template <class T>
837 struct NativeHasMember {
838 HAS_MEMBER_TYPEDEFS;
840 HAS_MEMBER(GetParentObject, GetParentObject);
841 HAS_MEMBER(WrapObject, WrapObject);
844 template <class T>
845 struct IsSmartPtr {
846 HAS_MEMBER_TYPEDEFS;
848 HAS_MEMBER(get, value);
851 template <class T>
852 struct IsRefcounted {
853 HAS_MEMBER_TYPEDEFS;
855 HAS_MEMBER(AddRef, HasAddref);
856 HAS_MEMBER(Release, HasRelease);
858 public:
859 static bool const value = HasAddref && HasRelease;
861 private:
862 // This struct only works if T is fully declared (not just forward declared).
863 // The std::is_base_of check will ensure that, we don't really need it for any
864 // other reason (the static assert will of course always be true).
865 static_assert(!std::is_base_of<nsISupports, T>::value || IsRefcounted::value,
866 "Classes derived from nsISupports are refcounted!");
869 #undef HAS_MEMBER
870 #undef HAS_MEMBER_CHECK
871 #undef HAS_MEMBER_TYPEDEFS
873 #ifdef DEBUG
874 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
875 struct CheckWrapperCacheCast {
876 static bool Check() {
877 return reinterpret_cast<uintptr_t>(
878 static_cast<nsWrapperCache*>(reinterpret_cast<T*>(1))) == 1;
881 template <class T>
882 struct CheckWrapperCacheCast<T, true> {
883 static bool Check() { return true; }
885 #endif
887 inline bool TryToOuterize(JS::MutableHandle<JS::Value> rval) {
888 #ifdef ENABLE_RECORD_TUPLE
889 if (rval.isExtendedPrimitive()) {
890 return true;
892 #endif
893 MOZ_ASSERT(rval.isObject());
894 if (js::IsWindow(&rval.toObject())) {
895 JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
896 MOZ_ASSERT(obj);
897 rval.set(JS::ObjectValue(*obj));
900 return true;
903 inline bool TryToOuterize(JS::MutableHandle<JSObject*> obj) {
904 if (js::IsWindow(obj)) {
905 JSObject* proxy = js::ToWindowProxyIfWindow(obj);
906 MOZ_ASSERT(proxy);
907 obj.set(proxy);
910 return true;
913 // Make sure to wrap the given string value into the right compartment, as
914 // needed.
915 MOZ_ALWAYS_INLINE
916 bool MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
917 MOZ_ASSERT(rval.isString());
918 JSString* str = rval.toString();
919 if (JS::GetStringZone(str) != js::GetContextZone(cx)) {
920 return JS_WrapValue(cx, rval);
922 return true;
925 // Make sure to wrap the given object value into the right compartment as
926 // needed. This will work correctly, but possibly slowly, on all objects.
927 MOZ_ALWAYS_INLINE
928 bool MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
929 MOZ_ASSERT(rval.hasObjectPayload());
931 // Cross-compartment always requires wrapping.
932 JSObject* obj = &rval.getObjectPayload();
933 if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
934 return JS_WrapValue(cx, rval);
937 // We're same-compartment, but we might still need to outerize if we
938 // have a Window.
939 return TryToOuterize(rval);
942 // Like MaybeWrapObjectValue, but working with a
943 // JS::MutableHandle<JSObject*> which must be non-null.
944 MOZ_ALWAYS_INLINE
945 bool MaybeWrapObject(JSContext* cx, JS::MutableHandle<JSObject*> obj) {
946 if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
947 return JS_WrapObject(cx, obj);
950 // We're same-compartment, but we might still need to outerize if we
951 // have a Window.
952 return TryToOuterize(obj);
955 // Like MaybeWrapObjectValue, but also allows null
956 MOZ_ALWAYS_INLINE
957 bool MaybeWrapObjectOrNullValue(JSContext* cx,
958 JS::MutableHandle<JS::Value> rval) {
959 MOZ_ASSERT(rval.isObjectOrNull());
960 if (rval.isNull()) {
961 return true;
963 return MaybeWrapObjectValue(cx, rval);
966 // Wrapping for objects that are known to not be DOM objects
967 MOZ_ALWAYS_INLINE
968 bool MaybeWrapNonDOMObjectValue(JSContext* cx,
969 JS::MutableHandle<JS::Value> rval) {
970 MOZ_ASSERT(rval.isObject());
971 // Compared to MaybeWrapObjectValue we just skip the TryToOuterize call. The
972 // only reason it would be needed is if we have a Window object, which would
973 // have a DOM class. Assert that we don't have any DOM-class objects coming
974 // through here.
975 MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
977 JSObject* obj = &rval.toObject();
978 if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
979 return true;
981 return JS_WrapValue(cx, rval);
984 // Like MaybeWrapNonDOMObjectValue but allows null
985 MOZ_ALWAYS_INLINE
986 bool MaybeWrapNonDOMObjectOrNullValue(JSContext* cx,
987 JS::MutableHandle<JS::Value> rval) {
988 MOZ_ASSERT(rval.isObjectOrNull());
989 if (rval.isNull()) {
990 return true;
992 return MaybeWrapNonDOMObjectValue(cx, rval);
995 // If rval is a gcthing and is not in the compartment of cx, wrap rval
996 // into the compartment of cx (typically by replacing it with an Xray or
997 // cross-compartment wrapper around the original object).
998 MOZ_ALWAYS_INLINE bool MaybeWrapValue(JSContext* cx,
999 JS::MutableHandle<JS::Value> rval) {
1000 if (rval.isGCThing()) {
1001 if (rval.isString()) {
1002 return MaybeWrapStringValue(cx, rval);
1004 if (rval.hasObjectPayload()) {
1005 return MaybeWrapObjectValue(cx, rval);
1007 // This could be optimized by checking the zone first, similar to
1008 // the way strings are handled. At present, this is used primarily
1009 // for structured cloning, so avoiding the overhead of JS_WrapValue
1010 // calls is less important than for other types.
1011 if (rval.isBigInt()) {
1012 return JS_WrapValue(cx, rval);
1014 MOZ_ASSERT(rval.isSymbol());
1015 JS_MarkCrossZoneId(cx, JS::PropertyKey::Symbol(rval.toSymbol()));
1017 return true;
1020 namespace binding_detail {
1021 enum GetOrCreateReflectorWrapBehavior {
1022 eWrapIntoContextCompartment,
1023 eDontWrapIntoContextCompartment
1026 template <class T>
1027 struct TypeNeedsOuterization {
1028 // We only need to outerize Window objects, so anything inheriting from
1029 // nsGlobalWindow (which inherits from EventTarget itself).
1030 static const bool value = std::is_base_of<nsGlobalWindowInner, T>::value ||
1031 std::is_base_of<nsGlobalWindowOuter, T>::value ||
1032 std::is_same_v<EventTarget, T>;
1035 #ifdef DEBUG
1036 template <typename T, bool isISupports = std::is_base_of<nsISupports, T>::value>
1037 struct CheckWrapperCacheTracing {
1038 static inline void Check(T* aObject) {}
1041 template <typename T>
1042 struct CheckWrapperCacheTracing<T, true> {
1043 static void Check(T* aObject) {
1044 // Rooting analysis thinks QueryInterface may GC, but we're dealing with
1045 // a subset of QueryInterface, C++ only types here.
1046 JS::AutoSuppressGCAnalysis nogc;
1048 nsWrapperCache* wrapperCacheFromQI = nullptr;
1049 aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
1050 reinterpret_cast<void**>(&wrapperCacheFromQI));
1052 MOZ_ASSERT(wrapperCacheFromQI,
1053 "Missing nsWrapperCache from QueryInterface implementation?");
1055 if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
1056 // Can't assert that we trace the wrapper, since we don't have any
1057 // wrapper to trace.
1058 return;
1061 nsISupports* ccISupports = nullptr;
1062 aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
1063 reinterpret_cast<void**>(&ccISupports));
1064 MOZ_ASSERT(ccISupports,
1065 "nsWrapperCache object which isn't cycle collectable?");
1067 nsXPCOMCycleCollectionParticipant* participant = nullptr;
1068 CallQueryInterface(ccISupports, &participant);
1069 MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
1071 wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
1075 void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
1076 JS::Handle<JSObject*> aGivenProto);
1077 #endif // DEBUG
1079 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
1080 MOZ_ALWAYS_INLINE bool DoGetOrCreateDOMReflector(
1081 JSContext* cx, T* value, JS::Handle<JSObject*> givenProto,
1082 JS::MutableHandle<JS::Value> rval) {
1083 MOZ_ASSERT(value);
1084 MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
1085 JSObject* obj = value->GetWrapper();
1086 if (obj) {
1087 #ifdef DEBUG
1088 AssertReflectorHasGivenProto(cx, obj, givenProto);
1089 // Have to reget obj because AssertReflectorHasGivenProto can
1090 // trigger gc so the pointer may now be invalid.
1091 obj = value->GetWrapper();
1092 #endif
1093 } else {
1094 obj = value->WrapObject(cx, givenProto);
1095 if (!obj) {
1096 // At this point, obj is null, so just return false.
1097 // Callers seem to be testing JS_IsExceptionPending(cx) to
1098 // figure out whether WrapObject() threw.
1099 return false;
1102 #ifdef DEBUG
1103 if (std::is_base_of<nsWrapperCache, T>::value) {
1104 CheckWrapperCacheTracing<T>::Check(value);
1106 #endif
1109 #ifdef DEBUG
1110 const DOMJSClass* clasp = GetDOMClass(obj);
1111 // clasp can be null if the cache contained a non-DOM object.
1112 if (clasp) {
1113 // Some sanity asserts about our object. Specifically:
1114 // 1) If our class claims we're nsISupports, we better be nsISupports
1115 // XXXbz ideally, we could assert that reinterpret_cast to nsISupports
1116 // does the right thing, but I don't see a way to do it. :(
1117 // 2) If our class doesn't claim we're nsISupports we better be
1118 // reinterpret_castable to nsWrapperCache.
1119 MOZ_ASSERT(clasp, "What happened here?");
1120 MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports,
1121 (std::is_base_of<nsISupports, T>::value));
1122 MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
1124 #endif
1126 #ifdef ENABLE_RECORD_TUPLE
1127 MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*obj));
1128 #endif
1129 rval.set(JS::ObjectValue(*obj));
1131 if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
1132 return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
1135 if (wrapBehavior == eDontWrapIntoContextCompartment) {
1136 if (TypeNeedsOuterization<T>::value) {
1137 JSAutoRealm ar(cx, obj);
1138 return TryToOuterize(rval);
1141 return true;
1144 return JS_WrapValue(cx, rval);
1147 } // namespace binding_detail
1149 // Create a JSObject wrapping "value", if there isn't one already, and store it
1150 // in rval. "value" must be a concrete class that implements a
1151 // GetWrapperPreserveColor() which can return its existing wrapper, if any, and
1152 // a WrapObject() which will try to create a wrapper. Typically, this is done by
1153 // having "value" inherit from nsWrapperCache.
1155 // The value stored in rval will be ready to be exposed to whatever JS
1156 // is running on cx right now. In particular, it will be in the
1157 // compartment of cx, and outerized as needed.
1158 template <class T>
1159 MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflector(
1160 JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval,
1161 JS::Handle<JSObject*> givenProto = nullptr) {
1162 using namespace binding_detail;
1163 return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(
1164 cx, value, givenProto, rval);
1167 // Like GetOrCreateDOMReflector but doesn't wrap into the context compartment,
1168 // and hence does not actually require cx to be in a compartment.
1169 template <class T>
1170 MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflectorNoWrap(
1171 JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval) {
1172 using namespace binding_detail;
1173 return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(
1174 cx, value, nullptr, rval);
1177 // Helper for different overloadings of WrapNewBindingNonWrapperCachedObject()
1178 inline bool FinishWrapping(JSContext* cx, JS::Handle<JSObject*> obj,
1179 JS::MutableHandle<JS::Value> rval) {
1180 #ifdef ENABLE_RECORD_TUPLE
1181 // If calling an (object) value's WrapObject() method returned a record/tuple,
1182 // then something is very wrong.
1183 MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*obj));
1184 #endif
1186 // We can end up here in all sorts of compartments, per comments in
1187 // WrapNewBindingNonWrapperCachedObject(). Make sure to JS_WrapValue!
1188 rval.set(JS::ObjectValue(*obj));
1189 return MaybeWrapObjectValue(cx, rval);
1192 // Create a JSObject wrapping "value", for cases when "value" is a
1193 // non-wrapper-cached object using WebIDL bindings. "value" must implement a
1194 // WrapObject() method taking a JSContext and a prototype (possibly null) and
1195 // returning the resulting object via a MutableHandle<JSObject*> outparam.
1196 template <class T>
1197 inline bool WrapNewBindingNonWrapperCachedObject(
1198 JSContext* cx, JS::Handle<JSObject*> scopeArg, T* value,
1199 JS::MutableHandle<JS::Value> rval,
1200 JS::Handle<JSObject*> givenProto = nullptr) {
1201 static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
1202 MOZ_ASSERT(value);
1203 // We try to wrap in the realm of the underlying object of "scope"
1204 JS::Rooted<JSObject*> obj(cx);
1206 // scope for the JSAutoRealm so that we restore the realm
1207 // before we call JS_WrapValue.
1208 Maybe<JSAutoRealm> ar;
1209 // Maybe<Handle> doesn't so much work, and in any case, adding
1210 // more Maybe (one for a Rooted and one for a Handle) adds more
1211 // code (and branches!) than just adding a single rooted.
1212 JS::Rooted<JSObject*> scope(cx, scopeArg);
1213 JS::Rooted<JSObject*> proto(cx, givenProto);
1214 if (js::IsWrapper(scope)) {
1215 // We are working in the Realm of cx and will be producing our reflector
1216 // there, so we need to succeed if that realm has access to the scope.
1217 scope =
1218 js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
1219 if (!scope) return false;
1220 ar.emplace(cx, scope);
1221 if (!JS_WrapObject(cx, &proto)) {
1222 return false;
1224 } else {
1225 // cx and scope are same-compartment, but they might still be
1226 // different-Realm. Enter the Realm of scope, since that's
1227 // where we want to create our object.
1228 ar.emplace(cx, scope);
1231 MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
1232 MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
1233 if (!value->WrapObject(cx, proto, &obj)) {
1234 return false;
1238 return FinishWrapping(cx, obj, rval);
1241 // Create a JSObject wrapping "value", for cases when "value" is a
1242 // non-wrapper-cached owned object using WebIDL bindings. "value" must
1243 // implement a WrapObject() method taking a taking a JSContext and a prototype
1244 // (possibly null) and returning two pieces of information: the resulting object
1245 // via a MutableHandle<JSObject*> outparam and a boolean return value that is
1246 // true if the JSObject took ownership
1247 template <class T>
1248 inline bool WrapNewBindingNonWrapperCachedObject(
1249 JSContext* cx, JS::Handle<JSObject*> scopeArg, UniquePtr<T>& value,
1250 JS::MutableHandle<JS::Value> rval,
1251 JS::Handle<JSObject*> givenProto = nullptr) {
1252 static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
1253 // We do a runtime check on value, because otherwise we might in
1254 // fact end up wrapping a null and invoking methods on it later.
1255 if (!value) {
1256 MOZ_CRASH("Don't try to wrap null objects");
1258 // We try to wrap in the realm of the underlying object of "scope"
1259 JS::Rooted<JSObject*> obj(cx);
1261 // scope for the JSAutoRealm so that we restore the realm
1262 // before we call JS_WrapValue.
1263 Maybe<JSAutoRealm> ar;
1264 // Maybe<Handle> doesn't so much work, and in any case, adding
1265 // more Maybe (one for a Rooted and one for a Handle) adds more
1266 // code (and branches!) than just adding a single rooted.
1267 JS::Rooted<JSObject*> scope(cx, scopeArg);
1268 JS::Rooted<JSObject*> proto(cx, givenProto);
1269 if (js::IsWrapper(scope)) {
1270 // We are working in the Realm of cx and will be producing our reflector
1271 // there, so we need to succeed if that realm has access to the scope.
1272 scope =
1273 js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
1274 if (!scope) return false;
1275 ar.emplace(cx, scope);
1276 if (!JS_WrapObject(cx, &proto)) {
1277 return false;
1279 } else {
1280 // cx and scope are same-compartment, but they might still be
1281 // different-Realm. Enter the Realm of scope, since that's
1282 // where we want to create our object.
1283 ar.emplace(cx, scope);
1286 MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
1287 MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
1288 if (!value->WrapObject(cx, proto, &obj)) {
1289 return false;
1292 // JS object took ownership
1293 Unused << value.release();
1296 return FinishWrapping(cx, obj, rval);
1299 // Helper for smart pointers (nsRefPtr/nsCOMPtr).
1300 template <template <typename> class SmartPtr, typename T,
1301 typename U = std::enable_if_t<IsRefcounted<T>::value, T>,
1302 typename V = std::enable_if_t<IsSmartPtr<SmartPtr<T>>::value, T>>
1303 inline bool WrapNewBindingNonWrapperCachedObject(
1304 JSContext* cx, JS::Handle<JSObject*> scope, const SmartPtr<T>& value,
1305 JS::MutableHandle<JS::Value> rval,
1306 JS::Handle<JSObject*> givenProto = nullptr) {
1307 return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
1308 givenProto);
1311 // Helper for object references (as opposed to pointers).
1312 template <typename T, typename U = std::enable_if_t<!IsSmartPtr<T>::value, T>>
1313 inline bool WrapNewBindingNonWrapperCachedObject(
1314 JSContext* cx, JS::Handle<JSObject*> scope, T& value,
1315 JS::MutableHandle<JS::Value> rval,
1316 JS::Handle<JSObject*> givenProto = nullptr) {
1317 return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
1318 givenProto);
1321 template <bool Fatal>
1322 inline bool EnumValueNotFound(BindingCallContext& cx, JS::Handle<JSString*> str,
1323 const char* type, const char* sourceDescription);
1325 template <>
1326 inline bool EnumValueNotFound<false>(BindingCallContext& cx,
1327 JS::Handle<JSString*> str,
1328 const char* type,
1329 const char* sourceDescription) {
1330 // TODO: Log a warning to the console.
1331 return true;
1334 template <>
1335 inline bool EnumValueNotFound<true>(BindingCallContext& cx,
1336 JS::Handle<JSString*> str, const char* type,
1337 const char* sourceDescription) {
1338 JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
1339 if (!deflated) {
1340 return false;
1342 return cx.ThrowErrorMessage<MSG_INVALID_ENUM_VALUE>(sourceDescription,
1343 deflated.get(), type);
1346 template <typename CharT>
1347 inline int FindEnumStringIndexImpl(const CharT* chars, size_t length,
1348 const EnumEntry* values) {
1349 int i = 0;
1350 for (const EnumEntry* value = values; value->value; ++value, ++i) {
1351 if (length != value->length) {
1352 continue;
1355 bool equal = true;
1356 const char* val = value->value;
1357 for (size_t j = 0; j != length; ++j) {
1358 if (unsigned(val[j]) != unsigned(chars[j])) {
1359 equal = false;
1360 break;
1364 if (equal) {
1365 return i;
1369 return -1;
1372 template <bool InvalidValueFatal>
1373 inline bool FindEnumStringIndex(BindingCallContext& cx, JS::Handle<JS::Value> v,
1374 const EnumEntry* values, const char* type,
1375 const char* sourceDescription, int* index) {
1376 // JS_StringEqualsAscii is slow as molasses, so don't use it here.
1377 JS::Rooted<JSString*> str(cx, JS::ToString(cx, v));
1378 if (!str) {
1379 return false;
1383 size_t length;
1384 JS::AutoCheckCannotGC nogc;
1385 if (JS::StringHasLatin1Chars(str)) {
1386 const JS::Latin1Char* chars =
1387 JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length);
1388 if (!chars) {
1389 return false;
1391 *index = FindEnumStringIndexImpl(chars, length, values);
1392 } else {
1393 const char16_t* chars =
1394 JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length);
1395 if (!chars) {
1396 return false;
1398 *index = FindEnumStringIndexImpl(chars, length, values);
1400 if (*index >= 0) {
1401 return true;
1405 return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
1408 inline nsWrapperCache* GetWrapperCache(const ParentObject& aParentObject) {
1409 return aParentObject.mWrapperCache;
1412 template <class T>
1413 inline T* GetParentPointer(T* aObject) {
1414 return aObject;
1417 inline nsISupports* GetParentPointer(const ParentObject& aObject) {
1418 return aObject.mObject;
1421 template <typename T>
1422 inline mozilla::dom::ReflectionScope GetReflectionScope(T* aParentObject) {
1423 return mozilla::dom::ReflectionScope::Content;
1426 inline mozilla::dom::ReflectionScope GetReflectionScope(
1427 const ParentObject& aParentObject) {
1428 return aParentObject.mReflectionScope;
1431 template <class T>
1432 inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
1433 MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
1434 (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
1435 cache->ClearWrapper(obj);
1438 template <class T>
1439 inline void ClearWrapper(T* p, void*, JSObject* obj) {
1440 // QueryInterface to nsWrapperCache can't GC, we hope.
1441 JS::AutoSuppressGCAnalysis nogc;
1443 nsWrapperCache* cache;
1444 CallQueryInterface(p, &cache);
1445 ClearWrapper(p, cache, obj);
1448 template <class T>
1449 inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj,
1450 const JSObject* old) {
1451 JS::AutoAssertGCCallback inCallback;
1452 cache->UpdateWrapper(obj, old);
1455 template <class T>
1456 inline void UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old) {
1457 JS::AutoAssertGCCallback inCallback;
1458 nsWrapperCache* cache;
1459 CallQueryInterface(p, &cache);
1460 UpdateWrapper(p, cache, obj, old);
1463 // Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
1464 // Return true if we successfully preserved the wrapper, or there is no wrapper
1465 // to preserve. In the latter case we don't need to preserve the wrapper,
1466 // because the object can only be obtained by JS once, or they cannot be
1467 // meaningfully owned from the native side.
1469 // This operation will return false only for non-nsISupports cycle-collected
1470 // objects, because we cannot determine if they are wrappercached or not.
1471 bool TryPreserveWrapper(JS::Handle<JSObject*> obj);
1473 bool HasReleasedWrapper(JS::Handle<JSObject*> obj);
1475 // Can only be called with a DOM JSClass.
1476 bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
1477 uint32_t depth);
1479 // Only set allowNativeWrapper to false if you really know you need it; if in
1480 // doubt use true. Setting it to false disables security wrappers.
1481 bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
1482 xpcObjectHelper& helper, const nsIID* iid,
1483 bool allowNativeWrapper,
1484 JS::MutableHandle<JS::Value> rval);
1486 // Special-cased wrapping for variants
1487 bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
1488 JS::MutableHandle<JS::Value> aRetval);
1490 // Wrap an object "p" which is not using WebIDL bindings yet. This _will_
1491 // actually work on WebIDL binding objects that are wrappercached, but will be
1492 // much slower than GetOrCreateDOMReflector. "cache" must either be null or be
1493 // the nsWrapperCache for "p".
1494 template <class T>
1495 inline bool WrapObject(JSContext* cx, T* p, nsWrapperCache* cache,
1496 const nsIID* iid, JS::MutableHandle<JS::Value> rval) {
1497 if (xpc_FastGetCachedWrapper(cx, cache, rval)) return true;
1498 xpcObjectHelper helper(ToSupports(p), cache);
1499 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
1500 return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
1503 // A specialization of the above for nsIVariant, because that needs to
1504 // do something different.
1505 template <>
1506 inline bool WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
1507 nsWrapperCache* cache, const nsIID* iid,
1508 JS::MutableHandle<JS::Value> rval) {
1509 MOZ_ASSERT(iid);
1510 MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
1511 return VariantToJsval(cx, p, rval);
1514 // Wrap an object "p" which is not using WebIDL bindings yet. Just like the
1515 // variant that takes an nsWrapperCache above, but will try to auto-derive the
1516 // nsWrapperCache* from "p".
1517 template <class T>
1518 inline bool WrapObject(JSContext* cx, T* p, const nsIID* iid,
1519 JS::MutableHandle<JS::Value> rval) {
1520 return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
1523 // Just like the WrapObject above, but without requiring you to pick which
1524 // interface you're wrapping as. This should only be used for objects that have
1525 // classinfo, for which it doesn't matter what IID is used to wrap.
1526 template <class T>
1527 inline bool WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval) {
1528 return WrapObject(cx, p, nullptr, rval);
1531 // Helper to make it possible to wrap directly out of an nsCOMPtr
1532 template <class T>
1533 inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p, const nsIID* iid,
1534 JS::MutableHandle<JS::Value> rval) {
1535 return WrapObject(cx, p.get(), iid, rval);
1538 // Helper to make it possible to wrap directly out of an nsCOMPtr
1539 template <class T>
1540 inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
1541 JS::MutableHandle<JS::Value> rval) {
1542 return WrapObject(cx, p, nullptr, rval);
1545 // Helper to make it possible to wrap directly out of an nsRefPtr
1546 template <class T>
1547 inline bool WrapObject(JSContext* cx, const RefPtr<T>& p, const nsIID* iid,
1548 JS::MutableHandle<JS::Value> rval) {
1549 return WrapObject(cx, p.get(), iid, rval);
1552 // Helper to make it possible to wrap directly out of an nsRefPtr
1553 template <class T>
1554 inline bool WrapObject(JSContext* cx, const RefPtr<T>& p,
1555 JS::MutableHandle<JS::Value> rval) {
1556 return WrapObject(cx, p, nullptr, rval);
1559 // Specialization to make it easy to use WrapObject in codegen.
1560 template <>
1561 inline bool WrapObject<JSObject>(JSContext* cx, JSObject* p,
1562 JS::MutableHandle<JS::Value> rval) {
1563 rval.set(JS::ObjectOrNullValue(p));
1564 return true;
1567 inline bool WrapObject(JSContext* cx, JSObject& p,
1568 JS::MutableHandle<JS::Value> rval) {
1569 rval.set(JS::ObjectValue(p));
1570 return true;
1573 bool WrapObject(JSContext* cx, const WindowProxyHolder& p,
1574 JS::MutableHandle<JS::Value> rval);
1576 // Given an object "p" that inherits from nsISupports, wrap it and return the
1577 // result. Null is returned on wrapping failure. This is somewhat similar to
1578 // WrapObject() above, but does NOT allow Xrays around the result, since we
1579 // don't want those for our parent object.
1580 template <typename T>
1581 static inline JSObject* WrapNativeISupports(JSContext* cx, T* p,
1582 nsWrapperCache* cache) {
1583 JS::Rooted<JSObject*> retval(cx);
1585 xpcObjectHelper helper(ToSupports(p), cache);
1586 JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
1587 JS::Rooted<JS::Value> v(cx);
1588 retval = XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v)
1589 ? v.toObjectOrNull()
1590 : nullptr;
1592 return retval;
1595 // Wrapping of our native parent, for cases when it's a WebIDL object.
1596 template <typename T, bool hasWrapObject = NativeHasMember<T>::WrapObject>
1597 struct WrapNativeHelper {
1598 static inline JSObject* Wrap(JSContext* cx, T* parent,
1599 nsWrapperCache* cache) {
1600 MOZ_ASSERT(cache);
1602 JSObject* obj;
1603 if ((obj = cache->GetWrapper())) {
1604 // GetWrapper always unmarks gray.
1605 JS::AssertObjectIsNotGray(obj);
1606 return obj;
1609 // WrapObject never returns a gray thing.
1610 obj = parent->WrapObject(cx, nullptr);
1611 JS::AssertObjectIsNotGray(obj);
1613 return obj;
1617 // Wrapping of our native parent, for cases when it's not a WebIDL object. In
1618 // this case it must be nsISupports.
1619 template <typename T>
1620 struct WrapNativeHelper<T, false> {
1621 static inline JSObject* Wrap(JSContext* cx, T* parent,
1622 nsWrapperCache* cache) {
1623 JSObject* obj;
1624 if (cache && (obj = cache->GetWrapper())) {
1625 #ifdef DEBUG
1626 JS::Rooted<JSObject*> rootedObj(cx, obj);
1627 NS_ASSERTION(WrapNativeISupports(cx, parent, cache) == rootedObj,
1628 "Unexpected object in nsWrapperCache");
1629 obj = rootedObj;
1630 #endif
1631 JS::AssertObjectIsNotGray(obj);
1632 return obj;
1635 obj = WrapNativeISupports(cx, parent, cache);
1636 JS::AssertObjectIsNotGray(obj);
1637 return obj;
1641 // Finding the associated global for an object.
1642 template <typename T>
1643 static inline JSObject* FindAssociatedGlobal(
1644 JSContext* cx, T* p, nsWrapperCache* cache,
1645 mozilla::dom::ReflectionScope scope =
1646 mozilla::dom::ReflectionScope::Content) {
1647 if (!p) {
1648 return JS::CurrentGlobalOrNull(cx);
1651 JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
1652 if (!obj) {
1653 return nullptr;
1655 JS::AssertObjectIsNotGray(obj);
1657 // The object is never a CCW but it may not be in the current compartment of
1658 // the JSContext.
1659 obj = JS::GetNonCCWObjectGlobal(obj);
1661 switch (scope) {
1662 case mozilla::dom::ReflectionScope::NAC: {
1663 return xpc::NACScope(obj);
1666 case mozilla::dom::ReflectionScope::UAWidget: {
1667 // If scope is set to UAWidgetScope, it means that the canonical reflector
1668 // for this native object should live in the UA widget scope.
1669 if (xpc::IsInUAWidgetScope(obj)) {
1670 return obj;
1672 JS::Rooted<JSObject*> rootedObj(cx, obj);
1673 JSObject* uaWidgetScope = xpc::GetUAWidgetScope(cx, rootedObj);
1674 MOZ_ASSERT_IF(uaWidgetScope, JS_IsGlobalObject(uaWidgetScope));
1675 JS::AssertObjectIsNotGray(uaWidgetScope);
1676 return uaWidgetScope;
1679 case ReflectionScope::Content:
1680 return obj;
1683 MOZ_CRASH("Unknown ReflectionScope variant");
1685 return nullptr;
1688 // Finding of the associated global for an object, when we don't want to
1689 // explicitly pass in things like the nsWrapperCache for it.
1690 template <typename T>
1691 static inline JSObject* FindAssociatedGlobal(JSContext* cx, const T& p) {
1692 return FindAssociatedGlobal(cx, GetParentPointer(p), GetWrapperCache(p),
1693 GetReflectionScope(p));
1696 // Specialization for the case of nsIGlobalObject, since in that case
1697 // we can just get the JSObject* directly.
1698 template <>
1699 inline JSObject* FindAssociatedGlobal(JSContext* cx,
1700 nsIGlobalObject* const& p) {
1701 if (!p) {
1702 return JS::CurrentGlobalOrNull(cx);
1705 JSObject* global = p->GetGlobalJSObject();
1706 if (!global) {
1707 // nsIGlobalObject doesn't have a JS object anymore,
1708 // fallback to the current global.
1709 return JS::CurrentGlobalOrNull(cx);
1712 MOZ_ASSERT(JS_IsGlobalObject(global));
1713 JS::AssertObjectIsNotGray(global);
1714 return global;
1717 template <typename T,
1718 bool hasAssociatedGlobal = NativeHasMember<T>::GetParentObject>
1719 struct FindAssociatedGlobalForNative {
1720 static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
1721 MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
1722 T* native = UnwrapDOMObject<T>(obj);
1723 return FindAssociatedGlobal(cx, native->GetParentObject());
1727 template <typename T>
1728 struct FindAssociatedGlobalForNative<T, false> {
1729 static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
1730 MOZ_CRASH();
1731 return nullptr;
1735 // Helper for calling GetOrCreateDOMReflector with smart pointers
1736 // (UniquePtr/RefPtr/nsCOMPtr) or references.
1737 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
1738 struct GetOrCreateDOMReflectorHelper {
1739 static inline bool GetOrCreate(JSContext* cx, const T& value,
1740 JS::Handle<JSObject*> givenProto,
1741 JS::MutableHandle<JS::Value> rval) {
1742 return GetOrCreateDOMReflector(cx, value.get(), rval, givenProto);
1746 template <class T>
1747 struct GetOrCreateDOMReflectorHelper<T, false> {
1748 static inline bool GetOrCreate(JSContext* cx, T& value,
1749 JS::Handle<JSObject*> givenProto,
1750 JS::MutableHandle<JS::Value> rval) {
1751 static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
1752 return GetOrCreateDOMReflector(cx, &value, rval, givenProto);
1756 template <class T>
1757 inline bool GetOrCreateDOMReflector(
1758 JSContext* cx, T& value, JS::MutableHandle<JS::Value> rval,
1759 JS::Handle<JSObject*> givenProto = nullptr) {
1760 return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, givenProto,
1761 rval);
1764 // Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
1765 // (UniquePtr/RefPtr/nsCOMPtr) or references.
1766 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
1767 struct GetOrCreateDOMReflectorNoWrapHelper {
1768 static inline bool GetOrCreate(JSContext* cx, const T& value,
1769 JS::MutableHandle<JS::Value> rval) {
1770 return GetOrCreateDOMReflectorNoWrap(cx, value.get(), rval);
1774 template <class T>
1775 struct GetOrCreateDOMReflectorNoWrapHelper<T, false> {
1776 static inline bool GetOrCreate(JSContext* cx, T& value,
1777 JS::MutableHandle<JS::Value> rval) {
1778 return GetOrCreateDOMReflectorNoWrap(cx, &value, rval);
1782 template <class T>
1783 inline bool GetOrCreateDOMReflectorNoWrap(JSContext* cx, T& value,
1784 JS::MutableHandle<JS::Value> rval) {
1785 return GetOrCreateDOMReflectorNoWrapHelper<T>::GetOrCreate(cx, value, rval);
1788 template <class T>
1789 inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T* aObj) {
1790 return aObj->Callback(aCx);
1793 // Helper for getting the callback JSObject* of a smart ptr around a
1794 // CallbackObject or a reference to a CallbackObject or something like
1795 // that.
1796 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
1797 struct GetCallbackFromCallbackObjectHelper {
1798 static inline JSObject* Get(JSContext* aCx, const T& aObj) {
1799 return GetCallbackFromCallbackObject(aCx, aObj.get());
1803 template <class T>
1804 struct GetCallbackFromCallbackObjectHelper<T, false> {
1805 static inline JSObject* Get(JSContext* aCx, T& aObj) {
1806 return GetCallbackFromCallbackObject(aCx, &aObj);
1810 template <class T>
1811 inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T& aObj) {
1812 return GetCallbackFromCallbackObjectHelper<T>::Get(aCx, aObj);
1815 static inline bool AtomizeAndPinJSString(JSContext* cx, jsid& id,
1816 const char* chars) {
1817 if (JSString* str = ::JS_AtomizeAndPinString(cx, chars)) {
1818 id = JS::PropertyKey::fromPinnedString(str);
1819 return true;
1821 return false;
1824 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
1825 nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
1826 JS::MutableHandle<JS::Value> aRetval,
1827 ErrorResult& aError);
1829 template <class T>
1830 void GetInterface(JSContext* aCx, T* aThis, JS::Handle<JS::Value> aIID,
1831 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
1832 GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
1835 bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
1837 bool ThrowConstructorWithoutNew(JSContext* cx, const char* name);
1839 // Helper for throwing an "invalid this" exception.
1840 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
1841 bool aSecurityError, prototypes::ID aProtoId);
1843 bool GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1844 JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
1845 bool* found, JS::MutableHandle<JS::Value> vp);
1848 bool HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1849 JS::Handle<jsid> id, bool* has);
1851 // Append the property names in "names" to "props". If
1852 // shadowPrototypeProperties is false then skip properties that are also
1853 // present on the proto chain of proxy. If shadowPrototypeProperties is true,
1854 // then the "proxy" argument is ignored.
1855 bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
1856 nsTArray<nsString>& names,
1857 bool shadowPrototypeProperties,
1858 JS::MutableHandleVector<jsid> props);
1860 enum StringificationBehavior { eStringify, eEmpty, eNull };
1862 static inline JSString* ConvertJSValueToJSString(JSContext* cx,
1863 JS::Handle<JS::Value> v) {
1864 if (MOZ_LIKELY(v.isString())) {
1865 return v.toString();
1867 return JS::ToString(cx, v);
1870 template <typename T>
1871 static inline bool ConvertJSValueToString(
1872 JSContext* cx, JS::Handle<JS::Value> v,
1873 StringificationBehavior nullBehavior,
1874 StringificationBehavior undefinedBehavior, T& result) {
1875 JSString* s;
1876 if (v.isString()) {
1877 s = v.toString();
1878 } else {
1879 StringificationBehavior behavior;
1880 if (v.isNull()) {
1881 behavior = nullBehavior;
1882 } else if (v.isUndefined()) {
1883 behavior = undefinedBehavior;
1884 } else {
1885 behavior = eStringify;
1888 if (behavior != eStringify) {
1889 if (behavior == eEmpty) {
1890 result.Truncate();
1891 } else {
1892 result.SetIsVoid(true);
1894 return true;
1897 s = JS::ToString(cx, v);
1898 if (!s) {
1899 return false;
1903 return AssignJSString(cx, result, s);
1906 template <typename T>
1907 static inline bool ConvertJSValueToString(
1908 JSContext* cx, JS::Handle<JS::Value> v,
1909 const char* /* unused sourceDescription */, T& result) {
1910 return ConvertJSValueToString(cx, v, eStringify, eStringify, result);
1913 [[nodiscard]] bool NormalizeUSVString(nsAString& aString);
1915 [[nodiscard]] bool NormalizeUSVString(
1916 binding_detail::FakeString<char16_t>& aString);
1918 template <typename T>
1919 static inline bool ConvertJSValueToUSVString(
1920 JSContext* cx, JS::Handle<JS::Value> v,
1921 const char* /* unused sourceDescription */, T& result) {
1922 if (!ConvertJSValueToString(cx, v, eStringify, eStringify, result)) {
1923 return false;
1926 if (!NormalizeUSVString(result)) {
1927 JS_ReportOutOfMemory(cx);
1928 return false;
1931 return true;
1934 template <typename T>
1935 inline bool ConvertIdToString(JSContext* cx, JS::Handle<JS::PropertyKey> id,
1936 T& result, bool& isSymbol) {
1937 if (MOZ_LIKELY(id.isString())) {
1938 if (!AssignJSString(cx, result, id.toString())) {
1939 return false;
1941 } else if (id.isSymbol()) {
1942 isSymbol = true;
1943 return true;
1944 } else {
1945 JS::Rooted<JS::Value> nameVal(cx, js::IdToValue(id));
1946 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, result)) {
1947 return false;
1950 isSymbol = false;
1951 return true;
1954 bool ConvertJSValueToByteString(BindingCallContext& cx, JS::Handle<JS::Value> v,
1955 bool nullable, const char* sourceDescription,
1956 nsACString& result);
1958 inline bool ConvertJSValueToByteString(BindingCallContext& cx,
1959 JS::Handle<JS::Value> v,
1960 const char* sourceDescription,
1961 nsACString& result) {
1962 return ConvertJSValueToByteString(cx, v, false, sourceDescription, result);
1965 template <typename T>
1966 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
1967 template <typename T>
1968 void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq);
1970 // Class used to trace sequences, with specializations for various
1971 // sequence types.
1972 template <typename T,
1973 bool isDictionary = std::is_base_of<DictionaryBase, T>::value,
1974 bool isTypedArray = std::is_base_of<AllTypedArraysBase, T>::value,
1975 bool isOwningUnion = std::is_base_of<AllOwningUnionBase, T>::value>
1976 class SequenceTracer {
1977 explicit SequenceTracer() = delete; // Should never be instantiated
1980 // sequence<object> or sequence<object?>
1981 template <>
1982 class SequenceTracer<JSObject*, false, false, false> {
1983 explicit SequenceTracer() = delete; // Should never be instantiated
1985 public:
1986 static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) {
1987 for (; objp != end; ++objp) {
1988 JS::TraceRoot(trc, objp, "sequence<object>");
1993 // sequence<any>
1994 template <>
1995 class SequenceTracer<JS::Value, false, false, false> {
1996 explicit SequenceTracer() = delete; // Should never be instantiated
1998 public:
1999 static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) {
2000 for (; valp != end; ++valp) {
2001 JS::TraceRoot(trc, valp, "sequence<any>");
2006 // sequence<sequence<T>>
2007 template <typename T>
2008 class SequenceTracer<Sequence<T>, false, false, false> {
2009 explicit SequenceTracer() = delete; // Should never be instantiated
2011 public:
2012 static void TraceSequence(JSTracer* trc, Sequence<T>* seqp,
2013 Sequence<T>* end) {
2014 for (; seqp != end; ++seqp) {
2015 DoTraceSequence(trc, *seqp);
2020 // sequence<sequence<T>> as return value
2021 template <typename T>
2022 class SequenceTracer<nsTArray<T>, false, false, false> {
2023 explicit SequenceTracer() = delete; // Should never be instantiated
2025 public:
2026 static void TraceSequence(JSTracer* trc, nsTArray<T>* seqp,
2027 nsTArray<T>* end) {
2028 for (; seqp != end; ++seqp) {
2029 DoTraceSequence(trc, *seqp);
2034 // sequence<someDictionary>
2035 template <typename T>
2036 class SequenceTracer<T, true, false, false> {
2037 explicit SequenceTracer() = delete; // Should never be instantiated
2039 public:
2040 static void TraceSequence(JSTracer* trc, T* dictp, T* end) {
2041 for (; dictp != end; ++dictp) {
2042 dictp->TraceDictionary(trc);
2047 // sequence<SomeTypedArray>
2048 template <typename T>
2049 class SequenceTracer<T, false, true, false> {
2050 explicit SequenceTracer() = delete; // Should never be instantiated
2052 public:
2053 static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
2054 for (; arrayp != end; ++arrayp) {
2055 arrayp->TraceSelf(trc);
2060 // sequence<SomeOwningUnion>
2061 template <typename T>
2062 class SequenceTracer<T, false, false, true> {
2063 explicit SequenceTracer() = delete; // Should never be instantiated
2065 public:
2066 static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
2067 for (; arrayp != end; ++arrayp) {
2068 arrayp->TraceUnion(trc);
2073 // sequence<T?> with T? being a Nullable<T>
2074 template <typename T>
2075 class SequenceTracer<Nullable<T>, false, false, false> {
2076 explicit SequenceTracer() = delete; // Should never be instantiated
2078 public:
2079 static void TraceSequence(JSTracer* trc, Nullable<T>* seqp,
2080 Nullable<T>* end) {
2081 for (; seqp != end; ++seqp) {
2082 if (!seqp->IsNull()) {
2083 // Pretend like we actually have a length-one sequence here so
2084 // we can do template instantiation correctly for T.
2085 T& val = seqp->Value();
2086 T* ptr = &val;
2087 SequenceTracer<T>::TraceSequence(trc, ptr, ptr + 1);
2093 template <typename K, typename V>
2094 void TraceRecord(JSTracer* trc, Record<K, V>& record) {
2095 for (auto& entry : record.Entries()) {
2096 // Act like it's a one-element sequence to leverage all that infrastructure.
2097 SequenceTracer<V>::TraceSequence(trc, &entry.mValue, &entry.mValue + 1);
2101 // sequence<record>
2102 template <typename K, typename V>
2103 class SequenceTracer<Record<K, V>, false, false, false> {
2104 explicit SequenceTracer() = delete; // Should never be instantiated
2106 public:
2107 static void TraceSequence(JSTracer* trc, Record<K, V>* seqp,
2108 Record<K, V>* end) {
2109 for (; seqp != end; ++seqp) {
2110 TraceRecord(trc, *seqp);
2115 template <typename T>
2116 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq) {
2117 SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
2118 seq.Elements() + seq.Length());
2121 template <typename T>
2122 void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq) {
2123 SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
2124 seq.Elements() + seq.Length());
2127 // Rooter class for sequences; this is what we mostly use in the codegen
2128 template <typename T>
2129 class MOZ_RAII SequenceRooter final : private JS::CustomAutoRooter {
2130 public:
2131 template <typename CX>
2132 SequenceRooter(const CX& cx, FallibleTArray<T>* aSequence)
2133 : JS::CustomAutoRooter(cx),
2134 mFallibleArray(aSequence),
2135 mSequenceType(eFallibleArray) {}
2137 template <typename CX>
2138 SequenceRooter(const CX& cx, nsTArray<T>* aSequence)
2139 : JS::CustomAutoRooter(cx),
2140 mInfallibleArray(aSequence),
2141 mSequenceType(eInfallibleArray) {}
2143 template <typename CX>
2144 SequenceRooter(const CX& cx, Nullable<nsTArray<T>>* aSequence)
2145 : JS::CustomAutoRooter(cx),
2146 mNullableArray(aSequence),
2147 mSequenceType(eNullableArray) {}
2149 private:
2150 enum SequenceType { eInfallibleArray, eFallibleArray, eNullableArray };
2152 virtual void trace(JSTracer* trc) override {
2153 if (mSequenceType == eFallibleArray) {
2154 DoTraceSequence(trc, *mFallibleArray);
2155 } else if (mSequenceType == eInfallibleArray) {
2156 DoTraceSequence(trc, *mInfallibleArray);
2157 } else {
2158 MOZ_ASSERT(mSequenceType == eNullableArray);
2159 if (!mNullableArray->IsNull()) {
2160 DoTraceSequence(trc, mNullableArray->Value());
2165 union {
2166 nsTArray<T>* mInfallibleArray;
2167 FallibleTArray<T>* mFallibleArray;
2168 Nullable<nsTArray<T>>* mNullableArray;
2171 SequenceType mSequenceType;
2174 // Rooter class for Record; this is what we mostly use in the codegen.
2175 template <typename K, typename V>
2176 class MOZ_RAII RecordRooter final : private JS::CustomAutoRooter {
2177 public:
2178 template <typename CX>
2179 RecordRooter(const CX& cx, Record<K, V>* aRecord)
2180 : JS::CustomAutoRooter(cx), mRecord(aRecord), mRecordType(eRecord) {}
2182 template <typename CX>
2183 RecordRooter(const CX& cx, Nullable<Record<K, V>>* aRecord)
2184 : JS::CustomAutoRooter(cx),
2185 mNullableRecord(aRecord),
2186 mRecordType(eNullableRecord) {}
2188 private:
2189 enum RecordType { eRecord, eNullableRecord };
2191 virtual void trace(JSTracer* trc) override {
2192 if (mRecordType == eRecord) {
2193 TraceRecord(trc, *mRecord);
2194 } else {
2195 MOZ_ASSERT(mRecordType == eNullableRecord);
2196 if (!mNullableRecord->IsNull()) {
2197 TraceRecord(trc, mNullableRecord->Value());
2202 union {
2203 Record<K, V>* mRecord;
2204 Nullable<Record<K, V>>* mNullableRecord;
2207 RecordType mRecordType;
2210 template <typename T>
2211 class MOZ_RAII RootedUnion : public T, private JS::CustomAutoRooter {
2212 public:
2213 template <typename CX>
2214 explicit RootedUnion(const CX& cx) : T(), JS::CustomAutoRooter(cx) {}
2216 virtual void trace(JSTracer* trc) override { this->TraceUnion(trc); }
2219 template <typename T>
2220 class MOZ_STACK_CLASS NullableRootedUnion : public Nullable<T>,
2221 private JS::CustomAutoRooter {
2222 public:
2223 template <typename CX>
2224 explicit NullableRootedUnion(const CX& cx)
2225 : Nullable<T>(), JS::CustomAutoRooter(cx) {}
2227 virtual void trace(JSTracer* trc) override {
2228 if (!this->IsNull()) {
2229 this->Value().TraceUnion(trc);
2234 inline bool AddStringToIDVector(JSContext* cx,
2235 JS::MutableHandleVector<jsid> vector,
2236 const char* name) {
2237 return vector.growBy(1) &&
2238 AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(),
2239 name);
2242 // We use one constructor JSNative to represent all DOM interface objects (so
2243 // we can easily detect when we need to wrap them in an Xray wrapper). We store
2244 // the real JSNative in the mNative member of a JSNativeHolder in the
2245 // CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
2246 // specific interface object. We also store the NativeProperties in the
2247 // JSNativeHolder.
2248 // Note that some interface objects are not yet a JSFunction but a normal
2249 // JSObject with a DOMJSClass, those do not use these slots.
2251 enum { CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0 };
2253 bool Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
2255 // Implementation of the bits that XrayWrapper needs
2258 * This resolves operations, attributes and constants of the interfaces for obj.
2260 * wrapper is the Xray JS object.
2261 * obj is the target object of the Xray, a binding's instance object or a
2262 * interface or interface prototype object.
2264 bool XrayResolveOwnProperty(
2265 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
2266 JS::Handle<jsid> id,
2267 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
2268 bool& cacheOnHolder);
2271 * Define a property on obj through an Xray wrapper.
2273 * wrapper is the Xray JS object.
2274 * obj is the target object of the Xray, a binding's instance object or a
2275 * interface or interface prototype object.
2276 * id and desc are the parameters for the property to be defined.
2277 * result is the out-parameter indicating success (read it only if
2278 * this returns true and also sets *done to true).
2279 * done will be set to true if a property was set as a result of this call
2280 * or if we want to always avoid setting this property
2281 * (i.e. indexed properties on DOM objects)
2283 bool XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
2284 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
2285 JS::Handle<JS::PropertyDescriptor> desc,
2286 JS::ObjectOpResult& result, bool* done);
2289 * Add to props the property keys of all indexed or named properties of obj and
2290 * operations, attributes and constants of the interfaces for obj.
2292 * wrapper is the Xray JS object.
2293 * obj is the target object of the Xray, a binding's instance object or a
2294 * interface or interface prototype object.
2295 * flags are JSITER_* flags.
2297 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
2298 JS::Handle<JSObject*> obj, unsigned flags,
2299 JS::MutableHandleVector<jsid> props);
2302 * Returns the prototype to use for an Xray for a DOM object, wrapped in cx's
2303 * compartment. This always returns the prototype that would be used for a DOM
2304 * object if we ignore any changes that might have been done to the prototype
2305 * chain by JS, the XBL code or plugins.
2307 * cx should be in the Xray's compartment.
2308 * obj is the target object of the Xray, a binding's instance object or an
2309 * interface or interface prototype object.
2311 inline bool XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
2312 JS::MutableHandle<JSObject*> protop) {
2313 JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj));
2315 JSAutoRealm ar(cx, global);
2316 const DOMJSClass* domClass = GetDOMClass(obj);
2317 if (domClass) {
2318 ProtoHandleGetter protoGetter = domClass->mGetProto;
2319 if (protoGetter) {
2320 protop.set(protoGetter(cx));
2321 } else {
2322 protop.set(JS::GetRealmObjectPrototype(cx));
2324 } else if (JS_ObjectIsFunction(obj)) {
2325 MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
2326 protop.set(JS::GetRealmFunctionPrototype(cx));
2327 } else {
2328 const JSClass* clasp = JS::GetClass(obj);
2329 MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
2330 ProtoGetter protoGetter =
2331 DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
2332 protop.set(protoGetter(cx));
2336 return JS_WrapObject(cx, protop);
2340 * Get the Xray expando class to use for the given DOM object.
2342 const JSClass* XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj);
2345 * Delete a named property, if any. Return value is false if exception thrown,
2346 * true otherwise. The caller should not do any more work after calling this
2347 * function, because it has no way whether a deletion was performed and hence
2348 * opresult already has state set on it. If callers ever need to change that,
2349 * add a "bool* found" argument and change the generated DeleteNamedProperty to
2350 * use it instead of a local variable.
2352 bool XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
2353 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
2354 JS::ObjectOpResult& opresult);
2356 namespace binding_detail {
2358 // Default implementations of the NativePropertyHooks' mResolveOwnProperty and
2359 // mEnumerateOwnProperties for WebIDL bindings implemented as proxies.
2360 bool ResolveOwnProperty(
2361 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
2362 JS::Handle<jsid> id,
2363 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
2364 bool EnumerateOwnProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
2365 JS::Handle<JSObject*> obj,
2366 JS::MutableHandleVector<jsid> props);
2368 } // namespace binding_detail
2371 * Get the object which should be used to cache the return value of a property
2372 * getter in the case of a [Cached] or [StoreInSlot] property. `obj` is the
2373 * `this` value for our property getter that we're working with.
2375 * This function can return null on failure to allocate the object, throwing on
2376 * the JSContext in the process.
2378 * The isXray outparam will be set to true if obj is an Xray and false
2379 * otherwise.
2381 * Note that the Slow version should only be called from
2382 * GetCachedSlotStorageObject.
2384 JSObject* GetCachedSlotStorageObjectSlow(JSContext* cx,
2385 JS::Handle<JSObject*> obj,
2386 bool* isXray);
2388 inline JSObject* GetCachedSlotStorageObject(JSContext* cx,
2389 JS::Handle<JSObject*> obj,
2390 bool* isXray) {
2391 if (IsDOMObject(obj)) {
2392 *isXray = false;
2393 return obj;
2396 return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
2399 extern NativePropertyHooks sEmptyNativePropertyHooks;
2401 extern const JSClassOps sBoringInterfaceObjectClassClassOps;
2403 extern const js::ObjectOps sInterfaceObjectClassObjectOps;
2405 inline bool UseDOMXray(JSObject* obj) {
2406 const JSClass* clasp = JS::GetClass(obj);
2407 return IsDOMClass(clasp) || JS_IsNativeFunction(obj, Constructor) ||
2408 IsDOMIfaceAndProtoClass(clasp);
2411 inline bool IsDOMConstructor(JSObject* obj) {
2412 if (JS_IsNativeFunction(obj, dom::Constructor)) {
2413 // LegacyFactoryFunction, like Image
2414 return true;
2417 const JSClass* clasp = JS::GetClass(obj);
2418 // Check for a DOM interface object.
2419 return dom::IsDOMIfaceAndProtoClass(clasp) &&
2420 dom::DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mType ==
2421 dom::eInterface;
2424 #ifdef DEBUG
2425 inline bool HasConstructor(JSObject* obj) {
2426 return JS_IsNativeFunction(obj, Constructor) ||
2427 JS::GetClass(obj)->getConstruct();
2429 #endif
2431 // Helpers for creating a const version of a type.
2432 template <typename T>
2433 const T& Constify(T& arg) {
2434 return arg;
2437 // Helper for turning (Owning)NonNull<T> into T&
2438 template <typename T>
2439 T& NonNullHelper(T& aArg) {
2440 return aArg;
2443 template <typename T>
2444 T& NonNullHelper(NonNull<T>& aArg) {
2445 return aArg;
2448 template <typename T>
2449 const T& NonNullHelper(const NonNull<T>& aArg) {
2450 return aArg;
2453 template <typename T>
2454 T& NonNullHelper(OwningNonNull<T>& aArg) {
2455 return aArg;
2458 template <typename T>
2459 const T& NonNullHelper(const OwningNonNull<T>& aArg) {
2460 return aArg;
2463 template <typename CharT>
2464 inline void NonNullHelper(NonNull<binding_detail::FakeString<CharT>>& aArg) {
2465 // This overload is here to make sure that we never end up applying
2466 // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
2467 // try to, it should fail to compile, since presumably the caller will try to
2468 // use our nonexistent return value.
2471 template <typename CharT>
2472 inline void NonNullHelper(
2473 const NonNull<binding_detail::FakeString<CharT>>& aArg) {
2474 // This overload is here to make sure that we never end up applying
2475 // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
2476 // try to, it should fail to compile, since presumably the caller will try to
2477 // use our nonexistent return value.
2480 template <typename CharT>
2481 inline void NonNullHelper(binding_detail::FakeString<CharT>& aArg) {
2482 // This overload is here to make sure that we never end up applying
2483 // NonNullHelper to a FakeString before we've constified it. If we
2484 // try to, it should fail to compile, since presumably the caller will try to
2485 // use our nonexistent return value.
2488 template <typename CharT>
2489 MOZ_ALWAYS_INLINE const nsTSubstring<CharT>& NonNullHelper(
2490 const binding_detail::FakeString<CharT>& aArg) {
2491 return aArg;
2494 // Given a DOM reflector aObj, give its underlying DOM object a reflector in
2495 // whatever global that underlying DOM object now thinks it should be in. If
2496 // this is in a different compartment from aObj, aObj will become a
2497 // cross-compatment wrapper for the new object. Otherwise, aObj will become the
2498 // new object (via a brain transplant). If the new global is the same as the
2499 // old global, we just keep using the same object.
2501 // On entry to this method, aCx and aObj must be same-compartment.
2502 void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
2503 ErrorResult& aError);
2506 * Used to implement the Symbol.hasInstance property of an interface object.
2508 bool InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp);
2510 bool InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
2511 JS::Handle<JSObject*> instance, bool* bp);
2513 // Used to implement the cross-context <Interface>.isInstance static method.
2514 bool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp);
2516 // Helper for lenient getters/setters to report to console. If this
2517 // returns false, we couldn't even get a global.
2518 bool ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
2520 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
2521 // interface, get the nsIGlobalObject corresponding to the content side, if any.
2522 // A false return means an exception was thrown.
2523 bool GetContentGlobalForJSImplementedObject(BindingCallContext& cx,
2524 JS::Handle<JSObject*> obj,
2525 nsIGlobalObject** global);
2527 void ConstructJSImplementation(const char* aContractId,
2528 nsIGlobalObject* aGlobal,
2529 JS::MutableHandle<JSObject*> aObject,
2530 ErrorResult& aRv);
2532 // XXX Avoid pulling in the whole ScriptSettings.h, however there should be a
2533 // unique declaration of this function somewhere else.
2534 JS::RootingContext* RootingCx();
2536 template <typename T>
2537 already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
2538 nsIGlobalObject* aGlobal,
2539 ErrorResult& aRv) {
2540 JS::RootingContext* cx = RootingCx();
2541 JS::Rooted<JSObject*> jsImplObj(cx);
2542 ConstructJSImplementation(aContractId, aGlobal, &jsImplObj, aRv);
2543 if (aRv.Failed()) {
2544 return nullptr;
2547 MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplObj));
2548 JS::Rooted<JSObject*> jsImplGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplObj));
2549 RefPtr<T> newObj = new T(jsImplObj, jsImplGlobal, aGlobal);
2550 return newObj.forget();
2553 template <typename T>
2554 already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
2555 const GlobalObject& aGlobal,
2556 ErrorResult& aRv) {
2557 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
2558 if (!global) {
2559 aRv.Throw(NS_ERROR_FAILURE);
2560 return nullptr;
2563 return ConstructJSImplementation<T>(aContractId, global, aRv);
2567 * Convert an nsCString to jsval, returning true on success.
2568 * These functions are intended for ByteString implementations.
2569 * As such, the string is not UTF-8 encoded. Any UTF8 strings passed to these
2570 * methods will be mangled.
2572 bool NonVoidByteStringToJsval(JSContext* cx, const nsACString& str,
2573 JS::MutableHandle<JS::Value> rval);
2574 inline bool ByteStringToJsval(JSContext* cx, const nsACString& str,
2575 JS::MutableHandle<JS::Value> rval) {
2576 if (str.IsVoid()) {
2577 rval.setNull();
2578 return true;
2580 return NonVoidByteStringToJsval(cx, str, rval);
2583 // Convert an utf-8 encoded nsCString to jsval, returning true on success.
2585 // TODO(bug 1606957): This could probably be better.
2586 inline bool NonVoidUTF8StringToJsval(JSContext* cx, const nsACString& str,
2587 JS::MutableHandle<JS::Value> rval) {
2588 JSString* jsStr =
2589 JS_NewStringCopyUTF8N(cx, {str.BeginReading(), str.Length()});
2590 if (!jsStr) {
2591 return false;
2593 rval.setString(jsStr);
2594 return true;
2597 inline bool UTF8StringToJsval(JSContext* cx, const nsACString& str,
2598 JS::MutableHandle<JS::Value> rval) {
2599 if (str.IsVoid()) {
2600 rval.setNull();
2601 return true;
2603 return NonVoidUTF8StringToJsval(cx, str, rval);
2606 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
2607 struct PreserveWrapperHelper {
2608 static void PreserveWrapper(T* aObject) {
2609 aObject->PreserveWrapper(aObject, NS_CYCLE_COLLECTION_PARTICIPANT(T));
2613 template <class T>
2614 struct PreserveWrapperHelper<T, true> {
2615 static void PreserveWrapper(T* aObject) {
2616 aObject->PreserveWrapper(reinterpret_cast<nsISupports*>(aObject));
2620 template <class T>
2621 void PreserveWrapper(T* aObject) {
2622 PreserveWrapperHelper<T>::PreserveWrapper(aObject);
2625 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
2626 struct CastingAssertions {
2627 static bool ToSupportsIsCorrect(T*) { return true; }
2628 static bool ToSupportsIsOnPrimaryInheritanceChain(T*, nsWrapperCache*) {
2629 return true;
2633 template <class T>
2634 struct CastingAssertions<T, true> {
2635 static bool ToSupportsIsCorrect(T* aObject) {
2636 return ToSupports(aObject) == reinterpret_cast<nsISupports*>(aObject);
2638 static bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject,
2639 nsWrapperCache* aCache) {
2640 return reinterpret_cast<void*>(aObject) != aCache;
2644 template <class T>
2645 bool ToSupportsIsCorrect(T* aObject) {
2646 return CastingAssertions<T>::ToSupportsIsCorrect(aObject);
2649 template <class T>
2650 bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) {
2651 return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject,
2652 aCache);
2655 // Get the size of allocated memory to associate with a binding JSObject for a
2656 // native object. This is supplied to the JS engine to allow it to schedule GC
2657 // when necessary.
2659 // This function supplies a default value and is overloaded for specific native
2660 // object types.
2661 inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; }
2663 // The BindingJSObjectCreator class is supposed to be used by a caller that
2664 // wants to create and initialise a binding JSObject. After initialisation has
2665 // been successfully completed it should call InitializationSucceeded().
2666 // The BindingJSObjectCreator object will root the JSObject until
2667 // InitializationSucceeded() is called on it. If the native object for the
2668 // binding is refcounted it will also hold a strong reference to it, that
2669 // reference is transferred to the JSObject (which holds the native in a slot)
2670 // when InitializationSucceeded() is called. If the BindingJSObjectCreator
2671 // object is destroyed and InitializationSucceeded() was never called on it then
2672 // the JSObject's slot holding the native will be set to undefined, and for a
2673 // refcounted native the strong reference will be released.
2674 template <class T>
2675 class MOZ_STACK_CLASS BindingJSObjectCreator {
2676 public:
2677 explicit BindingJSObjectCreator(JSContext* aCx) : mReflector(aCx) {}
2679 ~BindingJSObjectCreator() {
2680 if (mReflector) {
2681 JS::SetReservedSlot(mReflector, DOM_OBJECT_SLOT, JS::UndefinedValue());
2685 void CreateProxyObject(JSContext* aCx, const JSClass* aClass,
2686 const DOMProxyHandler* aHandler,
2687 JS::Handle<JSObject*> aProto, bool aLazyProto,
2688 T* aNative, JS::Handle<JS::Value> aExpandoValue,
2689 JS::MutableHandle<JSObject*> aReflector) {
2690 js::ProxyOptions options;
2691 options.setClass(aClass);
2692 options.setLazyProto(aLazyProto);
2694 aReflector.set(
2695 js::NewProxyObject(aCx, aHandler, aExpandoValue, aProto, options));
2696 if (aReflector) {
2697 js::SetProxyReservedSlot(aReflector, DOM_OBJECT_SLOT,
2698 JS::PrivateValue(aNative));
2699 mNative = aNative;
2700 mReflector = aReflector;
2702 if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
2703 JS::AddAssociatedMemory(aReflector, mallocBytes,
2704 JS::MemoryUse::DOMBinding);
2709 void CreateObject(JSContext* aCx, const JSClass* aClass,
2710 JS::Handle<JSObject*> aProto, T* aNative,
2711 JS::MutableHandle<JSObject*> aReflector) {
2712 aReflector.set(JS_NewObjectWithGivenProto(aCx, aClass, aProto));
2713 if (aReflector) {
2714 JS::SetReservedSlot(aReflector, DOM_OBJECT_SLOT,
2715 JS::PrivateValue(aNative));
2716 mNative = aNative;
2717 mReflector = aReflector;
2719 if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
2720 JS::AddAssociatedMemory(aReflector, mallocBytes,
2721 JS::MemoryUse::DOMBinding);
2726 void InitializationSucceeded() {
2727 T* pointer;
2728 mNative.forget(&pointer);
2729 mReflector = nullptr;
2732 private:
2733 struct OwnedNative {
2734 // Make sure the native objects inherit from NonRefcountedDOMObject so
2735 // that we log their ctor and dtor.
2736 static_assert(std::is_base_of<NonRefcountedDOMObject, T>::value,
2737 "Non-refcounted objects with DOM bindings should inherit "
2738 "from NonRefcountedDOMObject.");
2740 OwnedNative& operator=(T* aNative) {
2741 mNative = aNative;
2742 return *this;
2745 // This signature sucks, but it's the only one that will make a nsRefPtr
2746 // just forget about its pointer without warning.
2747 void forget(T** aResult) {
2748 *aResult = mNative;
2749 mNative = nullptr;
2752 // Keep track of the pointer for use in InitializationSucceeded().
2753 // The caller (or, after initialization succeeds, the JS object) retains
2754 // ownership of the object.
2755 T* mNative;
2758 JS::Rooted<JSObject*> mReflector;
2759 std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, OwnedNative> mNative;
2762 template <class T>
2763 struct DeferredFinalizerImpl {
2764 using SmartPtr = std::conditional_t<
2765 std::is_same_v<T, nsISupports>, nsCOMPtr<T>,
2766 std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, UniquePtr<T>>>;
2767 typedef SegmentedVector<SmartPtr> SmartPtrArray;
2769 static_assert(
2770 std::is_same_v<T, nsISupports> || !std::is_base_of<nsISupports, T>::value,
2771 "nsISupports classes should all use the nsISupports instantiation");
2773 static inline void AppendAndTake(
2774 SegmentedVector<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr) {
2775 smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
2777 template <class U>
2778 static inline void AppendAndTake(SegmentedVector<RefPtr<U>>& smartPtrArray,
2779 U* ptr) {
2780 smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
2782 template <class U>
2783 static inline void AppendAndTake(SegmentedVector<UniquePtr<U>>& smartPtrArray,
2784 U* ptr) {
2785 smartPtrArray.InfallibleAppend(ptr);
2788 static void* AppendDeferredFinalizePointer(void* aData, void* aObject) {
2789 SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
2790 if (!pointers) {
2791 pointers = new SmartPtrArray();
2793 AppendAndTake(*pointers, static_cast<T*>(aObject));
2794 return pointers;
2796 static bool DeferredFinalize(uint32_t aSlice, void* aData) {
2797 MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0");
2798 SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
2799 uint32_t oldLen = pointers->Length();
2800 if (oldLen < aSlice) {
2801 aSlice = oldLen;
2803 uint32_t newLen = oldLen - aSlice;
2804 pointers->PopLastN(aSlice);
2805 if (newLen == 0) {
2806 delete pointers;
2807 return true;
2809 return false;
2813 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
2814 struct DeferredFinalizer {
2815 static void AddForDeferredFinalization(T* aObject) {
2816 typedef DeferredFinalizerImpl<T> Impl;
2817 DeferredFinalize(Impl::AppendDeferredFinalizePointer,
2818 Impl::DeferredFinalize, aObject);
2822 template <class T>
2823 struct DeferredFinalizer<T, true> {
2824 static void AddForDeferredFinalization(T* aObject) {
2825 DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
2829 template <class T>
2830 static void AddForDeferredFinalization(T* aObject) {
2831 DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
2834 // This returns T's CC participant if it participates in CC and does not inherit
2835 // from nsISupports. Otherwise, it returns null. QI should be used to get the
2836 // participant if T inherits from nsISupports.
2837 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
2838 class GetCCParticipant {
2839 // Helper for GetCCParticipant for classes that participate in CC.
2840 template <class U>
2841 static constexpr nsCycleCollectionParticipant* GetHelper(
2842 int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy = nullptr) {
2843 return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant();
2845 // Helper for GetCCParticipant for classes that don't participate in CC.
2846 template <class U>
2847 static constexpr nsCycleCollectionParticipant* GetHelper(double) {
2848 return nullptr;
2851 public:
2852 static constexpr nsCycleCollectionParticipant* Get() {
2853 // Passing int() here will try to call the GetHelper that takes an int as
2854 // its first argument. If T doesn't participate in CC then substitution for
2855 // the second argument (with a default value) will fail and because of
2856 // SFINAE the next best match (the variant taking a double) will be called.
2857 return GetHelper<T>(int());
2861 template <class T>
2862 class GetCCParticipant<T, true> {
2863 public:
2864 static constexpr nsCycleCollectionParticipant* Get() { return nullptr; }
2867 void FinalizeGlobal(JS::GCContext* aGcx, JSObject* aObj);
2869 bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
2870 JS::Handle<jsid> aId, bool* aResolvedp);
2872 bool MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj);
2874 bool EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
2875 JS::MutableHandleVector<jsid> aProperties,
2876 bool aEnumerableOnly);
2878 struct CreateGlobalOptionsGeneric {
2879 static void TraceGlobal(JSTracer* aTrc, JSObject* aObj) {
2880 mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
2882 static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal) {
2883 MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
2885 return true;
2889 struct CreateGlobalOptionsWithXPConnect {
2890 static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
2891 static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
2894 template <class T>
2895 using IsGlobalWithXPConnect =
2896 std::integral_constant<bool,
2897 std::is_base_of<nsGlobalWindowInner, T>::value ||
2898 std::is_base_of<MessageManagerGlobal, T>::value>;
2900 template <class T>
2901 struct CreateGlobalOptions
2902 : std::conditional_t<IsGlobalWithXPConnect<T>::value,
2903 CreateGlobalOptionsWithXPConnect,
2904 CreateGlobalOptionsGeneric> {
2905 static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
2906 ProtoAndIfaceCache::NonWindowLike;
2909 template <>
2910 struct CreateGlobalOptions<nsGlobalWindowInner>
2911 : public CreateGlobalOptionsWithXPConnect {
2912 static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
2913 ProtoAndIfaceCache::WindowLike;
2916 uint64_t GetWindowID(void* aGlobal);
2917 uint64_t GetWindowID(nsGlobalWindowInner* aGlobal);
2918 uint64_t GetWindowID(DedicatedWorkerGlobalScope* aGlobal);
2920 // The return value is true if we created and successfully performed our part of
2921 // the setup for the global, false otherwise.
2923 // Typically this method's caller will want to ensure that
2924 // xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
2925 // called after, this method, to ensure that this global object and its
2926 // compartment are consistent with other global objects.
2927 template <class T, ProtoHandleGetter GetProto>
2928 bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
2929 const JSClass* aClass, JS::RealmOptions& aOptions,
2930 JSPrincipals* aPrincipal, bool aInitStandardClasses,
2931 JS::MutableHandle<JSObject*> aGlobal) {
2932 aOptions.creationOptions()
2933 .setTrace(CreateGlobalOptions<T>::TraceGlobal)
2934 .setProfilerRealmID(GetWindowID(aNative));
2935 xpc::SetPrefableRealmOptions(aOptions);
2937 aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
2938 JS::DontFireOnNewGlobalHook, aOptions));
2939 if (!aGlobal) {
2940 NS_WARNING("Failed to create global");
2941 return false;
2944 JSAutoRealm ar(aCx, aGlobal);
2947 JS::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
2948 NS_ADDREF(aNative);
2950 aCache->SetWrapper(aGlobal);
2952 dom::AllocateProtoAndIfaceCache(
2953 aGlobal, CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
2955 if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
2956 return false;
2960 if (aInitStandardClasses && !JS::InitRealmStandardClasses(aCx)) {
2961 NS_WARNING("Failed to init standard classes");
2962 return false;
2965 JS::Handle<JSObject*> proto = GetProto(aCx);
2966 if (!proto || !JS_SetPrototype(aCx, aGlobal, proto)) {
2967 NS_WARNING("Failed to set proto");
2968 return false;
2971 bool succeeded;
2972 if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) {
2973 return false;
2975 MOZ_ASSERT(succeeded,
2976 "making a fresh global object's [[Prototype]] immutable can "
2977 "internally fail, but it should never be unsuccessful");
2979 return true;
2982 namespace binding_detail {
2984 * WebIDL getters have a "generic" JSNative that is responsible for the
2985 * following things:
2987 * 1) Determining the "this" pointer for the C++ call.
2988 * 2) Extracting the "specialized" getter from the jitinfo on the JSFunction.
2989 * 3) Calling the specialized getter.
2990 * 4) Handling exceptions as needed.
2992 * There are several variants of (1) depending on the interface involved and
2993 * there are two variants of (4) depending on whether the return type is a
2994 * Promise. We handle this by templating our generic getter on a
2995 * this-determination policy and an exception handling policy, then explicitly
2996 * instantiating the relevant template specializations.
2998 template <typename ThisPolicy, typename ExceptionPolicy>
2999 bool GenericGetter(JSContext* cx, unsigned argc, JS::Value* vp);
3002 * WebIDL setters have a "generic" JSNative that is responsible for the
3003 * following things:
3005 * 1) Determining the "this" pointer for the C++ call.
3006 * 2) Extracting the "specialized" setter from the jitinfo on the JSFunction.
3007 * 3) Calling the specialized setter.
3009 * There are several variants of (1) depending on the interface
3010 * involved. We handle this by templating our generic setter on a
3011 * this-determination policy, then explicitly instantiating the
3012 * relevant template specializations.
3014 template <typename ThisPolicy>
3015 bool GenericSetter(JSContext* cx, unsigned argc, JS::Value* vp);
3018 * WebIDL methods have a "generic" JSNative that is responsible for the
3019 * following things:
3021 * 1) Determining the "this" pointer for the C++ call.
3022 * 2) Extracting the "specialized" method from the jitinfo on the JSFunction.
3023 * 3) Calling the specialized methodx.
3024 * 4) Handling exceptions as needed.
3026 * There are several variants of (1) depending on the interface involved and
3027 * there are two variants of (4) depending on whether the return type is a
3028 * Promise. We handle this by templating our generic method on a
3029 * this-determination policy and an exception handling policy, then explicitly
3030 * instantiating the relevant template specializations.
3032 template <typename ThisPolicy, typename ExceptionPolicy>
3033 bool GenericMethod(JSContext* cx, unsigned argc, JS::Value* vp);
3035 // A this-extraction policy for normal getters/setters/methods.
3036 struct NormalThisPolicy;
3038 // A this-extraction policy for getters/setters/methods on interfaces
3039 // that are on some global's proto chain.
3040 struct MaybeGlobalThisPolicy;
3042 // A this-extraction policy for lenient getters/setters.
3043 struct LenientThisPolicy;
3045 // A this-extraction policy for cross-origin getters/setters/methods.
3046 struct CrossOriginThisPolicy;
3048 // A this-extraction policy for getters/setters/methods that should
3049 // not be allowed to be called cross-origin but expect objects that
3050 // _can_ be cross-origin.
3051 struct MaybeCrossOriginObjectThisPolicy;
3053 // A this-extraction policy which is just like
3054 // MaybeCrossOriginObjectThisPolicy but has lenient-this behavior.
3055 struct MaybeCrossOriginObjectLenientThisPolicy;
3057 // An exception-reporting policy for normal getters/setters/methods.
3058 struct ThrowExceptions;
3060 // An exception-handling policy for Promise-returning getters/methods.
3061 struct ConvertExceptionsToPromises;
3062 } // namespace binding_detail
3064 bool StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp);
3066 // ConvertExceptionToPromise should only be called when we have an error
3067 // condition (e.g. returned false from a JSAPI method). Note that there may be
3068 // no exception on cx, in which case this is an uncatchable failure that will
3069 // simply be propagated. Otherwise this method will attempt to convert the
3070 // exception to a Promise rejected with the exception that it will store in
3071 // rval.
3072 bool ConvertExceptionToPromise(JSContext* cx,
3073 JS::MutableHandle<JS::Value> rval);
3075 #ifdef DEBUG
3076 void AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
3077 JS::Handle<JS::Value> aValue);
3078 #endif
3080 bool CallerSubsumes(JSObject* aObject);
3082 MOZ_ALWAYS_INLINE bool CallerSubsumes(JS::Handle<JS::Value> aValue) {
3083 if (!aValue.isObject()) {
3084 return true;
3086 return CallerSubsumes(&aValue.toObject());
3089 template <class T, class S>
3090 inline RefPtr<T> StrongOrRawPtr(already_AddRefed<S>&& aPtr) {
3091 return std::move(aPtr);
3094 template <class T, class S>
3095 inline RefPtr<T> StrongOrRawPtr(RefPtr<S>&& aPtr) {
3096 return std::move(aPtr);
3099 template <class T, class ReturnType = std::conditional_t<IsRefcounted<T>::value,
3100 T*, UniquePtr<T>>>
3101 inline ReturnType StrongOrRawPtr(T* aPtr) {
3102 return ReturnType(aPtr);
3105 template <class T, template <typename> class SmartPtr, class S>
3106 inline void StrongOrRawPtr(SmartPtr<S>&& aPtr) = delete;
3108 template <class T>
3109 using StrongPtrForMember =
3110 std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, UniquePtr<T>>;
3112 namespace binding_detail {
3113 inline JSObject* GetHackedNamespaceProtoObject(JSContext* aCx) {
3114 return JS_NewPlainObject(aCx);
3116 } // namespace binding_detail
3118 // Resolve an id on the given global object that wants to be included in
3119 // Exposed=System webidl annotations. False return value means exception
3120 // thrown.
3121 bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
3122 JS::Handle<jsid> id, bool* resolvedp);
3124 // Enumerate all ids on the given global object that wants to be included in
3125 // Exposed=System webidl annotations. False return value means exception
3126 // thrown.
3127 bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
3129 // Slot indexes for maplike/setlike forEach functions
3130 #define FOREACH_CALLBACK_SLOT 0
3131 #define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
3133 // Backing function for running .forEach() on maplike/setlike interfaces.
3134 // Unpacks callback and maplike/setlike object from reserved slots, then runs
3135 // callback for each key (and value, for maplikes)
3136 bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
3138 // Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
3139 // for a maplike/setlike interface. If backing object does not exist, creates
3140 // backing object in the compartment of the reflector involved, making this safe
3141 // to use across compartments/via xrays. Return values of these methods will
3142 // always be in the context compartment.
3143 bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
3144 size_t aSlotIndex,
3145 JS::MutableHandle<JSObject*> aBackingObj,
3146 bool* aBackingObjCreated);
3147 bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
3148 size_t aSlotIndex,
3149 JS::MutableHandle<JSObject*> aBackingObj,
3150 bool* aBackingObjCreated);
3152 // Unpacks backing object (ES Proxy exotic object) from the reserved slot of a
3153 // reflector for a observableArray attribute. If backing object does not exist,
3154 // creates backing object in the compartment of the reflector involved, making
3155 // this safe to use across compartments/via xrays. Return values of these
3156 // methods will always be in the context compartment.
3157 bool GetObservableArrayBackingObject(
3158 JSContext* aCx, JS::Handle<JSObject*> aObj, size_t aSlotIndex,
3159 JS::MutableHandle<JSObject*> aBackingObj, bool* aBackingObjCreated,
3160 const ObservableArrayProxyHandler* aHandler, void* aOwner);
3162 // Get the desired prototype object for an object construction from the given
3163 // CallArgs. The CallArgs must be for a constructor call. The
3164 // aProtoId/aCreator arguments are used to get a default if we don't find a
3165 // prototype on the newTarget of the callargs.
3166 bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
3167 prototypes::id::ID aProtoId,
3168 CreateInterfaceObjectsMethod aCreator,
3169 JS::MutableHandle<JSObject*> aDesiredProto);
3171 // This function is expected to be called from the constructor function for an
3172 // HTML or XUL element interface; the global/callargs need to be whatever was
3173 // passed to that constructor function.
3174 already_AddRefed<Element> CreateXULOrHTMLElement(
3175 const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
3176 JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
3178 void SetUseCounter(JSObject* aObject, UseCounter aUseCounter);
3179 void SetUseCounter(UseCounterWorker aUseCounter);
3181 // Warnings
3182 void DeprecationWarning(JSContext* aCx, JSObject* aObject,
3183 DeprecatedOperations aOperation);
3185 void DeprecationWarning(const GlobalObject& aGlobal,
3186 DeprecatedOperations aOperation);
3188 // A callback to perform funToString on an interface object
3189 JSString* InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
3190 unsigned /* indent */);
3192 namespace binding_detail {
3193 // Get a JS global object that can be used for some temporary allocations. The
3194 // idea is that this should be used for situations when you need to operate in
3195 // _some_ compartment but don't care which one. A typical example is when you
3196 // have non-JS input, non-JS output, but have to go through some sort of JS
3197 // representation in the middle, so need a compartment to allocate things in.
3199 // It's VERY important that any consumers of this function only do things that
3200 // are guaranteed to be side-effect-free, even in the face of a script
3201 // environment controlled by a hostile adversary. This is because in the worker
3202 // case the global is in fact the worker global, so it and its standard objects
3203 // are controlled by the worker script. This is why this function is in the
3204 // binding_detail namespace. Any use of this function MUST be very carefully
3205 // reviewed by someone who is sufficiently devious and has a very good
3206 // understanding of all the code that will run while we're using the return
3207 // value, including the SpiderMonkey parts.
3208 JSObject* UnprivilegedJunkScopeOrWorkerGlobal(const fallible_t&);
3210 // Implementation of the [HTMLConstructor] extended attribute.
3211 bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
3212 constructors::id::ID aConstructorId,
3213 prototypes::id::ID aProtoId,
3214 CreateInterfaceObjectsMethod aCreator);
3216 // A method to test whether an attribute with the given JSJitGetterOp getter is
3217 // enabled in the given set of prefable proeprty specs. For use for toJSON
3218 // conversions. aObj is the object that would be used as the "this" value.
3219 bool IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
3220 JSJitGetterOp aGetter,
3221 const Prefable<const JSPropertySpec>* aAttributes);
3223 // A class that can be used to examine the chars of a linear string.
3224 class StringIdChars {
3225 public:
3226 // Require a non-const ref to an AutoRequireNoGC to prevent callers
3227 // from passing temporaries.
3228 StringIdChars(JS::AutoRequireNoGC& nogc, JSLinearString* str) {
3229 mIsLatin1 = JS::LinearStringHasLatin1Chars(str);
3230 if (mIsLatin1) {
3231 mLatin1Chars = JS::GetLatin1LinearStringChars(nogc, str);
3232 } else {
3233 mTwoByteChars = JS::GetTwoByteLinearStringChars(nogc, str);
3235 #ifdef DEBUG
3236 mLength = JS::GetLinearStringLength(str);
3237 #endif // DEBUG
3240 MOZ_ALWAYS_INLINE char16_t operator[](size_t index) {
3241 MOZ_ASSERT(index < mLength);
3242 if (mIsLatin1) {
3243 return mLatin1Chars[index];
3245 return mTwoByteChars[index];
3248 private:
3249 bool mIsLatin1;
3250 union {
3251 const JS::Latin1Char* mLatin1Chars;
3252 const char16_t* mTwoByteChars;
3254 #ifdef DEBUG
3255 size_t mLength;
3256 #endif // DEBUG
3259 already_AddRefed<Promise> CreateRejectedPromiseFromThrownException(
3260 JSContext* aCx, ErrorResult& aError);
3262 } // namespace binding_detail
3264 } // namespace dom
3265 } // namespace mozilla
3267 #endif /* mozilla_dom_BindingUtils_h__ */