Promise.h (34507B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 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 #ifndef js_Promise_h 8 #define js_Promise_h 9 10 #include "mozilla/Attributes.h" 11 12 #include "jstypes.h" 13 14 #include "js/RootingAPI.h" 15 #include "js/TypeDecls.h" 16 #include "js/UniquePtr.h" 17 18 namespace JS { 19 20 class JS_PUBLIC_API AutoDebuggerJobQueueInterruption; 21 22 /** 23 * Abstract base class for an ECMAScript Job Queue: 24 * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-jobs-and-job-queues 25 * 26 * SpiderMonkey doesn't schedule Promise resolution jobs itself; instead, the 27 * embedding can provide an instance of this class SpiderMonkey can use to do 28 * that scheduling. 29 * 30 * The JavaScript shell includes a simple implementation adequate for running 31 * tests. Browsers need to augment job handling to meet their own additional 32 * requirements, so they can provide their own implementation. 33 */ 34 class JS_PUBLIC_API JobQueue { 35 public: 36 virtual ~JobQueue() = default; 37 38 /** 39 * Ask the embedding for the host defined data. 40 * 41 * This is the step 5 in 42 * https://html.spec.whatwg.org/multipage/webappapis.html#hostmakejobcallback 43 * 44 * SpiderMonkey doesn't itself have a notion of host defined data as defined 45 * by the HTML spec, so we need the embedding to provide this. See 46 * dom/script/ScriptSettings.h for details. 47 * 48 * If the embedding has the host defined data, this method should return the 49 * host defined data via the `data` out parameter and return `true`. 50 * The object in the `data` out parameter can belong to any compartment. 51 * If the embedding doesn't need the host defined data, this method should 52 * set the `data` out parameter to `nullptr` and return `true`. 53 * If any error happens while generating the host defined data, this method 54 * should set a pending exception to `cx` and return `false`. 55 */ 56 virtual bool getHostDefinedData(JSContext* cx, 57 JS::MutableHandle<JSObject*> data) const = 0; 58 59 /** 60 * If the embedding has a host-defined global, return it. This is used when 61 * we are able to optimize out the host defined data, as the embedding may 62 * still require this when running jobs. 63 * 64 * In Gecko, this is used for dealing with the incumbent global. 65 */ 66 virtual bool getHostDefinedGlobal( 67 JSContext* cx, JS::MutableHandle<JSObject*> data) const = 0; 68 69 /** 70 * Enqueue a reaction job `job` for `promise`, which was allocated at 71 * `allocationSite`. Provide `hostDefineData` as the host defined data for 72 * the reaction job's execution. 73 * 74 * The `hostDefinedData` value comes from `getHostDefinedData` method. 75 * The object is unwrapped, and it can belong to a different compartment 76 * than the current compartment. It can be `nullptr` if `getHostDefinedData` 77 * returns `nullptr`. 78 * 79 * `promise` can be null if the promise is optimized out. 80 * `promise` is guaranteed not to be optimized out if the promise has 81 * non-default user-interaction flag. 82 */ 83 virtual bool enqueuePromiseJob(JSContext* cx, JS::HandleObject promise, 84 JS::HandleObject job, 85 JS::HandleObject allocationSite, 86 JS::HandleObject hostDefinedData) = 0; 87 88 /** 89 * Run all jobs in the queue. Running one job may enqueue others; continue to 90 * run jobs until the queue is empty. 91 * 92 * Calling this method at the wrong time can break the web. The HTML spec 93 * indicates exactly when the job queue should be drained (in HTML jargon, 94 * when it should "perform a microtask checkpoint"), and doing so at other 95 * times can incompatibly change the semantics of programs that use promises 96 * or other microtask-based features. 97 * 98 * This method is called only via AutoDebuggerJobQueueInterruption, used by 99 * the Debugger API implementation to ensure that the debuggee's job queue is 100 * protected from the debugger's own activity. See the comments on 101 * AutoDebuggerJobQueueInterruption. 102 */ 103 virtual void runJobs(JSContext* cx) = 0; 104 105 /** 106 * Return true if the job queue is empty, false otherwise. 107 */ 108 virtual bool empty() const = 0; 109 110 /** 111 * Returns true if the job queue stops draining, which results in `empty()` 112 * being false after `runJobs()`. 113 */ 114 virtual bool isDrainingStopped() const = 0; 115 116 /* 117 * When enqueueing a job, should the job be enqueued in the debug queue 118 * rather than the regular queue? 119 */ 120 virtual bool useDebugQueue(JS::Handle<JSObject*> global) const { 121 return false; 122 } 123 124 /** 125 * Trace hook for non-GCThing microtask values. 126 * 127 * This hook is called during GC tracing for microtask queue values that are 128 * not GC-things. It allows the embedding to provide custom tracing for 129 * values stored in the microtask queue, particularly for Private values 130 * that represent embedding-specific task representations. 131 * 132 * If the value is not a GC thing, this method will be called to allow the 133 * embedding to trace any GC-reachable data associated with the value. 134 * 135 * The default implementation does nothing. 136 */ 137 virtual void traceNonGCThingMicroTask(JSTracer* trc, JS::Value* valuePtr) {} 138 139 protected: 140 friend class AutoDebuggerJobQueueInterruption; 141 142 /** 143 * A saved job queue, represented however the JobQueue implementation pleases. 144 * Use AutoDebuggerJobQueueInterruption rather than trying to construct one of 145 * these directly; see documentation there. 146 * 147 * Destructing an instance of this class should assert that the current queue 148 * is empty, and then restore the queue the instance captured. 149 */ 150 class SavedJobQueue { 151 public: 152 virtual ~SavedJobQueue() = default; 153 }; 154 155 /** 156 * Capture this JobQueue's current job queue as a SavedJobQueue and return it, 157 * leaving the JobQueue's job queue empty. Destroying the returned object 158 * should assert that this JobQueue's current job queue is empty, and restore 159 * the original queue. 160 * 161 * On OOM, this should call JS_ReportOutOfMemory on the given JSContext, 162 * and return a null UniquePtr. 163 */ 164 virtual js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) = 0; 165 }; 166 167 /** 168 * Tell SpiderMonkey to use `queue` to schedule promise reactions. 169 * 170 * SpiderMonkey does not take ownership of the queue; it is the embedding's 171 * responsibility to clean it up after the runtime is destroyed. 172 */ 173 extern JS_PUBLIC_API void SetJobQueue(JSContext* cx, JobQueue* queue); 174 175 /** 176 * [SMDOC] Protecting the debuggee's job/microtask queue from debugger activity. 177 * 178 * When the JavaScript debugger interrupts the execution of some debuggee code 179 * (for a breakpoint, for example), the debuggee's execution must be paused 180 * while the developer takes time to look at it. During this interruption, other 181 * tabs should remain active and usable. If the debuggee shares a main thread 182 * with non-debuggee tabs, that means that the thread will have to process 183 * non-debuggee HTML tasks and microtasks as usual, even as the debuggee's are 184 * on hold until the debugger lets it continue execution. (Letting debuggee 185 * microtasks run during the interruption would mean that, from the debuggee's 186 * point of view, their side effects would take place wherever the breakpoint 187 * was set - in general, not a place other code should ever run, and a violation 188 * of the run-to-completion rule.) 189 * 190 * This means that, even though the timing and ordering of microtasks is 191 * carefully specified by the standard - and important to preserve for 192 * compatibility and predictability - debugger use may, correctly, have the 193 * effect of reordering microtasks. During the interruption, microtasks enqueued 194 * by non-debuggee tabs must run immediately alongside their HTML tasks as 195 * usual, whereas any debuggee microtasks that were in the queue when the 196 * interruption began must wait for the debuggee to be continued - and thus run 197 * after microtasks enqueued after they were. 198 * 199 * Fortunately, this reordering is visible only at the global level: when 200 * implemented correctly, it is not detectable by an individual debuggee. Note 201 * that a debuggee should generally be a complete unit of similar-origin related 202 * browsing contexts. Since non-debuggee activity falls outside that unit, it 203 * should never be visible to the debuggee (except via mechanisms that are 204 * already asynchronous, like events), so the debuggee should be unable to 205 * detect non-debuggee microtasks running when they normally would not. As long 206 * as behavior *visible to the debuggee* is unaffected by the interruption, we 207 * have respected the spirit of the rule. 208 * 209 * Of course, even as we accept the general principle that interrupting the 210 * debuggee should have as little detectable effect as possible, we still permit 211 * the developer to do things like evaluate expressions at the console that have 212 * arbitrary effects on the debuggee's state—effects that could never occur 213 * naturally at that point in the program. But since these are explicitly 214 * requested by the developer, who presumably knows what they're doing, we 215 * support this as best we can. If the developer evaluates an expression in the 216 * console that resolves a promise, it seems most natural for the promise's 217 * reaction microtasks to run immediately, within the interruption. This is an 218 * 'unnatural' time for the microtasks to run, but no more unnatural than the 219 * evaluation that triggered them. 220 * 221 * So the overall behavior we need is as follows: 222 * 223 * - When the debugger interrupts a debuggee, the debuggee's microtask queue 224 * must be saved. 225 * 226 * - When debuggee execution resumes, the debuggee's microtask queue must be 227 * restored exactly as it was when the interruption occurred. 228 * 229 * - Non-debuggee task and microtask execution must take place normally during 230 * the interruption. 231 * 232 * Since each HTML task begins with an empty microtask queue, and it should not 233 * be possible for a task to mix debuggee and non-debuggee code, interrupting a 234 * debuggee should always find a microtask queue containing exclusively debuggee 235 * microtasks, if any. So saving and restoring the microtask queue should affect 236 * only the debuggee, not any non-debuggee content. 237 * 238 * AutoDebuggerJobQueueInterruption 239 * -------------------------------- 240 * 241 * AutoDebuggerJobQueueInterruption is an RAII class, meant for use by the 242 * Debugger API implementation, that takes care of saving and restoring the 243 * queue. 244 * 245 * Constructing and initializing an instance of AutoDebuggerJobQueueInterruption 246 * sets aside the given JSContext's job queue, leaving the JSContext's queue 247 * empty. When the AutoDebuggerJobQueueInterruption instance is destroyed, it 248 * asserts that the JSContext's current job queue (holding jobs enqueued while 249 * the AutoDebuggerJobQueueInterruption was alive) is empty, and restores the 250 * saved queue to the JSContext. 251 * 252 * Since the Debugger API's behavior is up to us, we can specify that Debugger 253 * hooks begin execution with an empty job queue, and that we drain the queue 254 * after each hook function has run. This drain will be visible to debugger 255 * hooks, and makes hook calls resemble HTML tasks, with their own automatic 256 * microtask checkpoint. But, the drain will be invisible to the debuggee, as 257 * its queue is preserved across the hook invocation. 258 * 259 * To protect the debuggee's job queue, Debugger takes care to invoke callback 260 * functions only within the scope of an AutoDebuggerJobQueueInterruption 261 * instance. 262 * 263 * Why not let the hook functions themselves take care of this? 264 * ------------------------------------------------------------ 265 * 266 * Certainly, we could leave responsibility for saving and restoring the job 267 * queue to the Debugger hook functions themselves. 268 * 269 * In fact, early versions of this change tried making the devtools server save 270 * and restore the queue explicitly, but because hooks are set and changed in 271 * numerous places, it was hard to be confident that every case had been 272 * covered, and it seemed that future changes could easily introduce new holes. 273 * 274 * Later versions of this change modified the accessor properties on the 275 * Debugger objects' prototypes to automatically protect the job queue when 276 * calling hooks, but the effect was essentially a monkeypatch applied to an API 277 * we defined and control, which doesn't make sense. 278 * 279 * In the end, since promises have become such a pervasive part of JavaScript 280 * programming, almost any imaginable use of Debugger would need to provide some 281 * kind of protection for the debuggee's job queue, so it makes sense to simply 282 * handle it once, carefully, in the implementation of Debugger itself. 283 */ 284 class MOZ_RAII JS_PUBLIC_API AutoDebuggerJobQueueInterruption { 285 public: 286 explicit AutoDebuggerJobQueueInterruption(); 287 ~AutoDebuggerJobQueueInterruption(); 288 289 bool init(JSContext* cx); 290 bool initialized() const { return !!saved; } 291 292 /** 293 * Drain the job queue. (In HTML terminology, perform a microtask checkpoint.) 294 * 295 * To make Debugger hook calls more like HTML tasks or ECMAScript jobs, 296 * Debugger promises that each hook begins execution with a clean microtask 297 * queue, and that a microtask checkpoint (queue drain) takes place after each 298 * hook returns, successfully or otherwise. 299 * 300 * To ensure these debugger-introduced microtask checkpoints serve only the 301 * hook's microtasks, and never affect the debuggee's, the Debugger API 302 * implementation uses only this method to perform the checkpoints, thereby 303 * statically ensuring that an AutoDebuggerJobQueueInterruption is in scope to 304 * protect the debuggee. 305 * 306 * SavedJobQueue implementations are required to assert that the queue is 307 * empty before restoring the debuggee's queue. If the Debugger API ever fails 308 * to perform a microtask checkpoint after calling a hook, that assertion will 309 * fail, catching the mistake. 310 */ 311 void runJobs(); 312 313 private: 314 JSContext* cx; 315 js::UniquePtr<JobQueue::SavedJobQueue> saved; 316 }; 317 318 enum class PromiseRejectionHandlingState { Unhandled, Handled }; 319 320 typedef void (*PromiseRejectionTrackerCallback)( 321 JSContext* cx, bool mutedErrors, JS::HandleObject promise, 322 JS::PromiseRejectionHandlingState state, void* data); 323 324 /** 325 * Sets the callback that's invoked whenever a Promise is rejected without 326 * a rejection handler, and when a Promise that was previously rejected 327 * without a handler gets a handler attached. 328 */ 329 extern JS_PUBLIC_API void SetPromiseRejectionTrackerCallback( 330 JSContext* cx, PromiseRejectionTrackerCallback callback, 331 void* data = nullptr); 332 333 /** 334 * Inform the runtime that the job queue is empty and the embedding is going to 335 * execute its last promise job. The runtime may now choose to skip creating 336 * promise jobs for asynchronous execution and instead continue execution 337 * synchronously. More specifically, this optimization is used to skip the 338 * standard job queuing behavior for `await` operations in async functions. 339 * 340 * This function may be called before executing the last job in the job queue. 341 * When it was called, JobQueueMayNotBeEmpty must be called in order to restore 342 * the default job queuing behavior before the embedding enqueues its next job 343 * into the job queue. 344 */ 345 extern JS_PUBLIC_API void JobQueueIsEmpty(JSContext* cx); 346 347 /** 348 * Inform the runtime that job queue is no longer empty. The runtime can now no 349 * longer skip creating promise jobs for asynchronous execution, because 350 * pending jobs in the job queue must be executed first to preserve the FIFO 351 * (first in - first out) property of the queue. This effectively undoes 352 * JobQueueIsEmpty and re-enables the standard job queuing behavior. 353 * 354 * This function must be called whenever enqueuing a job to the job queue when 355 * JobQueueIsEmpty was called previously. 356 */ 357 extern JS_PUBLIC_API void JobQueueMayNotBeEmpty(JSContext* cx); 358 359 /** 360 * Returns a new instance of the Promise builtin class in the current 361 * compartment, with the right slot layout. 362 * 363 * The `executor` can be a `nullptr`. In that case, the only way to resolve or 364 * reject the returned promise is via the `JS::ResolvePromise` and 365 * `JS::RejectPromise` JSAPI functions. 366 */ 367 extern JS_PUBLIC_API JSObject* NewPromiseObject(JSContext* cx, 368 JS::HandleObject executor); 369 370 /** 371 * Returns true if the given object is an unwrapped PromiseObject, false 372 * otherwise. 373 */ 374 extern JS_PUBLIC_API bool IsPromiseObject(JS::HandleObject obj); 375 376 /** 377 * Returns the current compartment's original Promise constructor. 378 */ 379 extern JS_PUBLIC_API JSObject* GetPromiseConstructor(JSContext* cx); 380 381 /** 382 * Returns the current compartment's original Promise.prototype. 383 */ 384 extern JS_PUBLIC_API JSObject* GetPromisePrototype(JSContext* cx); 385 386 // Keep this in sync with the PROMISE_STATE defines in SelfHostingDefines.h. 387 enum class PromiseState { Pending, Fulfilled, Rejected }; 388 389 /** 390 * Returns the given Promise's state as a JS::PromiseState enum value. 391 * 392 * Returns JS::PromiseState::Pending if the given object is a wrapper that 393 * can't safely be unwrapped. 394 */ 395 extern JS_PUBLIC_API PromiseState GetPromiseState(JS::HandleObject promise); 396 397 /** 398 * Returns the given Promise's process-unique ID. 399 */ 400 JS_PUBLIC_API uint64_t GetPromiseID(JS::HandleObject promise); 401 402 /** 403 * Returns the given Promise's result: either the resolution value for 404 * fulfilled promises, or the rejection reason for rejected ones. 405 */ 406 extern JS_PUBLIC_API JS::Value GetPromiseResult(JS::HandleObject promise); 407 408 /** 409 * Returns whether the given promise's rejection is already handled or not. 410 * 411 * The caller must check the given promise is rejected before checking it's 412 * handled or not. 413 */ 414 extern JS_PUBLIC_API bool GetPromiseIsHandled(JS::HandleObject promise); 415 416 /* 417 * Given a settled (i.e. fulfilled or rejected, not pending) promise, sets 418 * |promise.[[PromiseIsHandled]]| to true and removes it from the list of 419 * unhandled rejected promises. 420 */ 421 extern JS_PUBLIC_API bool SetSettledPromiseIsHandled(JSContext* cx, 422 JS::HandleObject promise); 423 424 /* 425 * Given a promise (settled or not), sets |promise.[[PromiseIsHandled]]| to true 426 * and removes it from the list of unhandled rejected promises if it's settled. 427 */ 428 [[nodiscard]] extern JS_PUBLIC_API bool SetAnyPromiseIsHandled( 429 JSContext* cx, JS::HandleObject promise); 430 431 /** 432 * Returns a js::SavedFrame linked list of the stack that lead to the given 433 * Promise's allocation. 434 * 435 * The promise parameter here should be an unwrapped promise. 436 */ 437 extern JS_PUBLIC_API JSObject* GetPromiseAllocationSite( 438 JS::HandleObject promise); 439 440 /** 441 * A more flexible version of the above: Handles the cases where 442 * 1. maybePromise is wrapped 443 * 2. maybePromise is null 444 * 3. maybePromise isn't actually a promise. 445 * 446 * Returns js::SavedFrame if the passed argument is a 447 * wrapped promise (that's not a dead object wrapper) and it 448 * has an allocation site. 449 */ 450 extern JS_PUBLIC_API JSObject* 451 MaybeGetPromiseAllocationSiteFromPossiblyWrappedPromise( 452 JS::HandleObject maybePromise); 453 454 extern JS_PUBLIC_API JSObject* GetPromiseResolutionSite( 455 JS::HandleObject promise); 456 457 #ifdef DEBUG 458 extern JS_PUBLIC_API void DumpPromiseAllocationSite(JSContext* cx, 459 JS::HandleObject promise); 460 461 extern JS_PUBLIC_API void DumpPromiseResolutionSite(JSContext* cx, 462 JS::HandleObject promise); 463 #endif 464 465 /** 466 * Calls the current compartment's original Promise.resolve on the original 467 * Promise constructor, with `resolutionValue` passed as an argument. 468 */ 469 extern JS_PUBLIC_API JSObject* CallOriginalPromiseResolve( 470 JSContext* cx, JS::HandleValue resolutionValue); 471 472 /** 473 * Calls the current compartment's original Promise.reject on the original 474 * Promise constructor, with `resolutionValue` passed as an argument. 475 */ 476 extern JS_PUBLIC_API JSObject* CallOriginalPromiseReject( 477 JSContext* cx, JS::HandleValue rejectionValue); 478 479 /** 480 * Resolves the given Promise with the given `resolutionValue`. 481 * 482 * Calls the `resolve` function that was passed to the executor function when 483 * the Promise was created. 484 */ 485 extern JS_PUBLIC_API bool ResolvePromise(JSContext* cx, 486 JS::HandleObject promiseObj, 487 JS::HandleValue resolutionValue); 488 489 /** 490 * Rejects the given `promise` with the given `rejectionValue`. 491 * 492 * Calls the `reject` function that was passed to the executor function when 493 * the Promise was created. 494 */ 495 extern JS_PUBLIC_API bool RejectPromise(JSContext* cx, 496 JS::HandleObject promiseObj, 497 JS::HandleValue rejectionValue); 498 499 /** 500 * Create a Promise with the given fulfill/reject handlers, that will be 501 * fulfilled/rejected with the value/reason that the promise `promise` is 502 * fulfilled/rejected with. 503 * 504 * This function basically acts like `promise.then(onFulfilled, onRejected)`, 505 * except that its behavior is unaffected by changes to `Promise`, 506 * `Promise[Symbol.species]`, `Promise.prototype.then`, `promise.constructor`, 507 * `promise.then`, and so on. 508 * 509 * This function throws if `promise` is not a Promise from this or another 510 * realm. 511 * 512 * This function will assert if `onFulfilled` or `onRejected` is non-null and 513 * also not IsCallable. 514 */ 515 extern JS_PUBLIC_API JSObject* CallOriginalPromiseThen( 516 JSContext* cx, JS::HandleObject promise, JS::HandleObject onFulfilled, 517 JS::HandleObject onRejected); 518 519 /** 520 * Unforgeable, optimized version of the JS builtin Promise.prototype.then. 521 * 522 * Takes a Promise instance and nullable `onFulfilled`/`onRejected` callables to 523 * enqueue as reactions for that promise. In contrast to Promise.prototype.then, 524 * this doesn't create and return a new Promise instance. 525 * 526 * Throws a TypeError if `promise` isn't a Promise (or possibly a different 527 * error if it's a security wrapper or dead object proxy). 528 */ 529 extern JS_PUBLIC_API bool AddPromiseReactions(JSContext* cx, 530 JS::HandleObject promise, 531 JS::HandleObject onFulfilled, 532 JS::HandleObject onRejected); 533 534 /** 535 * Unforgeable, optimized version of the JS builtin Promise.prototype.then. 536 * 537 * Takes a Promise instance and nullable `onFulfilled`/`onRejected` callables to 538 * enqueue as reactions for that promise. In contrast to Promise.prototype.then, 539 * this doesn't create and return a new Promise instance. 540 * 541 * Throws a TypeError if `promise` isn't a Promise (or possibly a different 542 * error if it's a security wrapper or dead object proxy). 543 * 544 * If `onRejected` is null and `promise` is rejected, this function -- unlike 545 * the function above -- will not report an unhandled rejection. 546 */ 547 extern JS_PUBLIC_API bool AddPromiseReactionsIgnoringUnhandledRejection( 548 JSContext* cx, JS::HandleObject promise, JS::HandleObject onFulfilled, 549 JS::HandleObject onRejected); 550 551 // This enum specifies whether a promise is expected to keep track of 552 // information that is useful for embedders to implement user activation 553 // behavior handling as specified in the HTML spec: 554 // https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation 555 // By default, promises created by SpiderMonkey do not make any attempt to keep 556 // track of information about whether an activation behavior was being processed 557 // when the original promise in a promise chain was created. If the embedder 558 // sets either of the HadUserInteractionAtCreation or 559 // DidntHaveUserInteractionAtCreation flags on a promise after creating it, 560 // SpiderMonkey will propagate that flag to newly created promises when 561 // processing Promise#then and will make it possible to query this flag off of a 562 // promise further down the chain later using the 563 // GetPromiseUserInputEventHandlingState() API. 564 enum class PromiseUserInputEventHandlingState { 565 // Don't keep track of this state (default for all promises) 566 DontCare, 567 // Keep track of this state, the original promise in the chain was created 568 // while an activation behavior was being processed. 569 HadUserInteractionAtCreation, 570 // Keep track of this state, the original promise in the chain was created 571 // while an activation behavior was not being processed. 572 DidntHaveUserInteractionAtCreation 573 }; 574 575 /** 576 * Returns the given Promise's activation behavior state flag per above as a 577 * JS::PromiseUserInputEventHandlingState value. All promises are created with 578 * the DontCare state by default. 579 * 580 * Returns JS::PromiseUserInputEventHandlingState::DontCare if the given object 581 * is a wrapper that can't safely be unwrapped. 582 */ 583 extern JS_PUBLIC_API PromiseUserInputEventHandlingState 584 GetPromiseUserInputEventHandlingState(JS::HandleObject promise); 585 586 /** 587 * Sets the given Promise's activation behavior state flag per above as a 588 * JS::PromiseUserInputEventHandlingState value. 589 * 590 * Returns false if the given object is a wrapper that can't safely be 591 * unwrapped. 592 */ 593 extern JS_PUBLIC_API bool SetPromiseUserInputEventHandlingState( 594 JS::HandleObject promise, JS::PromiseUserInputEventHandlingState state); 595 596 /** 597 * Unforgeable version of the JS builtin Promise.all. 598 * 599 * Takes a HandleObjectVector of Promise objects and returns a promise that's 600 * resolved with an array of resolution values when all those promises have 601 * been resolved, or rejected with the rejection value of the first rejected 602 * promise. 603 * 604 * Asserts that all objects in the `promises` vector are, maybe wrapped, 605 * instances of `Promise` or a subclass of `Promise`. 606 */ 607 extern JS_PUBLIC_API JSObject* GetWaitForAllPromise( 608 JSContext* cx, JS::HandleObjectVector promises); 609 610 /** 611 * The Dispatchable interface allows the embedding to call SpiderMonkey 612 * on a JSContext thread when requested via DispatchToEventLoopCallback. 613 */ 614 class JS_PUBLIC_API Dispatchable { 615 protected: 616 // The registered_ flag indicates that this task is counted as part of 617 // numRegistered_ of OffThreadPromiseRuntimeState, which will wait until 618 // all registered tasks have been run or destroyed. 619 bool registered_ = false; 620 621 public: 622 bool registered() const { return registered_; } 623 624 // Destruction of Dispatchables is public in order to be used with 625 // UniquePtrs. Their destruction by SpiderMonkey is enforced by 626 // ReleaseFailedTask. 627 virtual ~Dispatchable() = default; 628 629 // ShuttingDown indicates that SpiderMonkey should abort async tasks to 630 // expedite shutdown. 631 enum MaybeShuttingDown { NotShuttingDown, ShuttingDown }; 632 633 // Called by the embedding after DispatchToEventLoopCallback succeeds. 634 // Used to correctly release the unique ptr and call the run task. 635 static void Run(JSContext* cx, js::UniquePtr<Dispatchable>&& task, 636 MaybeShuttingDown maybeShuttingDown); 637 638 // Used to correctly release the unique ptr. Relies on the 639 // OffThreadRuntimePromiseState to handle cleanup via iteration over the 640 // live() set. 641 static void ReleaseFailedTask(js::UniquePtr<Dispatchable>&& task); 642 643 protected: 644 // Dispatchables are created exclusively by SpiderMonkey. 645 Dispatchable() = default; 646 647 // These two methods must be implemented in order to correctly handle 648 // success and failure cases for the task. 649 650 // Used to execute the task, run on the owning thread. 651 // A subclass should override this method to 652 // 1) execute the task as necessary and 653 // 2) delete the task. 654 virtual void run(JSContext* cx, MaybeShuttingDown maybeShuttingDown) = 0; 655 656 // Used to transfer the task back to the runtime if the embedding, upon 657 // taking ownership of the task, fails to dispatch and run it. This allows 658 // the runtime to delete the task during shutdown. This method can be called 659 // from any thread. 660 // Typically, this will be used with a UniquePtr like so: 661 // auto task = myTask.release(); 662 // task->transferToRuntime(); 663 virtual void transferToRuntime() = 0; 664 }; 665 666 /** 667 * Callback to dispatch a JS::Dispatchable to a JSContext's thread's event 668 * loop. 669 * 670 * The DispatchToEventLoopCallback set on a particular JSContext must accept 671 * JS::Dispatchable instances and arrange for their `run` methods to be called 672 * eventually on the JSContext's thread. This is used for cross-thread dispatch, 673 * so the callback itself must be safe to call from any thread. It cannot 674 * trigger a GC. 675 * 676 * The DelayedDispatchToEventLoopCallback in addition takes a delay, and it 677 * must accept JS::Dispatchable instances and arrange for their `run` methods 678 * to be called after the delay on the JSContext's thread. 679 * The embeddings must have its own timeout manager to handle the delay. 680 * If a timeout manager is not available for given context, it should return 681 * false, optionally with a warning message printed. 682 * 683 * The callback accepts a closure that is the same as the one given by the call 684 * to JS::InitAsyncTaskCallbacks. 685 * 686 * If the callback returns `true`, it must eventually run the given 687 * Dispatchable; otherwise, SpiderMonkey may leak memory or hang. 688 * 689 * The callback may return `false` to indicate that the JSContext's thread is 690 * shutting down and is no longer accepting runnables. Shutting down is a 691 * one-way transition: once the callback has rejected a runnable, it must reject 692 * all subsequently submitted runnables as well. 693 */ 694 695 typedef bool (*DispatchToEventLoopCallback)( 696 void* closure, js::UniquePtr<Dispatchable>&& dispatchable); 697 698 /** 699 * Callback to dispatch a JS::Dispatchable to a JSContext's thread's event 700 * loop with a delay. 701 * 702 * The DelayedDispatchToEventLoopCallback set on a particular JSContext must 703 * accept JS::Dispatchable instances and arrange for their `run` methods to be 704 * called after the given delay on the JSContext's thread. This is used for 705 * cross-thread dispatch, so the callback itself must be safe to call from any 706 * thread. It cannot trigger a GC. 707 * 708 * The embeddings must have its own timeout manager to handle the delay. 709 * If a timeout manager is not available for given context, it should return 710 * false, optionally with a warning message printed. 711 * 712 * The callback accepts a closure that is the same as the one given by the call 713 * to JS::InitAsyncTaskCallbacks. 714 * 715 * If the callback returns `true`, it must eventually run the given 716 * Dispatchable; otherwise, SpiderMonkey may leak memory or hang. 717 * 718 * The callback may return `false` to indicate that the JSContext's thread is 719 * shutting down and is no longer accepting runnables. Shutting down is a 720 * one-way transition: once the callback has rejected a runnable, it must reject 721 * all subsequently submitted runnables as well. 722 */ 723 724 typedef bool (*DelayedDispatchToEventLoopCallback)( 725 void* closure, js::UniquePtr<Dispatchable>&& dispatchable, uint32_t delay); 726 727 /** 728 * Callback to notify the embedder that a background task has begun. This can 729 * be used to keep the JSContext's thread alive if it's subject to garbage 730 * collection (e.g. as web workers are). 731 * 732 * This callback will be called on the same thread as the JSContext, but it 733 * must not perform any garbage collection or re-enter the engine. 734 * 735 * The passed dispatchable can be used to track the async task. Most async 736 * tasks will finish in a bounded amount of time, but some 737 * (i.e. Atomics.waitAsync) are not guaranteed to finish. These tasks may be 738 * canceled using JS::CancelAsyncTasks. 739 * 740 * The callback accepts a closure that is the same as the one given by the call 741 * to JS::InitAsyncTaskCallbacks. 742 */ 743 typedef void (*AsyncTaskStartedCallback)(void* closure, 744 Dispatchable* dispatchable); 745 746 /** 747 * Callback to notify the embedder that a background task has finished. This can 748 * be used to keep the JSContext's thread alive if it's subject to garbage 749 * collection (e.g. as web workers are). 750 * 751 * This callback will be called on the same thread as the JSContext, but it 752 * must not perform any garbage collection or re-enter the engine. 753 * 754 * The passed dispatchable will be the same as the one given in 755 * AsyncTaskStartedCallback. See that callback for more information. 756 * 757 * The callback accepts a closure that is the same as the one given by the call 758 * to JS::InitAsyncTaskCallbacks. 759 */ 760 typedef void (*AsyncTaskFinishedCallback)(void* closure, 761 Dispatchable* dispatchable); 762 763 /** 764 * Initialize the async task callbacks. Embedders must use this or else call 765 * js::UseInternalJobQueues to select a default implementation built into 766 * SpiderMonkey. This latter depends on the embedding to call js::RunJobs on 767 * the JavaScript thread to process queued Dispatchables at appropriate times. 768 * 769 * The dispatchCallback and delayedDispatchCallback are mandatory. 770 * asyncTaskStartedCallback and asyncTaskFinishedCallback are optional and may 771 * be null. 772 * 773 */ 774 extern JS_PUBLIC_API void InitAsyncTaskCallbacks( 775 JSContext* cx, DispatchToEventLoopCallback dispatchCallback, 776 DelayedDispatchToEventLoopCallback delayedDispatchCallback, 777 AsyncTaskStartedCallback asyncTaskStartedCallback, 778 AsyncTaskFinishedCallback asyncTaskFinishedCallback, void* closure); 779 780 /** 781 * Cancel all cancellable async tasks. Some tasks may not be cancellable, and 782 * must be waited on through a call to JS::ShutdownAsyncTasks. 783 * 784 * This will fire JSAsyncTaskFinished callbacks for cancelled tasks. 785 */ 786 extern JS_PUBLIC_API void CancelAsyncTasks(JSContext* cx); 787 788 /** 789 * Cancel all cancellable async tasks and block until non-cancellable tasks are 790 * finished. 791 * 792 * This will fire JSAsyncTaskFinished callbacks for all tasks, and there should 793 * be no outstanding tasks after this returns. All roots held by tasks will be 794 * released. 795 * 796 * Destroying a JSRuntime will implicitly perform this. However, this is not 797 * soon enough for cycle collection, which needs to have roots dropped earlier 798 * so that the cycle collector can transitively remove roots for a future GC. 799 * For these and other cases, the set of pending async tasks can be canceled 800 * with this call earlier than JSRuntime destruction. 801 */ 802 803 extern JS_PUBLIC_API void ShutdownAsyncTasks(JSContext* cx); 804 805 } // namespace JS 806 807 #endif // js_Promise_h