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