Skip to content
File

Blob: src/workerd/api/basics.h

cpp804 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#pragma once
6// This file defines Event- and EventTarget-related APIs.
7//
8// TODO(cleanup): Rename to events.h?
9 
10#include <workerd/io/compatibility-date.capnp.h>
11#include <workerd/io/external-pusher.h>
12#include <workerd/io/io-own.h>
13#include <workerd/io/worker-interface.capnp.h>
14#include <workerd/jsg/jsg.h>
15#include <workerd/util/canceler.h>
16 
17#include <kj/function.h>
18#include <kj/map.h>
19 
20namespace workerd::api {
21 
22class EventTarget;
23class AbortSignal;
24class AbortController;
25class ActorState;
26 
27// An implementation of the Web Platform Standard Event API
28class Event: public jsg::Object {
29 public:
30 struct Init final {
31 jsg::Optional<bool> bubbles;
32 jsg::Optional<bool> cancelable;
33 jsg::Optional<bool> composed;
34 JSG_STRUCT(bubbles, cancelable, composed);
35 };
36 
37 inline explicit Event(kj::String ownType, Init init = {}, bool trusted = true)
38 : ownType(kj::mv(ownType)),
39 type(this->ownType) {
40 flags.trusted = trusted;
41 flags.bubbles = init.bubbles.orDefault(false);
42 flags.cancelable = init.cancelable.orDefault(false);
43 flags.composed = init.composed.orDefault(false);
44 }
45 
46 inline explicit Event(kj::StringPtr type, Init init = {}, bool trusted = true): type(type) {
47 flags.trusted = trusted;
48 flags.bubbles = init.bubbles.orDefault(false);
49 flags.cancelable = init.cancelable.orDefault(false);
50 flags.composed = init.composed.orDefault(false);
51 }
52 
53 inline bool isPreventDefault() const {
54 return flags.preventedDefault;
55 }
56 inline void clearPreventDefault() {
57 flags.preventedDefault = false;
58 }
59 
60 void beginDispatch(jsg::Ref<EventTarget> target);
61 inline void endDispatch() {
62 flags.isBeingDispatched = false;
63 }
64 
65 inline bool isStopped() const {
66 return flags.stopped;
67 }
68 
69 static jsg::Ref<Event> constructor(jsg::Lock& js, kj::String type, jsg::Optional<Init> init);
70 kj::StringPtr getType();
71 
72 inline void stopImmediatePropagation() {
73 flags.stopped = true;
74 }
75 inline void preventDefault() {
76 flags.preventedDefault = true;
77 }
78 
79 // The only phases we actually use are NONE and AT_TARGET but we provide
80 // all of them to meet spec compliance.
81 enum Phase {
82 NONE,
83 CAPTURING_PHASE,
84 AT_TARGET,
85 BUBBLING_PHASE,
86 };
87 
88 inline int getEventPhase() const {
89 return flags.isBeingDispatched ? AT_TARGET : NONE;
90 }
91 
92 // Much of the following is not used in our implementation of Event
93 // simply because we do not support the notion of bubbled events
94 // (events propagated up through a hierarchy of objects). They are
95 // provided to fill-out Event spec compliance.
96 
97 inline bool getCancelBubble() const {
98 return flags.propagationStopped;
99 }
100 inline void setCancelBubble(bool stopped) {
101 flags.propagationStopped = stopped;
102 }
103 inline void stopPropagation() {
104 flags.propagationStopped = true;
105 }
106 inline bool getComposed() const {
107 return flags.composed;
108 }
109 inline bool getBubbles() const {
110 return flags.bubbles;
111 }
112 inline bool getCancelable() const {
113 return flags.cancelable;
114 }
115 inline bool getDefaultPrevented() const {
116 return getCancelable() && flags.preventedDefault;
117 }
118 inline bool getReturnValue() const {
119 return !getDefaultPrevented();
120 }
121 
122 // We provide the timeStamp property for spec compliance but we force
123 // the value to 0.0 always because we really don't want users to rely
124 // on this property for timing details.
125 inline double getTimestamp() const {
126 return 0.0;
127 }
128 
129 // What makes an Event trusted? It's pretty simple... any Event created
130 // by EW internally is Trusted, any Event created using new Event() in JS
131 // is not trusted.
132 inline bool getIsTrusted() const {
133 return flags.trusted;
134 }
135 
136 // The currentTarget is the EventTarget on which the Event is being
137 // dispatched. This will be set every time dispatchEvent() is called
138 // successfully and will be null after dispatchEvent returns.
139 kj::Maybe<jsg::Ref<EventTarget>> getCurrentTarget();
140 
141 // Because we don't support hierarchical EventTargets, this function
142 // will always return the same value as getCurrentTarget().
143 jsg::Optional<jsg::Ref<EventTarget>> getTarget();
144 
145 // For our implementation, since we do not support hierarchical EventTargets,
146 // the composedPath is always either an empty array if the Event is currently
147 // not being dispatched, or an array containing only the currentTarget if
148 // it is being dispatched.
149 kj::Array<jsg::Ref<EventTarget>> composedPath();
150 
151 JSG_RESOURCE_TYPE(Event, CompatibilityFlags::Reader flags) {
152 // Previously, we were setting all properties as instance properties,
153 // which broke the ability to subclass the Event object. With the
154 // compatibility flag set, we instead attach the properties to the
155 // prototype.
156 if (flags.getJsgPropertyOnPrototypeTemplate()) {
157 JSG_READONLY_PROTOTYPE_PROPERTY(type, getType);
158 JSG_READONLY_PROTOTYPE_PROPERTY(eventPhase, getEventPhase);
159 JSG_READONLY_PROTOTYPE_PROPERTY(composed, getComposed);
160 JSG_READONLY_PROTOTYPE_PROPERTY(bubbles, getBubbles);
161 JSG_READONLY_PROTOTYPE_PROPERTY(cancelable, getCancelable);
162 JSG_READONLY_PROTOTYPE_PROPERTY(defaultPrevented, getDefaultPrevented);
163 JSG_READONLY_PROTOTYPE_PROPERTY(returnValue, getReturnValue);
164 if (flags.getPedanticWpt()) {
165 JSG_READONLY_PROTOTYPE_PROPERTY(currentTarget, getCurrentTarget);
166 } else {
167 // The original implementation had getTarget simply deferring to
168 // getCurrentTarget, the new impl moves the original impl into
169 // getTarget here so having currentTarget point to getTarget
170 // preserves the original behavior.
171 JSG_READONLY_PROTOTYPE_PROPERTY(currentTarget, getTarget);
172 }
173 JSG_READONLY_PROTOTYPE_PROPERTY(target, getTarget);
174 JSG_READONLY_PROTOTYPE_PROPERTY(srcElement, getTarget);
175 JSG_READONLY_PROTOTYPE_PROPERTY(timeStamp, getTimestamp);
176 if (flags.getPedanticWpt()) {
177 JSG_READONLY_INSTANCE_PROPERTY(isTrusted, getIsTrusted);
178 } else {
179 JSG_READONLY_PROTOTYPE_PROPERTY(isTrusted, getIsTrusted);
180 }
181 
182 JSG_PROTOTYPE_PROPERTY(cancelBubble, getCancelBubble, setCancelBubble);
183 } else {
184 JSG_READONLY_INSTANCE_PROPERTY(type, getType);
185 JSG_READONLY_INSTANCE_PROPERTY(eventPhase, getEventPhase);
186 JSG_READONLY_INSTANCE_PROPERTY(composed, getComposed);
187 JSG_READONLY_INSTANCE_PROPERTY(bubbles, getBubbles);
188 JSG_READONLY_INSTANCE_PROPERTY(cancelable, getCancelable);
189 JSG_READONLY_INSTANCE_PROPERTY(defaultPrevented, getDefaultPrevented);
190 JSG_READONLY_INSTANCE_PROPERTY(returnValue, getReturnValue);
191 if (flags.getPedanticWpt()) {
192 JSG_READONLY_INSTANCE_PROPERTY(currentTarget, getCurrentTarget);
193 } else {
194 JSG_READONLY_INSTANCE_PROPERTY(currentTarget, getTarget);
195 }
196 JSG_READONLY_INSTANCE_PROPERTY(target, getTarget);
197 JSG_READONLY_INSTANCE_PROPERTY(srcElement, getCurrentTarget);
198 JSG_READONLY_INSTANCE_PROPERTY(timeStamp, getTimestamp);
199 JSG_READONLY_INSTANCE_PROPERTY(isTrusted, getIsTrusted);
200 
201 JSG_INSTANCE_PROPERTY(cancelBubble, getCancelBubble, setCancelBubble);
202 }
203 
204 JSG_METHOD(stopImmediatePropagation);
205 JSG_METHOD(preventDefault);
206 JSG_METHOD(stopPropagation);
207 JSG_METHOD(composedPath);
208 
209 JSG_STATIC_CONSTANT(NONE);
210 JSG_STATIC_CONSTANT(CAPTURING_PHASE);
211 JSG_STATIC_CONSTANT(AT_TARGET);
212 JSG_STATIC_CONSTANT(BUBBLING_PHASE);
213 }
214 
215 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
216 tracker.trackField("type", ownType);
217 tracker.trackField("target", target);
218 }
219 
220 private:
221 // listing ownType first so type can be initialized with it in constructor
222 kj::String ownType;
223 kj::StringPtr type;
224 kj::Maybe<jsg::Ref<EventTarget>> target;
225 
226 struct Flags {
227 uint8_t trusted : 1 = 1;
228 uint8_t stopped : 1 = 0;
229 uint8_t preventedDefault : 1 = 0;
230 uint8_t isBeingDispatched : 1 = 0;
231 uint8_t propagationStopped : 1 = 0;
232 uint8_t composed : 1 = 0;
233 uint8_t bubbles : 1 = 0;
234 uint8_t cancelable : 1 = 0;
235 };
236 Flags flags{};
237 
238 void visitForGc(jsg::GcVisitor& visitor) {
239 visitor.visit(target);
240 }
241};
242 
243class ExtendableEvent: public Event {
244 public:
245 using Event::Event;
246 
247 // While ExtendableEvent is defined by the spec to be constructable, there's really not a
248 // lot of reason currently to do so, especially with the restriction that waitUntil can
249 // only be called on trusted events (which have to originate from within the system).
250 static jsg::Ref<ExtendableEvent> constructor(kj::String type) = delete;
251 
252 void waitUntil(kj::Promise<void> promise);
253 
254 jsg::Optional<jsg::Ref<ActorState>> getActorState(jsg::Lock& js);
255 
256 JSG_RESOURCE_TYPE(ExtendableEvent) {
257 JSG_INHERIT(Event);
258 JSG_METHOD(waitUntil);
259 
260#if !WORKERD_API_BASICS_TEST
261 JSG_LAZY_READONLY_INSTANCE_PROPERTY(actorState, getActorState);
262#endif
263 
264 JSG_TS_OVERRIDE({ actorState: never });
265 // Omit `actorState` from definitions
266 }
267};
268 
269// An implementation of the Web Platform Standard CustomEvent API
270class CustomEvent: public Event {
271 public:
272 struct CustomEventInit final {
273 jsg::Optional<bool> bubbles;
274 jsg::Optional<bool> cancelable;
275 jsg::Optional<bool> composed;
276 jsg::Optional<jsg::JsRef<jsg::JsValue>> detail;
277 JSG_STRUCT(bubbles, cancelable, composed, detail);
278 
279 operator Event::Init();
280 };
281 
282 explicit CustomEvent(kj::String ownType, CustomEventInit init = CustomEventInit());
283 
284 static jsg::Ref<CustomEvent> constructor(
285 jsg::Lock& js, kj::String type, jsg::Optional<CustomEventInit> init);
286 
287 jsg::Optional<jsg::JsValue> getDetail(jsg::Lock& js);
288 
289 JSG_RESOURCE_TYPE(CustomEvent) {
290 JSG_INHERIT(Event);
291 JSG_READONLY_PROTOTYPE_PROPERTY(detail, getDetail);
292 JSG_TS_OVERRIDE(<T = any> {
293 get detail(): T;
294 });
295 }
296 
297 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
298 tracker.trackField("detail", detail);
299 }
300 
301 private:
302 jsg::Optional<jsg::JsRef<jsg::JsValue>> detail;
303};
304 
305// An implementation of the Web Platform Standard EventTarget API
306class EventTarget: public jsg::Object {
307 public:
308 ~EventTarget() noexcept(false);
309 
310 size_t getHandlerCount(kj::StringPtr type) const;
311 
312 kj::Array<kj::StringPtr> getHandlerNames() const;
313 
314 bool dispatchEventImpl(jsg::Lock& js, jsg::Ref<Event> event);
315 
316 inline void removeAllHandlers() {
317 typeMap.clear();
318 }
319 
320 inline void enableWarningOnSpecialEvents() {
321 flags.warnOnSpecialEvents = true;
322 }
323 
324 // The EventListenerCallback, if given, is called whenever addEventListener
325 // or removeEventListener is invoked to report the number of registered
326 // handlers for the event.
327 using EventListenerCallback = jsg::Function<void(kj::StringPtr, size_t)>;
328 
329 // ---------------------------------------------------------------------------
330 // JS API
331 
332 struct EventListenerOptions {
333 jsg::Optional<bool> capture;
334 
335 JSG_STRUCT(capture);
336 };
337 
338 struct AddEventListenerOptions {
339 jsg::Optional<bool> capture;
340 jsg::Optional<bool> passive;
341 jsg::Optional<bool> once;
342 jsg::Optional<jsg::Ref<AbortSignal>> signal;
343 
344 JSG_STRUCT(capture, passive, once, signal);
345 
346 // A following signal is used when the EventTarget is an AbortSignal
347 // that is being followed by another AbortSignal via the AbortSignal.any.
348 // This is used to keep the following signal alive until either the
349 // signal is triggered or this AbortSignal is destroyed.
350 jsg::Optional<jsg::Ref<AbortSignal>> followingSignal;
351 };
352 
353 using AddEventListenerOpts = kj::OneOf<AddEventListenerOptions, bool>;
354 using EventListenerOpts = kj::OneOf<EventListenerOptions, bool>;
355 
356 using HandlerFunction = jsg::Function<jsg::Optional<jsg::Value>(jsg::Ref<Event>)>;
357 
358 struct HandlerObject {
359 HandlerFunction handleEvent;
360 jsg::SelfRef self;
361 JSG_STRUCT(handleEvent, self);
362 
363 // TODO(cleanup): Get rid of this override and parse the type directly in param-extractor.rs
364 JSG_STRUCT_TS_OVERRIDE({
365 handleEvent: (event: Event) => any | undefined;
366 });
367 };
368 using Handler = kj::OneOf<HandlerFunction, HandlerObject>;
369 
370 void addEventListener(jsg::Lock& js,
371 kj::String type,
372 kj::Maybe<jsg::Identified<Handler>> maybeHandler,
373 jsg::Optional<AddEventListenerOpts> maybeOptions,
374 const jsg::TypeHandler<jsg::Ref<EventTarget>>& eventTargetHandler);
375 void removeEventListener(jsg::Lock& js,
376 kj::String type,
377 kj::Maybe<jsg::HashableV8Ref<v8::Object>> maybeHandler,
378 jsg::Optional<EventListenerOpts> options);
379 bool dispatchEvent(jsg::Lock& js, jsg::Ref<Event> event);
380 
381 JSG_RESOURCE_TYPE(EventTarget) {
382 JSG_METHOD(addEventListener);
383 JSG_METHOD(removeEventListener);
384 JSG_METHOD(dispatchEvent);
385 
386 JSG_TS_DEFINE(
387 type EventListener<EventType extends Event = Event> = (event: EventType) => void;
388 interface EventListenerObject<EventType extends Event = Event> {
389 handleEvent(event: EventType): void;
390 }
391 type EventListenerOrEventListenerObject<EventType extends Event = Event> = EventListener<EventType> | EventListenerObject<EventType>;
392 );
393 JSG_TS_OVERRIDE(<EventMap extends Record<string, Event> = Record<string, Event>> {
394 addEventListener<Type extends keyof EventMap>(type: Type, handler: EventListenerOrEventListenerObject<EventMap[Type]>, options?: EventTargetAddEventListenerOptions | boolean): void;
395 removeEventListener<Type extends keyof EventMap>(type: Type, handler: EventListenerOrEventListenerObject<EventMap[Type]>, options?: EventTargetEventListenerOptions | boolean): void;
396 dispatchEvent(event: EventMap[keyof EventMap]): boolean;
397 });
398 }
399 JSG_REFLECTION(onEvents);
400 
401 static jsg::Ref<EventTarget> constructor(jsg::Lock& js);
402 
403 // Registers a lambda that will be called when the given event type is emitted.
404 // The handler will be registered for as long as the returned kj::Own<void>
405 // handle is held. If the EventTarget is destroyed while the native handler handle
406 // is held, it will be automatically detached.
407 //
408 // The caller must not do anything with the returned Own<void> except drop it. This is why it
409 // is Own<void> and not Own<NativeHandler>.
410 kj::Own<void> newNativeHandler(
411 jsg::Lock& js, kj::String type, jsg::Function<void(jsg::Ref<Event>)> func, bool once = false);
412 
413 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const;
414 
415 protected:
416 void setEventListenerCallback(EventListenerCallback&& callback) {
417 maybeListenerCallback = kj::mv(callback);
418 }
419 
420 private:
421 // RAII-style listener that can be attached to an EventTarget.
422 class NativeHandler {
423 public:
424 using Signature = void(jsg::Ref<Event>);
425 NativeHandler(jsg::Lock& js,
426 EventTarget& target,
427 kj::String type,
428 jsg::Function<Signature> func,
429 bool once = false);
430 ~NativeHandler() noexcept(false);
431 KJ_DISALLOW_COPY_AND_MOVE(NativeHandler);
432 
433 void operator()(jsg::Lock& js, jsg::Ref<Event> event);
434 
435 uint hashCode() const;
436 
437 void visitForGc(jsg::GcVisitor& visitor);
438 
439 private:
440 void detach();
441 
442 kj::String type;
443 struct State {
444 // target's destructor will null out `state`, so this is OK to be a bare reference.
445 EventTarget& target;
446 
447 jsg::Function<Signature> func;
448 };
449 
450 kj::Maybe<State> state;
451 bool once;
452 
453 friend class EventTarget;
454 };
455 
456 void addNativeListener(jsg::Lock& js, NativeHandler& handler);
457 bool removeNativeListener(NativeHandler& handler);
458 
459 struct EventHandler {
460 struct JavaScriptHandler {
461 jsg::HashableV8Ref<v8::Object> identity;
462 HandlerFunction callback;
463 
464 // If the event handler is registered with an AbortSignal, then the abortHandler points
465 // at the NativeHandler representing that registration, so that if this object is GC'ed before
466 // the AbortSignal is signalled, we unregister ourselves from listening on it. Note that
467 // this is Own<void> for the same reason newNativeHandler() returns Own<void>: We are not
468 // supposed to do anything with this except drop it.
469 kj::Maybe<kj::Own<void>> abortHandler;
470 
471 void visitForGc(jsg::GcVisitor& visitor) {
472 visitor.visit(identity, callback);
473 
474 // Note that we intentionally do NOT visit `abortHandler`. This is because the JS handles
475 // held by `abortHandler` are not ever accessed by this path. Instead, they are accessed
476 // by the AbortSignal, if and when it fires. So it is the AbortSignal's responsibility to
477 // visit the NativeHandler's content.
478 }
479 
480 kj::StringPtr jsgGetMemoryName() const {
481 return "JavaScriptHandler"_kjc;
482 }
483 size_t jsgGetMemorySelfSize() const;
484 void jsgGetMemoryInfo(jsg::MemoryTracker& tracker) const;
485 };
486 
487 struct NativeHandlerRef {
488 NativeHandler& handler;
489 };
490 
491 // An EventHandler can be backed by either a JavaScript Handler (which is either a
492 // function or an object) or a native handler. The insertion order matters here so
493 // we maintain a single table.
494 using Handler = kj::OneOf<JavaScriptHandler, NativeHandlerRef>;
495 
496 Handler handler;
497 
498 // When once is true, the handler will be removed after it is invoked one time.
499 bool once = false;
500 
501 EventHandler(Handler handler, bool once): handler(kj::mv(handler)), once(once) {}
502 KJ_DISALLOW_COPY_AND_MOVE(EventHandler);
503 
504 kj::StringPtr jsgGetMemoryName() const {
505 return "EventHandler"_kjc;
506 }
507 size_t jsgGetMemorySelfSize() const;
508 void jsgGetMemoryInfo(jsg::MemoryTracker& tracker) const;
509 };
510 
511 struct EventHandlerHashCallbacks {
512 const EventHandler::Handler& keyForRow(const kj::Own<EventHandler>& row) const;
513 bool matches(const kj::Own<EventHandler>& a, const jsg::HashableV8Ref<v8::Object>& b) const;
514 bool matches(const kj::Own<EventHandler>& a, const NativeHandler& b) const;
515 bool matches(const kj::Own<EventHandler>& a, const EventHandler::NativeHandlerRef& b) const;
516 bool matches(const kj::Own<EventHandler>& a, const EventHandler::Handler& b) const;
517 uint hashCode(const jsg::HashableV8Ref<v8::Object>& obj) const;
518 uint hashCode(const NativeHandler& handler) const;
519 uint hashCode(const EventHandler::NativeHandlerRef& handler) const;
520 uint hashCode(const EventHandler::JavaScriptHandler& handler) const;
521 uint hashCode(const EventHandler::Handler& handler) const;
522 };
523 
524 struct EventHandlerSet {
525 kj::Table<kj::Own<EventHandler>,
526 kj::HashIndex<EventHandlerHashCallbacks>,
527 kj::InsertionOrderIndex>
528 handlers;
529 
530 EventHandlerSet(): handlers(EventHandlerHashCallbacks(), {}) {}
531 
532 kj::StringPtr jsgGetMemoryName() const {
533 return "EventHandlerSet"_kjc;
534 }
535 size_t jsgGetMemorySelfSize() const;
536 void jsgGetMemoryInfo(jsg::MemoryTracker& tracker) const;
537 };
538 
539 EventHandlerSet& getOrCreate(kj::StringPtr str) KJ_LIFETIMEBOUND;
540 
541 jsg::PropertyReflection<kj::OneOf<HandlerFunction, jsg::Value>> onEvents;
542 
543 kj::HashMap<kj::String, EventHandlerSet> typeMap;
544 
545 kj::Maybe<EventListenerCallback> maybeListenerCallback;
546 
547 struct Flags {
548 // When using module syntax, the "fetch", "scheduled", "trace", etc.
549 // events are handled by exports rather than events. When warnOnSpecialEvents is true,
550 // when using module syntax, attempts to register event handlers for these special
551 // types of events will result in a warning being emitted.
552 uint8_t warnOnSpecialEvents : 1 = 0;
553 // Event handlers are not supposed to return values. The first time one does, we'll
554 // emit a warning to help users debug things but we'll otherwise ignore it.
555 uint8_t warnOnHandlerReturn : 1 = 1;
556 };
557 Flags flags;
558 
559 void visitForGc(jsg::GcVisitor& visitor);
560 
561 friend class NativeHandler;
562};
563 
564// An implementation of the Web Platform Standard AbortSignal API
565class AbortTriggerRpcClient;
566 
567class AbortSignal final: public EventTarget {
568 public:
569 enum class Flag { NONE, NEVER_ABORTS, IGNORE_FOR_SUBREQUESTS };
570 
571 AbortSignal(kj::Maybe<kj::Exception> exception = kj::none,
572 jsg::Optional<jsg::JsRef<jsg::JsValue>> maybeReason = kj::none,
573 Flag flag = Flag::NONE);
574 
575 using PendingReason = ExternalPusherImpl::PendingAbortReason;
576 
577 // The AbortSignal explicitly does not expose a constructor(). It is
578 // illegal for user code to create an AbortSignal directly.
579 static jsg::Ref<AbortSignal> constructor() = delete;
580 
581 bool getAborted(jsg::Lock& js);
582 
583 jsg::JsValue getReason(jsg::Lock& js);
584 
585 // Will synchronously throw an error if the abort signal has been triggered.
586 void throwIfAborted(jsg::Lock& js);
587 
588 inline bool getNeverAborts() const {
589 return flag == Flag::NEVER_ABORTS;
590 }
591 
592 // The static abort() function here returns an AbortSignal that
593 // has been pre-emptively aborted. It's useful when it might still
594 // be desirable to kick off an async process while communicating
595 // that it shouldn't continue.
596 static jsg::Ref<AbortSignal> abort(jsg::Lock& js, jsg::Optional<jsg::JsValue> reason);
597 
598 // Returns an AbortSignal that is triggered after delay milliseconds.
599 static jsg::Ref<AbortSignal> timeout(jsg::Lock& js, double delay);
600 
601 void triggerAbort(
602 jsg::Lock& js, jsg::Optional<kj::OneOf<kj::Exception, jsg::JsValue>> maybeReason);
603 
604 static jsg::Ref<AbortSignal> any(jsg::Lock& js,
605 kj::Array<jsg::Ref<AbortSignal>> signals,
606 const jsg::TypeHandler<EventTarget::HandlerFunction>& handler,
607 const jsg::TypeHandler<jsg::Ref<EventTarget>>& eventTargetHandler);
608 
609 // While AbortSignal extends EventTarget, and our EventTarget implementation will
610 // automatically support onabort being set as an own property, the spec defines
611 // onabort as a prototype property on the AbortSignal prototype. Therefore, we
612 // need to explicitly set it as a prototype property here.
613 kj::Maybe<jsg::JsValue> getOnAbort(jsg::Lock& js);
614 void setOnAbort(jsg::Lock& js, jsg::Optional<jsg::JsValue> handler);
615 
616 void addEventListener(jsg::Lock& js,
617 kj::String type,
618 jsg::Identified<Handler> handler,
619 jsg::Optional<AddEventListenerOpts> maybeOptions,
620 const jsg::TypeHandler<jsg::Ref<EventTarget>>& eventTargetHandler);
621 
622 JSG_RESOURCE_TYPE(AbortSignal, CompatibilityFlags::Reader flags) {
623 JSG_INHERIT(EventTarget);
624 JSG_STATIC_METHOD(abort);
625 JSG_STATIC_METHOD(timeout);
626 JSG_STATIC_METHOD(any);
627 if (flags.getJsgPropertyOnPrototypeTemplate()) {
628 JSG_READONLY_PROTOTYPE_PROPERTY(aborted, getAborted);
629 JSG_READONLY_PROTOTYPE_PROPERTY(reason, getReason);
630 } else {
631 JSG_READONLY_INSTANCE_PROPERTY(aborted, getAborted);
632 JSG_READONLY_INSTANCE_PROPERTY(reason, getReason);
633 }
634 JSG_PROTOTYPE_PROPERTY(onabort, getOnAbort, setOnAbort);
635 JSG_METHOD(throwIfAborted);
636 
637 if (flags.getWorkerdExperimental()) {
638 JSG_METHOD(skipReleaseForTest);
639 JSG_TS_OVERRIDE({ skipReleaseForTest: never });
640 }
641 }
642 
643 // Allows this AbortSignal to also serve as a kj::Canceler
644 template <typename T>
645 kj::Promise<T> wrap(jsg::Lock& js, kj::Promise<T> promise) {
646 subscribeToRpcAbort(js);
647 
648 JSG_REQUIRE(!canceler->isCanceled(), TypeError, "The AbortSignal has already been triggered");
649 return canceler->wrap(kj::mv(promise));
650 }
651 
652 template <typename T>
653 static kj::Promise<T> maybeCancelWrap(
654 jsg::Lock& js, kj::Maybe<jsg::Ref<AbortSignal>>& signal, kj::Promise<T> promise) {
655 KJ_IF_SOME(s, signal) {
656 return s->wrap(js, kj::mv(promise));
657 } else {
658 return kj::mv(promise);
659 }
660 }
661 
662 RefcountedCanceler& getCanceler();
663 
664 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
665 EventTarget::visitForMemoryInfo(tracker);
666 tracker.trackInlineFieldWithSize(
667 "IoOwn<RefcountedCanceler>", sizeof(IoOwn<RefcountedCanceler>));
668 tracker.trackField("reason", reason);
669 }
670 
671 void serialize(jsg::Lock& js, jsg::Serializer& serializer);
672 
673 // To test what happens if a capability is dropped before invoking release on the cloned abort
674 // signal, this method will tell every rpcClient to skip this step before destruction.
675 void skipReleaseForTest();
676 
677 static jsg::Ref<AbortSignal> deserialize(
678 jsg::Lock& js, rpc::SerializationTag tag, jsg::Deserializer& deserializer);
679 
680 JSG_SERIALIZABLE(rpc::SerializationTag::ABORT_SIGNAL);
681 
682 // True if this is a signal on the request of an incoming fetch. When the compat flag
683 // `requestSignalPassthrough` is set, this flag has no effect. But to ensure backwards
684 // compatibility, when this flag is not set, this signal will not be passed through to
685 // subrequests derived from the incoming request.
686 bool isIgnoredForSubrequests(jsg::Lock& js) const;
687 
688 private:
689 IoOwn<RefcountedCanceler> canceler;
690 Flag flag;
691 
692 kj::Maybe<jsg::JsRef<jsg::JsValue>> reason;
693 kj::Maybe<jsg::JsRef<jsg::JsValue>> onAbortHandler;
694 
695 static kj::Exception abortException(
696 jsg::Lock& js, const jsg::Optional<kj::OneOf<kj::Exception, jsg::JsValue>>& reason);
697 
698 void visitForGc(jsg::GcVisitor& visitor);
699 
700 friend class AbortController;
701 
702 // -------------------------------------------------------------
703 // RPC client functionality. Used if this signal was serialized.
704 
705 // A collection of rpcClients, which will be notified if this signal is triggered and when this
706 // signal is destroyed.
707 kj::Vector<IoOwn<AbortTriggerRpcClient>> rpcClients;
708 
709 // Trigger an abort on all associated clients
710 kj::Promise<void> sendToRpc(kj::Array<kj::byte>&& reason);
711 
712 // ---------------------------------------------------------------
713 // RPC server functionality. Used if this signal was deserialized.
714 
715 // A promise that is fulfilled if an abort() message is received over RPC.
716 kj::Maybe<IoOwn<kj::Promise<void>>> rpcAbortPromise;
717 
718 // A refcounted object used to receive a serialized abort reason
719 // The abort reason is required in asynchronous event handlers as well as synchronous methods
720 // like getReason(). As a result, we can't pass the abort reason in the above promise, and both
721 // sync and async methods will need to check this value.
722 kj::Maybe<IoOwn<PendingReason>> pendingReason;
723 
724 // Synchronously check if an abort reason was sent over RPC
725 bool hasPendingReason();
726 kj::Maybe<jsg::JsValue> deserializePendingReason(jsg::Lock& js);
727 
728 // Wait for abort over RPC.
729 // We invoke this once at least one event handler is attached to the AbortSignal
730 void subscribeToRpcAbort(jsg::Lock& js);
731};
732 
733// An implementation of the Web Platform Standard AbortController API
734class AbortController final: public jsg::Object {
735 public:
736 explicit AbortController(
737 jsg::Lock& js, AbortSignal::Flag abortSignalFlag = AbortSignal::Flag::NONE)
738 : signal(js.alloc<AbortSignal>(
739 kj::none /* exception */, kj::none /* maybeReason */, abortSignalFlag)) {}
740 
741 static jsg::Ref<AbortController> constructor(jsg::Lock& js) {
742 return js.alloc<AbortController>(js);
743 }
744 
745 jsg::Ref<AbortSignal> getSignal() {
746 return signal.addRef();
747 }
748 
749 void abort(jsg::Lock& js, jsg::Optional<jsg::JsValue> reason);
750 
751 JSG_RESOURCE_TYPE(AbortController, CompatibilityFlags::Reader flags) {
752 if (flags.getJsgPropertyOnPrototypeTemplate()) {
753 JSG_READONLY_PROTOTYPE_PROPERTY(signal, getSignal);
754 } else {
755 JSG_READONLY_INSTANCE_PROPERTY(signal, getSignal);
756 }
757 JSG_METHOD(abort);
758 }
759 
760 void visitForMemoryInfo(jsg::MemoryTracker& tracker) const {
761 tracker.trackField("signal", signal);
762 }
763 
764 private:
765 jsg::Ref<AbortSignal> signal;
766 
767 void visitForGc(jsg::GcVisitor& visitor) {
768 // We have to be careful with GC here. The event listeners added to the AbortSignal
769 // could hold a circular reference to the AbortController.
770 visitor.visit(signal);
771 }
772};
773 
774// The scheduler class is an emerging web platform standard API that is meant
775// to be global and provides task scheduling APIs. We currently only implement
776// a subset of the API that is being defined.
777class Scheduler final: public jsg::Object {
778 public:
779 struct WaitOptions {
780 jsg::Optional<jsg::Ref<AbortSignal>> signal;
781 JSG_STRUCT(signal);
782 };
783 
784 // Returns a promise that resolves after the `delay` milliseconds.
785 // Essentially an awaitable alternative to setTimeout(). The wait
786 // can be canceled using an AbortSignal.
787 kj::Promise<void> wait(jsg::Lock& js, double delay, jsg::Optional<WaitOptions> maybeOptions);
788 
789 JSG_RESOURCE_TYPE(Scheduler) {
790 JSG_METHOD(wait);
791 }
792 
793 private:
794};
795 
796#define EW_BASICS_ISOLATE_TYPES \
797 api::Event, api::Event::Init, api::EventTarget, api::EventTarget::EventListenerOptions, \
798 api::EventTarget::AddEventListenerOptions, api::EventTarget::HandlerObject, \
799 api::AbortController, api::AbortSignal, api::Scheduler, api::Scheduler::WaitOptions, \
800 api::ExtendableEvent, api::CustomEvent, api::CustomEvent::CustomEventInit
801// The list of basics.h types that are added to worker.c++'s JSG_DECLARE_ISOLATE_TYPE
802 
803} // namespace workerd::api