summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2020-03-05 08:36:13 -0500
committerGitHub <noreply@github.com>2020-03-05 08:36:13 -0500
commitc850b258b4e2487b0456a95cd6b65c169ffb9de2 (patch)
treed631f6995c048b0c7cddb600d4f33297922ba5b1
parent54a1688868810af0ef16f5c186dfb839f2fce23f (diff)
Support async function and EventListenerObject as listeners (#4240)
-rw-r--r--cli/js/dom_types.ts36
-rw-r--r--cli/js/event_target.ts25
-rw-r--r--cli/js/event_target_test.ts79
-rw-r--r--cli/js/globals.ts2
-rw-r--r--cli/js/lib.deno.shared_globals.d.ts51
5 files changed, 140 insertions, 53 deletions
diff --git a/cli/js/dom_types.ts b/cli/js/dom_types.ts
index 1e6c622c8..cdd681615 100644
--- a/cli/js/dom_types.ts
+++ b/cli/js/dom_types.ts
@@ -76,20 +76,44 @@ export const eventTargetListeners: unique symbol = Symbol();
export const eventTargetMode: unique symbol = Symbol();
export const eventTargetNodeType: unique symbol = Symbol();
+export interface EventListener {
+ // Different from lib.dom.d.ts. Added Promise<void>
+ (evt: Event): void | Promise<void>;
+}
+
+export interface EventListenerObject {
+ // Different from lib.dom.d.ts. Added Promise<void>
+ handleEvent(evt: Event): void | Promise<void>;
+}
+
+export type EventListenerOrEventListenerObject =
+ | EventListener
+ | EventListenerObject;
+
+// This is actually not part of actual DOM types,
+// but an implementation specific thing on our custom EventTarget
+// (due to the presence of our custom symbols)
+export interface EventTargetListener {
+ callback: EventListenerOrEventListenerObject;
+ options: AddEventListenerOptions;
+}
+
export interface EventTarget {
+ // TODO: below 4 symbol props should not present on EventTarget WebIDL.
+ // They should be implementation specific details.
[eventTargetHost]: EventTarget | null;
- [eventTargetListeners]: { [type in string]: EventListener[] };
+ [eventTargetListeners]: { [type in string]: EventTargetListener[] };
[eventTargetMode]: string;
[eventTargetNodeType]: NodeType;
addEventListener(
type: string,
- callback: (event: Event) => void | null,
+ listener: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions
): void;
dispatchEvent(event: Event): boolean;
removeEventListener(
type: string,
- callback?: (event: Event) => void | null,
+ listener: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void;
}
@@ -147,12 +171,6 @@ export interface URLSearchParams extends DomIterable<string, string> {
): void;
}
-export interface EventListener {
- handleEvent(event: Event): void;
- readonly callback: (event: Event) => void | null;
- readonly options: boolean | AddEventListenerOptions;
-}
-
export interface EventInit {
bubbles?: boolean;
cancelable?: boolean;
diff --git a/cli/js/event_target.ts b/cli/js/event_target.ts
index daa73eb23..03557526a 100644
--- a/cli/js/event_target.ts
+++ b/cli/js/event_target.ts
@@ -25,7 +25,7 @@ export const eventTargetHasActivationBehavior: unique symbol = Symbol();
export class EventTarget implements domTypes.EventTarget {
public [domTypes.eventTargetHost]: domTypes.EventTarget | null = null;
public [domTypes.eventTargetListeners]: {
- [type in string]: domTypes.EventListener[];
+ [type in string]: domTypes.EventTargetListener[];
} = {};
public [domTypes.eventTargetMode] = "";
public [domTypes.eventTargetNodeType]: domTypes.NodeType =
@@ -35,7 +35,7 @@ export class EventTarget implements domTypes.EventTarget {
public addEventListener(
type: string,
- callback: (event: domTypes.Event) => void | null,
+ callback: domTypes.EventListenerOrEventListenerObject | null,
options?: domTypes.AddEventListenerOptions | boolean
): void {
const this_ = this || globalThis;
@@ -68,20 +68,15 @@ export class EventTarget implements domTypes.EventTarget {
}
}
- // eslint-disable-next-line @typescript-eslint/no-this-alias
- const eventTarget = this;
listeners[type].push({
callback,
- options: normalizedOptions,
- handleEvent(event: domTypes.Event): void {
- this.callback.call(eventTarget, event);
- }
- } as domTypes.EventListener);
+ options: normalizedOptions
+ });
}
public removeEventListener(
type: string,
- callback: (event: domTypes.Event) => void | null,
+ callback: domTypes.EventListenerOrEventListenerObject | null,
options?: domTypes.EventListenerOptions | boolean
): void {
const this_ = this || globalThis;
@@ -359,7 +354,7 @@ const eventTargetHelpers = {
innerInvokeEventListeners(
targetImpl: EventTarget,
eventImpl: domTypes.Event,
- targetListeners: { [type in string]: domTypes.EventListener[] }
+ targetListeners: { [type in string]: domTypes.EventTargetListener[] }
): boolean {
let found = false;
@@ -413,7 +408,13 @@ const eventTargetHelpers = {
}
try {
- listener.handleEvent(eventImpl);
+ if (typeof listener.callback === "object") {
+ if (typeof listener.callback.handleEvent === "function") {
+ listener.callback.handleEvent(eventImpl);
+ }
+ } else {
+ listener.callback.call(eventImpl.currentTarget, eventImpl);
+ }
} catch (error) {
// TODO(bartlomieju): very likely that different error
// should be thrown here (DOMException?)
diff --git a/cli/js/event_target_test.ts b/cli/js/event_target_test.ts
index af7895081..376c96dd0 100644
--- a/cli/js/event_target_test.ts
+++ b/cli/js/event_target_test.ts
@@ -39,7 +39,7 @@ unitTest(function anEventTargetCanBeSubclassed(): void {
class NicerEventTarget extends EventTarget {
on(
type: string,
- callback: (e: Event) => void | null,
+ callback: ((e: Event) => void) | null,
options?: __domTypes.AddEventListenerOptions
): void {
this.addEventListener(type, callback, options);
@@ -47,7 +47,7 @@ unitTest(function anEventTargetCanBeSubclassed(): void {
off(
type: string,
- callback: (e: Event) => void | null,
+ callback: ((e: Event) => void) | null,
options?: __domTypes.EventListenerOptions
): void {
this.removeEventListener(type, callback, options);
@@ -154,3 +154,78 @@ unitTest(function eventTargetThisShouldDefaultToWindow(): void {
dispatchEvent(event);
assertEquals(n, 1);
});
+
+test(function eventTargetShouldAcceptEventListenerObject(): void {
+ const target = new EventTarget();
+ const event = new Event("foo", { bubbles: true, cancelable: false });
+ let callCount = 0;
+
+ const listener = {
+ handleEvent(e: Event): void {
+ assertEquals(e, event);
+ ++callCount;
+ }
+ };
+
+ target.addEventListener("foo", listener);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+
+test(function eventTargetShouldAcceptAsyncFunction(): void {
+ const target = new EventTarget();
+ const event = new Event("foo", { bubbles: true, cancelable: false });
+ let callCount = 0;
+
+ const listener = async (e: Event): Promise<void> => {
+ assertEquals(e, event);
+ ++callCount;
+ };
+
+ target.addEventListener("foo", listener);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+});
+
+test(
+ function eventTargetShouldAcceptAsyncFunctionForEventListenerObject(): void {
+ const target = new EventTarget();
+ const event = new Event("foo", { bubbles: true, cancelable: false });
+ let callCount = 0;
+
+ const listener = {
+ async handleEvent(e: Event): Promise<void> {
+ assertEquals(e, event);
+ ++callCount;
+ }
+ };
+
+ target.addEventListener("foo", listener);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 1);
+
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+
+ target.removeEventListener("foo", listener);
+ target.dispatchEvent(event);
+ assertEquals(callCount, 2);
+ }
+);
diff --git a/cli/js/globals.ts b/cli/js/globals.ts
index fd2082e40..0e3ae8fd8 100644
--- a/cli/js/globals.ts
+++ b/cli/js/globals.ts
@@ -104,7 +104,7 @@ declare global {
/* eslint-disable no-var */
var addEventListener: (
type: string,
- callback: (event: domTypes.Event) => void | null,
+ callback: domTypes.EventListenerOrEventListenerObject | null,
options?: boolean | domTypes.AddEventListenerOptions | undefined
) => void;
var queueMicrotask: (callback: () => void) => void;
diff --git a/cli/js/lib.deno.shared_globals.d.ts b/cli/js/lib.deno.shared_globals.d.ts
index fef155f3d..9ec045a8e 100644
--- a/cli/js/lib.deno.shared_globals.d.ts
+++ b/cli/js/lib.deno.shared_globals.d.ts
@@ -42,13 +42,13 @@ declare interface WindowOrWorkerGlobalScope {
addEventListener: (
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: boolean | __domTypes.AddEventListenerOptions | undefined
) => void;
dispatchEvent: (event: __domTypes.Event) => boolean;
removeEventListener: (
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: boolean | __domTypes.EventListenerOptions | undefined
) => void;
}
@@ -240,7 +240,7 @@ declare const CustomEventInit: typeof __customEvent.CustomEventInit;
declare const CustomEvent: typeof __customEvent.CustomEvent;
declare const EventInit: typeof __event.EventInit;
declare const Event: typeof __event.Event;
-declare const EventListener: typeof __eventTarget.EventListener;
+declare const EventListener: __domTypes.EventListener;
declare const EventTarget: typeof __eventTarget.EventTarget;
declare const URL: typeof __url.URL;
declare const URLSearchParams: typeof __urlSearchParams.URLSearchParams;
@@ -256,13 +256,13 @@ declare const Worker: typeof __workers.WorkerImpl;
declare const addEventListener: (
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: boolean | __domTypes.AddEventListenerOptions | undefined
) => void;
declare const dispatchEvent: (event: __domTypes.Event) => boolean;
declare const removeEventListener: (
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: boolean | __domTypes.EventListenerOptions | undefined
) => void;
@@ -346,6 +346,19 @@ declare namespace __domTypes {
export const eventTargetListeners: unique symbol;
export const eventTargetMode: unique symbol;
export const eventTargetNodeType: unique symbol;
+ export interface EventListener {
+ (evt: Event): void | Promise<void>;
+ }
+ export interface EventListenerObject {
+ handleEvent(evt: Event): void | Promise<void>;
+ }
+ export type EventListenerOrEventListenerObject =
+ | EventListener
+ | EventListenerObject;
+ export interface EventTargetListener {
+ callback: EventListenerOrEventListenerObject;
+ options: AddEventListenerOptions;
+ }
export interface EventTarget {
[eventTargetHost]: EventTarget | null;
[eventTargetListeners]: { [type in string]: EventListener[] };
@@ -353,13 +366,13 @@ declare namespace __domTypes {
[eventTargetNodeType]: NodeType;
addEventListener(
type: string,
- callback: (event: Event) => void | null,
+ callback: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions
): void;
dispatchEvent(event: Event): boolean;
removeEventListener(
type: string,
- callback?: (event: Event) => void | null,
+ callback?: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void;
}
@@ -414,11 +427,6 @@ declare namespace __domTypes {
thisArg?: any
): void;
}
- export interface EventListener {
- handleEvent(event: Event): void;
- readonly callback: (event: Event) => void | null;
- readonly options: boolean | AddEventListenerOptions;
- }
export interface EventInit {
bubbles?: boolean;
cancelable?: boolean;
@@ -1095,21 +1103,6 @@ declare namespace __eventTarget {
readonly passive: boolean;
readonly once: boolean;
}
- export class EventListener implements __domTypes.EventListener {
- allEvents: __domTypes.Event[];
- atEvents: __domTypes.Event[];
- bubbledEvents: __domTypes.Event[];
- capturedEvents: __domTypes.Event[];
- private _callback;
- private _options;
- constructor(
- callback: (event: __domTypes.Event) => void | null,
- options: boolean | __domTypes.AddEventListenerOptions
- );
- handleEvent(event: __domTypes.Event): void;
- readonly callback: (event: __domTypes.Event) => void | null;
- readonly options: __domTypes.AddEventListenerOptions | boolean;
- }
export const eventTargetAssignedSlot: unique symbol;
export const eventTargetHasActivationBehavior: unique symbol;
export class EventTarget implements __domTypes.EventTarget {
@@ -1123,12 +1116,12 @@ declare namespace __eventTarget {
private [eventTargetHasActivationBehavior];
addEventListener(
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: __domTypes.AddEventListenerOptions | boolean
): void;
removeEventListener(
type: string,
- callback: (event: __domTypes.Event) => void | null,
+ callback: __domTypes.EventListenerOrEventListenerObject | null,
options?: __domTypes.EventListenerOptions | boolean
): void;
dispatchEvent(event: __domTypes.Event): boolean;