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