summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/unit/event_target_test.ts37
-rw-r--r--ext/web/02_event.js20
-rw-r--r--ext/web/lib.deno_web.d.ts1
-rw-r--r--tools/wpt/testharnessreport.js7
4 files changed, 58 insertions, 7 deletions
diff --git a/cli/tests/unit/event_target_test.ts b/cli/tests/unit/event_target_test.ts
index dfc28605b..f732003ec 100644
--- a/cli/tests/unit/event_target_test.ts
+++ b/cli/tests/unit/event_target_test.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-window-prefix
-import { assertEquals } from "./test_util.ts";
+import { assertEquals, assertThrows } from "./test_util.ts";
Deno.test(function addEventListenerTest() {
const document = new EventTarget();
@@ -244,3 +244,38 @@ Deno.test(function eventTargetDispatchShouldSetTargetInListener() {
target.dispatchEvent(event);
assertEquals(called, true);
});
+
+Deno.test(function eventTargetAddEventListenerGlobalAbort() {
+ return new Promise((resolve) => {
+ const c = new AbortController();
+
+ c.signal.addEventListener("abort", () => resolve());
+ addEventListener("test", () => {}, { signal: c.signal });
+ c.abort();
+ });
+});
+
+Deno.test(function eventTargetBrandChecking() {
+ const self = {};
+
+ assertThrows(
+ () => {
+ EventTarget.prototype.addEventListener.call(self, "test", null);
+ },
+ TypeError,
+ );
+
+ assertThrows(
+ () => {
+ EventTarget.prototype.removeEventListener.call(self, "test", null);
+ },
+ TypeError,
+ );
+
+ assertThrows(
+ () => {
+ EventTarget.prototype.dispatchEvent.call(self, new Event("test"));
+ },
+ TypeError,
+ );
+});
diff --git a/ext/web/02_event.js b/ext/web/02_event.js
index 9d5169b9e..f5b4bd7d3 100644
--- a/ext/web/02_event.js
+++ b/ext/web/02_event.js
@@ -917,6 +917,7 @@
class EventTarget {
constructor() {
this[eventTargetData] = getDefaultTargetData();
+ this[webidl.brand] = webidl.brand;
}
addEventListener(
@@ -924,6 +925,8 @@
callback,
options,
) {
+ const self = this ?? globalThis;
+ webidl.assertBranded(self, EventTargetPrototype);
const prefix = "Failed to execute 'addEventListener' on 'EventTarget'";
webidl.requiredArguments(arguments.length, 2, {
@@ -939,7 +942,7 @@
return;
}
- const { listeners } = (this ?? globalThis)[eventTargetData];
+ const { listeners } = self[eventTargetData];
if (!(ReflectHas(listeners, type))) {
listeners[type] = [];
@@ -965,7 +968,7 @@
// If listener’s signal is not null, then add the following abort
// abort steps to it: Remove an event listener.
signal.addEventListener("abort", () => {
- this.removeEventListener(type, callback, options);
+ self.removeEventListener(type, callback, options);
});
}
}
@@ -978,11 +981,13 @@
callback,
options,
) {
+ const self = this ?? globalThis;
+ webidl.assertBranded(self, EventTargetPrototype);
webidl.requiredArguments(arguments.length, 2, {
prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
});
- const { listeners } = (this ?? globalThis)[eventTargetData];
+ const { listeners } = self[eventTargetData];
if (callback !== null && ReflectHas(listeners, type)) {
listeners[type] = ArrayPrototypeFilter(
listeners[type],
@@ -1010,14 +1015,15 @@
}
dispatchEvent(event) {
- webidl.requiredArguments(arguments.length, 1, {
- prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
- });
// If `this` is not present, then fallback to global scope. We don't use
// `globalThis` directly here, because it could be deleted by user.
// Instead use saved reference to global scope when the script was
// executed.
const self = this ?? window;
+ webidl.assertBranded(self, EventTargetPrototype);
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
+ });
const { listeners } = self[eventTargetData];
if (!ReflectHas(listeners, event.type)) {
@@ -1042,6 +1048,7 @@
}
webidl.configurePrototype(EventTarget);
+ const EventTargetPrototype = EventTarget.prototype;
defineEnumerableProps(EventTarget, [
"addEventListener",
@@ -1411,6 +1418,7 @@
reportException(error);
}
+ window[webidl.brand] = webidl.brand;
window.Event = Event;
window.EventTarget = EventTarget;
window.ErrorEvent = ErrorEvent;
diff --git a/ext/web/lib.deno_web.d.ts b/ext/web/lib.deno_web.d.ts
index 00b494684..e3fc58be0 100644
--- a/ext/web/lib.deno_web.d.ts
+++ b/ext/web/lib.deno_web.d.ts
@@ -142,6 +142,7 @@ declare type EventListenerOrEventListenerObject =
interface AddEventListenerOptions extends EventListenerOptions {
once?: boolean;
passive?: boolean;
+ signal?: AbortSignal;
}
interface EventListenerOptions {
diff --git a/tools/wpt/testharnessreport.js b/tools/wpt/testharnessreport.js
index 3368faa4e..a07c916c7 100644
--- a/tools/wpt/testharnessreport.js
+++ b/tools/wpt/testharnessreport.js
@@ -18,5 +18,12 @@ window.add_completion_callback((_tests, harnessStatus) => {
while (bytesWritten < data.byteLength) {
bytesWritten += Deno.stderr.writeSync(data.subarray(bytesWritten));
}
+
+ // TODO(cjihrig): Restore the prototype of globalThis to be an EventTarget
+ // again. There are WPTs that change the prototype, which causes brand
+ // checking to fail. Once the globalThis prototype is frozen properly, this
+ // line can be removed.
+ Object.setPrototypeOf(globalThis, EventTarget.prototype);
+
Deno.exit(harnessStatus.status === 0 ? 0 : 1);
});