tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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