CallbackObject.h (26053B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /** 8 * A common base class for representing WebIDL callback function and 9 * callback interface types in C++. 10 * 11 * This class implements common functionality like lifetime 12 * management, initialization with the JS object, and setup of the 13 * call environment. Subclasses are responsible for providing methods 14 * that do the call into JS as needed. 15 */ 16 17 #ifndef mozilla_dom_CallbackObject_h 18 #define mozilla_dom_CallbackObject_h 19 20 #include <cstddef> 21 #include <cstdint> 22 23 #include "js/Exception.h" 24 #include "js/RootingAPI.h" 25 #include "js/Wrapper.h" 26 #include "jsapi.h" 27 #include "mozilla/AlreadyAddRefed.h" 28 #include "mozilla/Assertions.h" 29 #include "mozilla/Attributes.h" 30 #include "mozilla/HoldDropJSObjects.h" 31 #include "mozilla/Maybe.h" 32 #include "mozilla/MemoryReporting.h" 33 #include "mozilla/RefPtr.h" 34 #include "mozilla/dom/AutoEntryScript.h" 35 #include "mozilla/dom/BindingCallContext.h" 36 #include "mozilla/dom/ScriptSettings.h" 37 #include "nsCOMPtr.h" 38 #include "nsCycleCollectionParticipant.h" 39 #include "nsID.h" 40 #include "nsIGlobalObject.h" 41 #include "nsISupports.h" 42 #include "nsISupportsUtils.h" 43 #include "nsStringFwd.h" 44 45 class JSAutoRealm; 46 class JSObject; 47 class JSTracer; 48 class nsCycleCollectionTraversalCallback; 49 struct JSContext; 50 51 namespace JS { 52 class AutoSetAsyncStackForNewCalls; 53 class Realm; 54 class Value; 55 } // namespace JS 56 57 namespace mozilla { 58 59 class ErrorResult; 60 class PromiseJobRunnable; 61 template <class T> 62 class OwningNonNull; 63 64 namespace dom { 65 66 #define DOM_CALLBACKOBJECT_IID \ 67 {0xbe74c190, 0x6d76, 0x4991, {0x84, 0xb9, 0x65, 0x06, 0x99, 0xe6, 0x93, 0x2b}} 68 69 class CallbackObjectBase { 70 public: 71 CallbackObjectBase() = default; 72 CallbackObjectBase(JSObject* aCallback, JSObject* aCallbackGlobal, 73 JSObject* aAsyncStack, nsIGlobalObject* aIncumbentGlobal) { 74 InitNoHold(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal); 75 } 76 77 // This is guaranteed to be non-null from the time the CallbackObject is 78 // created until JavaScript has had a chance to run. It will only return null 79 // after a JavaScript caller has called nukeSandbox on a Sandbox object and 80 // the cycle collector has had a chance to run, unless Reset() is explicitly 81 // called (see below). 82 // 83 // This means that any native callee which receives a CallbackObject as an 84 // argument can safely rely on the callback being non-null so long as it 85 // doesn't trigger any scripts before it accesses it. 86 JSObject* CallbackOrNull() const { 87 mCallback.exposeToActiveJS(); 88 return CallbackPreserveColor(); 89 } 90 91 JSObject* CallbackGlobalOrNull() const { 92 mCallbackGlobal.exposeToActiveJS(); 93 return mCallbackGlobal; 94 } 95 96 // Like CallbackOrNull(), but will return a new dead proxy object in the 97 // caller's realm if the callback is null. 98 JSObject* Callback(JSContext* aCx); 99 100 JSObject* GetCreationStack() const { return mCreationStack; } 101 102 void MarkForCC() { 103 mCallback.exposeToActiveJS(); 104 mCallbackGlobal.exposeToActiveJS(); 105 mCreationStack.exposeToActiveJS(); 106 } 107 108 /* 109 * This getter does not change the color of the JSObject meaning that the 110 * object returned is not guaranteed to be kept alive past the next CC. 111 */ 112 JSObject* CallbackPreserveColor() const { return mCallback.unbarrieredGet(); } 113 JSObject* CallbackGlobalPreserveColor() const { 114 return mCallbackGlobal.unbarrieredGet(); 115 } 116 117 /* 118 * If the callback is known to be non-gray, then this method can be 119 * used instead of CallbackOrNull() to avoid the overhead of 120 * ExposeObjectToActiveJS(). 121 */ 122 JSObject* CallbackKnownNotGray() const { 123 JS::AssertObjectIsNotGray(mCallback); 124 return CallbackPreserveColor(); 125 } 126 127 nsIGlobalObject* IncumbentGlobalOrNull() const { return mIncumbentGlobal; } 128 129 enum ExceptionHandling { 130 // Report any exception and don't throw it to the caller code. 131 eReportExceptions, 132 // Throw any exception to the caller code and don't report it. 133 eRethrowExceptions, 134 // Throw an exception to the caller code if the thrown exception is a 135 // binding object for a DOMException from the caller's scope, otherwise 136 // report it. 137 eRethrowContentExceptions 138 }; 139 140 // Append a UTF-8 string to aOutString that describes the callback function, 141 // for use in logging or profiler markers. 142 // The string contains the function name and its source location, if 143 // available, in the following format: 144 // "<functionName> (<sourceURL>:<lineNumber>)" 145 void GetDescription(nsACString& aOutString); 146 147 // Used for cycle collection optimization. Should return true only if all our 148 // outgoing edges are to known-live objects. In that case, there's no point 149 // traversing our edges to them, because we know they can't be collected 150 // anyway. 151 bool IsBlackForCC() const { 152 // Play it safe in case this gets called after unlink. 153 return (!mCallback || !JS::ObjectIsMarkedGray(mCallback)) && 154 (!mCallbackGlobal || !JS::ObjectIsMarkedGray(mCallbackGlobal)) && 155 (!mCreationStack || !JS::ObjectIsMarkedGray(mCreationStack)) && 156 (!mIncumbentJSGlobal || 157 !JS::ObjectIsMarkedGray(mIncumbentJSGlobal)) && 158 // mIncumbentGlobal is known-live if we have a known-live 159 // mIncumbentJSGlobal, since mIncumbentJSGlobal will keep a ref to 160 // it. At this point if mIncumbentJSGlobal is not null, it's 161 // known-live. 162 (!mIncumbentGlobal || mIncumbentJSGlobal); 163 } 164 165 protected: 166 virtual ~CallbackObjectBase() = default; 167 168 // Provide a way to clear this object's pointers to GC things after the 169 // callback has been run. Note that CallbackOrNull() will return null after 170 // this point. This should only be called if the object is known not to be 171 // used again, and no handles (e.g. those returned by CallbackPreserveColor) 172 // are in use. 173 virtual void Reset() { 174 ClearJSReferences(); 175 mIncumbentGlobal = nullptr; 176 } 177 178 friend class mozilla::PromiseJobRunnable; 179 180 inline void ClearJSReferences() { 181 mCallback = nullptr; 182 mCallbackGlobal = nullptr; 183 mCreationStack = nullptr; 184 mIncumbentJSGlobal = nullptr; 185 } 186 187 inline void InitNoHold(JSObject* aCallback, JSObject* aCallbackGlobal, 188 JSObject* aCreationStack, 189 nsIGlobalObject* aIncumbentGlobal) { 190 MOZ_ASSERT(aCallback && !mCallback); 191 MOZ_ASSERT(aCallbackGlobal); 192 MOZ_DIAGNOSTIC_ASSERT(JS::GetCompartment(aCallback) == 193 JS::GetCompartment(aCallbackGlobal)); 194 MOZ_ASSERT(JS_IsGlobalObject(aCallbackGlobal)); 195 mCallback = aCallback; 196 mCallbackGlobal = aCallbackGlobal; 197 mCreationStack = aCreationStack; 198 if (aIncumbentGlobal) { 199 mIncumbentGlobal = aIncumbentGlobal; 200 // We don't want to expose to JS here (change the color). If someone ever 201 // reads mIncumbentJSGlobal, that will expose. If not, no need to expose 202 // here. 203 mIncumbentJSGlobal = aIncumbentGlobal->GetGlobalJSObjectPreserveColor(); 204 } 205 } 206 207 void ClearJSObjects() { 208 MOZ_ASSERT_IF(mIncumbentJSGlobal, mCallback); 209 if (mCallback) { 210 ClearJSReferences(); 211 } 212 } 213 214 // For use from subclasses that want to be usable with Rooted. 215 void Trace(JSTracer* aTracer); 216 217 // mCallback is not unwrapped, so it can be a cross-compartment-wrapper. 218 // This is done to ensure that, if JS code can't call a callback f(), or get 219 // its members, directly itself, this code won't call f(), or get its members, 220 // on the code's behalf. 221 JS::Heap<JSObject*> mCallback; 222 // mCallbackGlobal is the global that we were in when we created the 223 // callback. In particular, it is guaranteed to be same-compartment with 224 // aCallback. We store it separately, because we have no way to recover the 225 // global if mCallback is a cross-compartment wrapper. 226 JS::Heap<JSObject*> mCallbackGlobal; 227 JS::Heap<JSObject*> mCreationStack; 228 // Ideally, we'd just hold a reference to the nsIGlobalObject, since that's 229 // what we need to pass to AutoIncumbentScript. Unfortunately, that doesn't 230 // hold the actual JS global alive. So we maintain an additional pointer to 231 // the JS global itself so that we can trace it. 232 // 233 // At some point we should consider trying to make native globals hold their 234 // scripted global alive, at which point we can get rid of the duplication 235 // here. 236 nsCOMPtr<nsIGlobalObject> mIncumbentGlobal; 237 JS::TenuredHeap<JSObject*> mIncumbentJSGlobal; 238 }; 239 240 /** 241 * A class that performs whatever setup we need to safely make a 242 * call while this class is on the stack, After the constructor 243 * returns, the call is safe to make if GetContext() returns 244 * non-null. 245 */ 246 class MOZ_STACK_CLASS CallSetup { 247 public: 248 // If aExceptionHandling == eRethrowContentExceptions then aRealm 249 // needs to be set to the realm in which exceptions will be rethrown. 250 // 251 // If aExceptionHandling == eRethrowExceptions then aRealm may be set 252 // to the realm in which exceptions will be rethrown. In that case 253 // they will only be rethrown if that realm's principal subsumes the 254 // principal of our (unwrapped) callback. 255 CallSetup(CallbackObjectBase* aCallback, ErrorResult& aRv, 256 const char* aExecutionReason, 257 CallbackObjectBase::ExceptionHandling aExceptionHandling, 258 JS::Realm* aRealm = nullptr, bool aIsJSImplementedWebIDL = false); 259 260 CallSetup(JS::Handle<JSObject*> aCallbackGlobal, 261 nsIGlobalObject* aIncumbentGlobal, 262 JS::Handle<JSObject*> aCreationStack, ErrorResult& aRv, 263 const char* aExecutionReason, 264 CallbackObjectBase::ExceptionHandling aExceptionHandling, 265 JS::Realm* aRealm = nullptr); 266 267 MOZ_CAN_RUN_SCRIPT ~CallSetup(); 268 269 JSContext* GetContext() const { return mCx; } 270 271 // Safe to call this after the constructor has run without throwing on the 272 // ErrorResult it was handed. 273 BindingCallContext& GetCallContext() { return *mCallContext; } 274 275 private: 276 CallSetup(CallbackObjectBase* aCallback, ErrorResult& aRv, 277 const char* aExecutionReason, 278 CallbackObjectBase::ExceptionHandling aExceptionHandling, 279 JS::Realm* aRealm, bool aIsJSImplementedWebIDL, 280 CycleCollectedJSContext* aCCJS); 281 // Private delegating constructor for common initialization 282 CallSetup(ErrorResult& aRv, 283 CallbackObjectBase::ExceptionHandling aExceptionHandling, 284 JS::Realm* aRealm, bool aIsMainThread, 285 CycleCollectedJSContext* aCCJS); 286 287 // We better not get copy-constructed 288 CallSetup(const CallSetup&) = delete; 289 290 bool ShouldRethrowException(JS::Handle<JS::Value> aException); 291 292 static nsIGlobalObject* GetActiveGlobalObjectForCall( 293 JS::Handle<JSObject*> callbackOrGlobal, bool aIsMainThread, 294 bool aIsJSImplementedWebIDL, ErrorResult& aRv); 295 296 static bool CheckBeforeExecution(nsIGlobalObject* aGlobalObject, 297 JSObject* aCallbackOrGlobal, 298 bool aIsJSImplementedWebIDL, 299 ErrorResult& aRv); 300 301 // Perform the final setup work. If this succeeds, mCx is set and we are able 302 // to run the callback with the appropriate environment. 303 void SetupForExecution(nsIGlobalObject* aGlobalObject, 304 nsIGlobalObject* aIncumbentGlobal, 305 JS::Handle<JSObject*> aCallbackOrGlobal, 306 JS::Handle<JSObject*> aCallbackGlobal, 307 JS::Handle<JSObject*> aCreationStack, 308 nsIPrincipal* aWebIDLCallerPrincipal, 309 const char* aExecutionReason, ErrorResult& aRv); 310 311 // Members which can go away whenever 312 JSContext* mCx; 313 314 // Caller's realm. This will only have a sensible value if 315 // mExceptionHandling == eRethrowContentExceptions. 316 JS::Realm* mRealm; 317 318 // And now members whose construction/destruction order we need to control. 319 Maybe<AutoEntryScript> mAutoEntryScript; 320 Maybe<AutoIncumbentScript> mAutoIncumbentScript; 321 322 Maybe<JS::Rooted<JSObject*>> mRootedCallable; 323 Maybe<JS::AutoSetAsyncStackForNewCalls> mAsyncStackSetter; 324 325 // Can't construct a JSAutoRealm without a JSContext either. Also, 326 // Put mAr after mAutoEntryScript so that we exit the realm before we 327 // pop the script settings stack. Though in practice we'll often manually 328 // order those two things. 329 Maybe<JSAutoRealm> mAr; 330 331 // Our BindingCallContext. This is a Maybe so we can avoid constructing it 332 // until after we have a JSContext to construct it with. 333 Maybe<BindingCallContext> mCallContext; 334 335 // An ErrorResult to possibly re-throw exceptions on and whether 336 // we should re-throw them. 337 ErrorResult& mErrorResult; 338 const CallbackObjectBase::ExceptionHandling mExceptionHandling; 339 const bool mIsMainThread; 340 }; 341 342 class CallbackObject : public nsISupports, 343 public CallbackObjectBase, 344 public JSHolderBase { 345 public: 346 NS_INLINE_DECL_STATIC_IID(DOM_CALLBACKOBJECT_IID) 347 348 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 349 NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CallbackObject) 350 351 // The caller may pass a global object which will act as an override for the 352 // incumbent script settings object when the callback is invoked (overriding 353 // the entry point computed from aCallback). If no override is required, the 354 // caller should pass null. |aCx| is used to capture the current 355 // stack, which is later used as an async parent when the callback 356 // is invoked. aCx can be nullptr, in which case no stack is 357 // captured. 358 explicit CallbackObject(JSContext* aCx, JS::Handle<JSObject*> aCallback, 359 JS::Handle<JSObject*> aCallbackGlobal, 360 nsIGlobalObject* aIncumbentGlobal) { 361 if (aCx && JS::IsAsyncStackCaptureEnabledForRealm(aCx)) { 362 JS::Rooted<JSObject*> stack(aCx); 363 if (!JS::CaptureCurrentStack(aCx, &stack)) { 364 JS_ClearPendingException(aCx); 365 } 366 Init(aCallback, aCallbackGlobal, stack, aIncumbentGlobal); 367 } else { 368 Init(aCallback, aCallbackGlobal, nullptr, aIncumbentGlobal); 369 } 370 } 371 372 // Instead of capturing the current stack to use as an async parent when the 373 // callback is invoked, the caller can use this overload to pass in a stack 374 // for that purpose. 375 explicit CallbackObject(JSObject* aCallback, JSObject* aCallbackGlobal, 376 JSObject* aAsyncStack, 377 nsIGlobalObject* aIncumbentGlobal) { 378 Init(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal); 379 } 380 381 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { 382 return aMallocSizeOf(this); 383 } 384 385 void Reset() final { 386 CallbackObjectBase::Reset(); 387 mozilla::DropJSObjectsWithKey(this); 388 } 389 390 protected: 391 virtual ~CallbackObject() { mozilla::DropJSObjectsWithKey(this); } 392 393 explicit CallbackObject(CallbackObject* aCallbackObject) { 394 Init(aCallbackObject->mCallback, aCallbackObject->mCallbackGlobal, 395 aCallbackObject->mCreationStack, aCallbackObject->mIncumbentGlobal); 396 } 397 398 // For use from subclasses that want to be traced for a bit then possibly 399 // switch to HoldJSObjects and do other slow JS-related init work we might do. 400 // If we have more than one owner, this will HoldJSObjects and do said slow 401 // init work; otherwise it will just forget all our JS references. 402 void FinishSlowJSInitIfMoreThanOneOwner(JSContext* aCx); 403 404 // Struct used as a way to force a CallbackObject constructor to not call 405 // HoldJSObjects. We're putting it here so that CallbackObject subclasses will 406 // have access to it, but outside code will not. 407 // 408 // Places that use this need to ensure that the callback is traced (e.g. via a 409 // Rooted) until the HoldJSObjects call happens. 410 struct FastCallbackConstructor {}; 411 412 // Just like the public version without the FastCallbackConstructor argument, 413 // except for not calling HoldJSObjects and not capturing async stacks (on the 414 // assumption that we will do that last whenever we decide to actually 415 // HoldJSObjects; see FinishSlowJSInitIfMoreThanOneOwner). If you use this, 416 // you MUST ensure that the object is traced until the HoldJSObjects happens! 417 CallbackObject(JSObject* aCallback, JSObject* aCallbackGlobal, 418 const FastCallbackConstructor&) { 419 InitNoHold(aCallback, aCallbackGlobal, nullptr, nullptr); 420 } 421 422 bool operator==(const CallbackObject& aOther) const { 423 JSObject* wrappedThis = CallbackPreserveColor(); 424 JSObject* wrappedOther = aOther.CallbackPreserveColor(); 425 if (!wrappedThis || !wrappedOther) { 426 return this == &aOther; 427 } 428 429 JSObject* thisObj = js::UncheckedUnwrap(wrappedThis); 430 JSObject* otherObj = js::UncheckedUnwrap(wrappedOther); 431 return thisObj == otherObj; 432 } 433 434 class JSObjectsDropper final { 435 public: 436 explicit JSObjectsDropper(CallbackObject* aHolder) : mHolder(aHolder) {} 437 438 ~JSObjectsDropper() { mHolder->ClearJSObjects(); } 439 440 private: 441 RefPtr<CallbackObject> mHolder; 442 }; 443 444 private: 445 CallbackObject(const CallbackObject&) = delete; 446 CallbackObject& operator=(const CallbackObject&) = delete; 447 448 inline void Init(JSObject* aCallback, JSObject* aCallbackGlobal, 449 JSObject* aCreationStack, 450 nsIGlobalObject* aIncumbentGlobal) { 451 // Set script objects before we hold, on the off chance that a GC could 452 // somehow happen in there... (which would be pretty odd, granted). 453 InitNoHold(aCallback, aCallbackGlobal, aCreationStack, aIncumbentGlobal); 454 mozilla::HoldJSObjectsWithKey(this); 455 } 456 }; 457 458 template <class WebIDLCallbackT, class XPCOMCallbackT> 459 class CallbackObjectHolder; 460 461 template <class T, class U> 462 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField); 463 464 class CallbackObjectHolderBase { 465 protected: 466 // Returns null on all failures 467 already_AddRefed<nsISupports> ToXPCOMCallback(CallbackObject* aCallback, 468 const nsIID& aIID) const; 469 }; 470 471 template <class WebIDLCallbackT, class XPCOMCallbackT> 472 class CallbackObjectHolder : CallbackObjectHolderBase { 473 /** 474 * A class which stores either a WebIDLCallbackT* or an XPCOMCallbackT*. Both 475 * types must inherit from nsISupports. The pointer that's stored can be 476 * null. 477 * 478 * When storing a WebIDLCallbackT*, mPtrBits is set to the pointer value. 479 * When storing an XPCOMCallbackT*, mPtrBits is the pointer value with low bit 480 * set. 481 */ 482 public: 483 explicit CallbackObjectHolder(WebIDLCallbackT* aCallback) 484 : mPtrBits(reinterpret_cast<uintptr_t>(aCallback)) { 485 NS_IF_ADDREF(aCallback); 486 } 487 488 explicit CallbackObjectHolder(XPCOMCallbackT* aCallback) 489 : mPtrBits(reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag) { 490 NS_IF_ADDREF(aCallback); 491 } 492 493 CallbackObjectHolder(CallbackObjectHolder&& aOther) 494 : mPtrBits(aOther.mPtrBits) { 495 aOther.mPtrBits = 0; 496 static_assert(sizeof(CallbackObjectHolder) == sizeof(void*), 497 "This object is expected to be as small as a pointer, and it " 498 "is currently passed by value in various places. If it is " 499 "bloating, we may want to pass it by reference then."); 500 } 501 502 CallbackObjectHolder(const CallbackObjectHolder& aOther) = delete; 503 504 CallbackObjectHolder() : mPtrBits(0) {} 505 506 ~CallbackObjectHolder() { UnlinkSelf(); } 507 508 void operator=(WebIDLCallbackT* aCallback) { 509 UnlinkSelf(); 510 mPtrBits = reinterpret_cast<uintptr_t>(aCallback); 511 NS_IF_ADDREF(aCallback); 512 } 513 514 void operator=(XPCOMCallbackT* aCallback) { 515 UnlinkSelf(); 516 mPtrBits = reinterpret_cast<uintptr_t>(aCallback) | XPCOMCallbackFlag; 517 NS_IF_ADDREF(aCallback); 518 } 519 520 void operator=(CallbackObjectHolder&& aOther) { 521 UnlinkSelf(); 522 mPtrBits = aOther.mPtrBits; 523 aOther.mPtrBits = 0; 524 } 525 526 void operator=(const CallbackObjectHolder& aOther) = delete; 527 528 void Reset() { UnlinkSelf(); } 529 530 nsISupports* GetISupports() const { 531 return reinterpret_cast<nsISupports*>(mPtrBits & ~XPCOMCallbackFlag); 532 } 533 534 already_AddRefed<nsISupports> Forget() { 535 // This can be called from random threads. Make sure to not refcount things 536 // in here! 537 nsISupports* supp = GetISupports(); 538 mPtrBits = 0; 539 return dont_AddRef(supp); 540 } 541 542 // Boolean conversion operator so people can use this in boolean tests 543 explicit operator bool() const { return GetISupports(); } 544 545 CallbackObjectHolder Clone() const { 546 CallbackObjectHolder result; 547 result.mPtrBits = mPtrBits; 548 NS_IF_ADDREF(GetISupports()); 549 return result; 550 } 551 552 // Even if HasWebIDLCallback returns true, GetWebIDLCallback() might still 553 // return null. 554 bool HasWebIDLCallback() const { return !(mPtrBits & XPCOMCallbackFlag); } 555 556 WebIDLCallbackT* GetWebIDLCallback() const { 557 MOZ_ASSERT(HasWebIDLCallback()); 558 return reinterpret_cast<WebIDLCallbackT*>(mPtrBits); 559 } 560 561 XPCOMCallbackT* GetXPCOMCallback() const { 562 MOZ_ASSERT(!HasWebIDLCallback()); 563 return reinterpret_cast<XPCOMCallbackT*>(mPtrBits & ~XPCOMCallbackFlag); 564 } 565 566 bool operator==(WebIDLCallbackT* aOtherCallback) const { 567 if (!aOtherCallback) { 568 // If other is null, then we must be null to be equal. 569 return !GetISupports(); 570 } 571 572 if (!HasWebIDLCallback() || !GetWebIDLCallback()) { 573 // If other is non-null, then we can't be equal if we have a 574 // non-WebIDL callback or a null callback. 575 return false; 576 } 577 578 return *GetWebIDLCallback() == *aOtherCallback; 579 } 580 581 bool operator==(XPCOMCallbackT* aOtherCallback) const { 582 return (!aOtherCallback && !GetISupports()) || 583 (!HasWebIDLCallback() && GetXPCOMCallback() == aOtherCallback); 584 } 585 586 bool operator==(const CallbackObjectHolder& aOtherCallback) const { 587 if (aOtherCallback.HasWebIDLCallback()) { 588 return *this == aOtherCallback.GetWebIDLCallback(); 589 } 590 591 return *this == aOtherCallback.GetXPCOMCallback(); 592 } 593 594 // Try to return an XPCOMCallbackT version of this object. 595 already_AddRefed<XPCOMCallbackT> ToXPCOMCallback() const { 596 if (!HasWebIDLCallback()) { 597 RefPtr<XPCOMCallbackT> callback = GetXPCOMCallback(); 598 return callback.forget(); 599 } 600 601 nsCOMPtr<nsISupports> supp = CallbackObjectHolderBase::ToXPCOMCallback( 602 GetWebIDLCallback(), NS_GET_IID(XPCOMCallbackT)); 603 if (supp) { 604 // ToXPCOMCallback already did the right QI for us. 605 return supp.forget().downcast<XPCOMCallbackT>(); 606 } 607 return nullptr; 608 } 609 610 // Try to return a WebIDLCallbackT version of this object. 611 already_AddRefed<WebIDLCallbackT> ToWebIDLCallback() const { 612 if (HasWebIDLCallback()) { 613 RefPtr<WebIDLCallbackT> callback = GetWebIDLCallback(); 614 return callback.forget(); 615 } 616 return nullptr; 617 } 618 619 private: 620 static const uintptr_t XPCOMCallbackFlag = 1u; 621 622 friend void ImplCycleCollectionUnlink<WebIDLCallbackT, XPCOMCallbackT>( 623 CallbackObjectHolder& aField); 624 625 void UnlinkSelf() { 626 // NS_IF_RELEASE because we might have been unlinked before 627 nsISupports* ptr = GetISupports(); 628 // Clear mPtrBits before the release to prevent reentrance. 629 mPtrBits = 0; 630 NS_IF_RELEASE(ptr); 631 } 632 633 uintptr_t mPtrBits; 634 }; 635 636 template <class T, class U> 637 inline void ImplCycleCollectionTraverse( 638 nsCycleCollectionTraversalCallback& aCallback, 639 CallbackObjectHolder<T, U>& aField, const char* aName, 640 uint32_t aFlags = 0) { 641 if (aField) { 642 CycleCollectionNoteChild(aCallback, aField.GetISupports(), aName, aFlags); 643 } 644 } 645 646 template <class T, class U> 647 void ImplCycleCollectionUnlink(CallbackObjectHolder<T, U>& aField) { 648 aField.UnlinkSelf(); 649 } 650 651 // T is expected to be a RefPtr or OwningNonNull around a CallbackObject 652 // subclass. This class is used in bindings to safely handle Fast* callbacks; 653 // it ensures that the callback is traced, and that if something is holding onto 654 // the callback when we're done with it HoldJSObjects is called. 655 // 656 // Since we effectively hold a ref to a refcounted thing (like RefPtr or 657 // OwningNonNull), we are also MOZ_IS_SMARTPTR_TO_REFCOUNTED for static analysis 658 // purposes. 659 template <typename T> 660 class MOZ_RAII MOZ_IS_SMARTPTR_TO_REFCOUNTED RootedCallback 661 : public JS::Rooted<T> { 662 public: 663 explicit RootedCallback(JSContext* cx) : JS::Rooted<T>(cx), mCx(cx) {} 664 665 // We need a way to make assignment from pointers (how we're normally used) 666 // work. 667 template <typename S> 668 void operator=(S* arg) { 669 this->get().operator=(arg); 670 } 671 672 // But nullptr can't use the above template, because it doesn't know which S 673 // to select. So we need a special overload for nullptr. 674 void operator=(decltype(nullptr) arg) { this->get().operator=(arg); } 675 676 // Codegen relies on being able to do CallbackOrNull() and Callback() on us. 677 JSObject* CallbackOrNull() const { return this->get()->CallbackOrNull(); } 678 679 JSObject* Callback(JSContext* aCx) const { 680 return this->get()->Callback(aCx); 681 } 682 683 ~RootedCallback() { 684 // Ensure that our callback starts holding on to its own JS objects as 685 // needed. We really do need to check that things are initialized even when 686 // T is OwningNonNull, because we might be running before the OwningNonNull 687 // ever got assigned to! 688 if (IsInitialized(this->get())) { 689 this->get()->FinishSlowJSInitIfMoreThanOneOwner(mCx); 690 } 691 } 692 693 private: 694 template <typename U> 695 static bool IsInitialized(U& aArg); // Not implemented 696 697 template <typename U> 698 static bool IsInitialized(RefPtr<U>& aRefPtr) { 699 return aRefPtr; 700 } 701 702 template <typename U> 703 static bool IsInitialized(OwningNonNull<U>& aOwningNonNull) { 704 return aOwningNonNull.isInitialized(); 705 } 706 707 JSContext* mCx; 708 }; 709 710 } // namespace dom 711 } // namespace mozilla 712 713 #endif // mozilla_dom_CallbackObject_h