Skip to content

Commit 4d18f55

Browse files
authored
feat(ext/web): Add error events for event listener and timer errors (#14159)
- feat: Add handleable error event for even listener errors - feat: Add handleable error event for setTimeout()/setInterval() errors - feat: Add Deno.core.destructureError() - feat: Add Deno.core.terminate() - fix: Don't throw listener errors from dispatchEvent() - fix: Use biased mode when selecting between mod_evaluate() and run_event_loop() results
1 parent d621ce1 commit 4d18f55

35 files changed

+440
-131
lines changed

.github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ jobs:
236236
~/.cargo/registry/index
237237
~/.cargo/registry/cache
238238
~/.cargo/git/db
239-
key: 5-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
239+
key: 7-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
240240

241241
# In main branch, always creates fresh cache
242242
- name: Cache build output (main)
@@ -252,7 +252,7 @@ jobs:
252252
!./target/*/*.zip
253253
!./target/*/*.tar.gz
254254
key: |
255-
5-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
255+
7-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
256256
257257
# Restore cache from the latest 'main' branch build.
258258
- name: Cache build output (PR)
@@ -268,7 +268,7 @@ jobs:
268268
!./target/*/*.tar.gz
269269
key: never_saved
270270
restore-keys: |
271-
5-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
271+
7-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
272272
273273
# Don't save cache after building PRs or branches other than 'main'.
274274
- name: Skip save cache (PR)

cli/dts/lib.deno.window.d.ts

+51-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77
/// <reference lib="deno.webstorage" />
88
/// <reference lib="esnext" />
99

10+
interface WindowEventMap {
11+
"error": ErrorEvent;
12+
}
13+
1014
declare class Window extends EventTarget {
1115
new(): Window;
1216
readonly window: Window & typeof globalThis;
1317
readonly self: Window & typeof globalThis;
18+
onerror: ((this: Window, ev: ErrorEvent) => any) | null;
1419
onload: ((this: Window, ev: Event) => any) | null;
1520
onunload: ((this: Window, ev: Event) => any) | null;
1621
close: () => void;
@@ -25,10 +30,38 @@ declare class Window extends EventTarget {
2530
location: Location;
2631
localStorage: Storage;
2732
sessionStorage: Storage;
33+
34+
addEventListener<K extends keyof WindowEventMap>(
35+
type: K,
36+
listener: (
37+
this: Window,
38+
ev: WindowEventMap[K],
39+
) => any,
40+
options?: boolean | AddEventListenerOptions,
41+
): void;
42+
addEventListener(
43+
type: string,
44+
listener: EventListenerOrEventListenerObject,
45+
options?: boolean | AddEventListenerOptions,
46+
): void;
47+
removeEventListener<K extends keyof WindowEventMap>(
48+
type: K,
49+
listener: (
50+
this: Window,
51+
ev: WindowEventMap[K],
52+
) => any,
53+
options?: boolean | EventListenerOptions,
54+
): void;
55+
removeEventListener(
56+
type: string,
57+
listener: EventListenerOrEventListenerObject,
58+
options?: boolean | EventListenerOptions,
59+
): void;
2860
}
2961

3062
declare var window: Window & typeof globalThis;
3163
declare var self: Window & typeof globalThis;
64+
declare var onerror: ((this: Window, ev: ErrorEvent) => any) | null;
3265
declare var onload: ((this: Window, ev: Event) => any) | null;
3366
declare var onunload: ((this: Window, ev: Event) => any) | null;
3467
declare var localStorage: Storage;
@@ -77,10 +110,17 @@ declare function prompt(message?: string, defaultValue?: string): string | null;
77110
* dispatchEvent(new Event('unload'));
78111
* ```
79112
*/
113+
declare function addEventListener<
114+
K extends keyof WindowEventMap,
115+
>(
116+
type: K,
117+
listener: (this: Window, ev: WindowEventMap[K]) => any,
118+
options?: boolean | AddEventListenerOptions,
119+
): void;
80120
declare function addEventListener(
81121
type: string,
82-
callback: EventListenerOrEventListenerObject | null,
83-
options?: boolean | AddEventListenerOptions | undefined,
122+
listener: EventListenerOrEventListenerObject,
123+
options?: boolean | AddEventListenerOptions,
84124
): void;
85125

86126
/** Remove a previously registered event listener from the global scope
@@ -91,10 +131,17 @@ declare function addEventListener(
91131
* removeEventListener('load', listener);
92132
* ```
93133
*/
134+
declare function removeEventListener<
135+
K extends keyof WindowEventMap,
136+
>(
137+
type: K,
138+
listener: (this: Window, ev: WindowEventMap[K]) => any,
139+
options?: boolean | EventListenerOptions,
140+
): void;
94141
declare function removeEventListener(
95142
type: string,
96-
callback: EventListenerOrEventListenerObject | null,
97-
options?: boolean | EventListenerOptions | undefined,
143+
listener: EventListenerOrEventListenerObject,
144+
options?: boolean | EventListenerOptions,
98145
): void;
99146

100147
// TODO(nayeemrmn): Move this to `extensions/web` where its implementation is.

cli/tests/integration/run_tests.rs

+29
Original file line numberDiff line numberDiff line change
@@ -2685,3 +2685,32 @@ itest!(future_check2 {
26852685
output: "future_check2.out",
26862686
envs: vec![("DENO_FUTURE_CHECK".to_string(), "1".to_string())],
26872687
});
2688+
2689+
itest!(event_listener_error {
2690+
args: "run --quiet event_listener_error.ts",
2691+
output: "event_listener_error.ts.out",
2692+
exit_code: 1,
2693+
});
2694+
2695+
itest!(event_listener_error_handled {
2696+
args: "run --quiet event_listener_error_handled.ts",
2697+
output: "event_listener_error_handled.ts.out",
2698+
});
2699+
2700+
// https://github.com/denoland/deno/pull/14159#issuecomment-1092285446
2701+
itest!(event_listener_error_immediate_exit {
2702+
args: "run --quiet event_listener_error_immediate_exit.ts",
2703+
output: "event_listener_error_immediate_exit.ts.out",
2704+
exit_code: 1,
2705+
});
2706+
2707+
itest!(set_timeout_error {
2708+
args: "run --quiet set_timeout_error.ts",
2709+
output: "set_timeout_error.ts.out",
2710+
exit_code: 1,
2711+
});
2712+
2713+
itest!(set_timeout_error_handled {
2714+
args: "run --quiet set_timeout_error_handled.ts",
2715+
output: "set_timeout_error_handled.ts.out",
2716+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
addEventListener("foo", () => {
2+
throw new Error("bar");
3+
});
4+
console.log(1);
5+
dispatchEvent(new CustomEvent("foo"));
6+
console.log(2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
1
2+
error: Uncaught Error: bar
3+
throw new Error("bar");
4+
^
5+
at [WILDCARD]/event_listener_error.ts:2:9
6+
at [WILDCARD]
7+
at [WILDCARD]/event_listener_error.ts:5:1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
addEventListener("error", (event) => {
2+
console.log({
3+
cancelable: event.cancelable,
4+
message: event.message,
5+
filename: event.filename?.slice?.(-100),
6+
lineno: event.lineno,
7+
colno: event.colno,
8+
error: event.error,
9+
});
10+
event.preventDefault();
11+
});
12+
13+
onerror = (event) => {
14+
console.log("onerror() called", event.error);
15+
};
16+
17+
addEventListener("foo", () => {
18+
throw new Error("bar");
19+
});
20+
21+
console.log(1);
22+
dispatchEvent(new CustomEvent("foo"));
23+
console.log(2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
1
2+
{
3+
cancelable: true,
4+
message: "Uncaught Error: bar",
5+
filename: "[WILDCARD]/event_listener_error_handled.ts",
6+
lineno: 18,
7+
colno: 9,
8+
error: Error: bar
9+
at [WILDCARD]/event_listener_error_handled.ts:18:9
10+
at [WILDCARD]
11+
at [WILDCARD]/event_listener_error_handled.ts:22:1
12+
}
13+
onerror() called Error: bar
14+
at [WILDCARD]/event_listener_error_handled.ts:18:9
15+
at [WILDCARD]
16+
at [WILDCARD]/event_listener_error_handled.ts:22:1
17+
2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
addEventListener("foo", () => {
2+
queueMicrotask(() => console.log("queueMicrotask"));
3+
setTimeout(() => console.log("timer"), 0);
4+
throw new Error("bar");
5+
});
6+
console.log(1);
7+
// @ts-ignore Deno.core
8+
Deno.core.setNextTickCallback(() => console.log("nextTick"));
9+
// @ts-ignore Deno.core
10+
Deno.core.setHasTickScheduled(true);
11+
dispatchEvent(new CustomEvent("foo"));
12+
console.log(2);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1
2+
error: Uncaught Error: bar
3+
throw new Error("bar");
4+
^
5+
at [WILDCARD]/event_listener_error_immediate_exit.ts:4:9[WILDCARD]
6+
at [WILDCARD]/event_listener_error_immediate_exit.ts:11:1
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
setTimeout(() => {
2+
throw new Error("foo");
3+
}, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Uncaught Error: foo
2+
throw new Error("foo");
3+
^
4+
at [WILDCARD]/set_timeout_error.ts:2:9
5+
at [WILDCARD]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
addEventListener("error", (event) => {
2+
console.log({
3+
cancelable: event.cancelable,
4+
message: event.message,
5+
filename: event.filename?.slice?.(-100),
6+
lineno: event.lineno,
7+
colno: event.colno,
8+
error: event.error,
9+
});
10+
event.preventDefault();
11+
});
12+
13+
onerror = (event) => {
14+
console.log("onerror() called", event.error);
15+
};
16+
17+
setTimeout(() => {
18+
throw new Error("foo");
19+
}, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
cancelable: true,
3+
message: "Uncaught Error: foo",
4+
filename: "[WILDCARD]/set_timeout_error_handled.ts",
5+
lineno: 18,
6+
colno: 9,
7+
error: Error: foo
8+
at [WILDCARD]/set_timeout_error_handled.ts:18:9
9+
at [WILDCARD]
10+
}
11+
onerror() called Error: foo
12+
at [WILDCARD]/set_timeout_error_handled.ts:18:9
13+
at [WILDCARD]

cli/tests/testdata/worker_drop_handle_race.js.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ error: Uncaught (in worker "") Error
44
at [WILDCARD]/workers/drop_handle_race.js:2:9
55
at Object.action (deno:ext/web/02_timers.js:[WILDCARD])
66
at handleTimerMacrotask (deno:ext/web/02_timers.js:[WILDCARD])
7-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
7+
error: Uncaught (in promise) Error: Unhandled error in child worker.
88
at Worker.#pollControl (deno:runtime/js/11_workers.js:[WILDCARD])

cli/tests/testdata/worker_event_handler_test.js.out

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
Target from self.onmessage: [object DedicatedWorkerGlobalScope]
22
Target from message event listener: [object DedicatedWorkerGlobalScope]
33
Arguments from self.onerror: [
4-
"Some error message",
5-
"",
6-
0,
7-
0,
4+
"Uncaught Error: Some error message",
5+
"[WILDCARD]/worker_event_handlers.js",
6+
9,
7+
9,
88
Error: Some error message
99
at [WILDCARD]
1010
]
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[WILDCARD]error: Uncaught (in worker "") Module not found "file:///[WILDCARD]/workers/doesnt_exist.js".
2-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
2+
error: Uncaught (in promise) Error: Unhandled error in child worker.
33
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
22
at blob:null/[WILDCARD]:1:8
3-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
3+
error: Uncaught (in promise) Error: Unhandled error in child worker.
44
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
22
at blob:null/[WILDCARD]:1:8
3-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
3+
error: Uncaught (in promise) Error: Unhandled error in child worker.
44
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Uncaught (in worker "") Requires read access to "[WILDCARD]local_file.ts", run again with the --allow-read flag
22
at data:application/javascript;base64,[WILDCARD]:1:8
3-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
3+
error: Uncaught (in promise) Error: Unhandled error in child worker.
44
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
22
at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8
3-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
3+
error: Uncaught (in promise) Error: Unhandled error in child worker.
44
at Worker.#pollControl ([WILDCARD])

cli/tests/testdata/workers/permissions_dynamic_remote.ts.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "e
22
await import("https://example.com/some/file.ts");
33
^
44
at async http://localhost:4545/workers/dynamic_remote.ts:2:1
5-
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error event in child worker.
5+
[WILDCARD]error: Uncaught (in promise) Error: Unhandled error in child worker.
66
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag
22
at http://localhost:4545/workers/static_remote.ts:2:8
3-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
3+
error: Uncaught (in promise) Error: Unhandled error in child worker.
44
at Worker.#pollControl ([WILDCARD])

cli/tests/testdata/workers/worker_async_error.ts.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ error: Uncaught (in worker "foo") (in promise) Error: bar
33
^
44
at [WILDCARD]/async_error.ts:[WILDCARD]
55
at [WILDCARD]/async_error.ts:[WILDCARD]
6-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
6+
error: Uncaught (in promise) Error: Unhandled error in child worker.
77
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[WILDCARD]error: Uncaught (in worker "bar") Error: foo[WILDCARD]
22
at foo ([WILDCARD])
33
at [WILDCARD]
4-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
4+
error: Uncaught (in promise) Error: Unhandled error in child worker.
55
at Worker.#pollControl ([WILDCARD])
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
error: Uncaught (in worker "foo") (in promise) Error: bar
1+
error: Uncaught (in worker "foo") Error: bar
22
throw new Error("bar");
33
^
44
at onmessage ([WILDCARD]/message_handler_error.ts:[WILDCARD])
55
at [WILDCARD]
6-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
6+
error: Uncaught (in promise) Error: Unhandled error in child worker.
77
at Worker.#pollControl ([WILDCARD])

cli/tests/testdata/workers/worker_nested_error.ts.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
^
44
at foo ([WILDCARD]/workers/error.ts:[WILDCARD])
55
at [WILDCARD]/workers/error.ts:[WILDCARD]
6-
error: Uncaught (in worker "baz") (in promise) Error: Unhandled error event in child worker.
6+
error: Uncaught (in worker "baz") (in promise) Error: Unhandled error in child worker.
77
at Worker.#pollControl ([WILDCARD])
8-
error: Uncaught (in promise) Error: Unhandled error event in child worker.
8+
error: Uncaught (in promise) Error: Unhandled error in child worker.
99
at Worker.#pollControl ([WILDCARD])

0 commit comments

Comments
 (0)