diff --git a/cli/tests/error_009_op_crates_error.js.out b/cli/tests/error_009_op_crates_error.js.out
index fd428b28e9..b46ba3c168 100644
--- a/cli/tests/error_009_op_crates_error.js.out
+++ b/cli/tests/error_009_op_crates_error.js.out
@@ -1,3 +1,3 @@
-[WILDCARD]error: Uncaught TypeError: Event requires at least 1 argument, but only 0 present[WILDCARD]
+[WILDCARD]error: Uncaught TypeError: Failed to construct 'Event': 1 argument, but only 0 present.[WILDCARD]
at new Event (deno:op_crates/web/[WILDCARD])
at [WILDCARD]
diff --git a/cli/tests/unit/event_test.ts b/cli/tests/unit/event_test.ts
index 149509c9d4..9eb90fa36c 100644
--- a/cli/tests/unit/event_test.ts
+++ b/cli/tests/unit/event_test.ts
@@ -99,27 +99,27 @@ unitTest(function eventInspectOutput(): void {
[
new Event("test"),
(event: Event) =>
- `Event {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n target: null,\n timeStamp: ${event.timeStamp},\n type: "test"\n}`,
+ `Event {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "test"\n}`,
],
[
new ErrorEvent("error"),
(event: Event) =>
- `ErrorEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n target: null,\n timeStamp: ${event.timeStamp},\n type: "error",\n message: "",\n filename: "",\n lineno: 0,\n colno: 0,\n error: null\n}`,
+ `ErrorEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "error",\n message: "",\n filename: "",\n lineno: 0,\n colno: 0,\n error: null\n}`,
],
[
new CloseEvent("close"),
(event: Event) =>
- `CloseEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n target: null,\n timeStamp: ${event.timeStamp},\n type: "close",\n wasClean: false,\n code: 0,\n reason: ""\n}`,
+ `CloseEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "close",\n wasClean: false,\n code: 0,\n reason: ""\n}`,
],
[
new CustomEvent("custom"),
(event: Event) =>
- `CustomEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n target: null,\n timeStamp: ${event.timeStamp},\n type: "custom",\n detail: undefined\n}`,
+ `CustomEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "custom",\n detail: undefined\n}`,
],
[
new ProgressEvent("progress"),
(event: Event) =>
- `ProgressEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n target: null,\n timeStamp: ${event.timeStamp},\n type: "progress",\n lengthComputable: false,\n loaded: 0,\n total: 0\n}`,
+ `ProgressEvent {\n bubbles: false,\n cancelable: false,\n composed: false,\n currentTarget: null,\n defaultPrevented: false,\n eventPhase: 0,\n srcElement: null,\n target: null,\n returnValue: true,\n timeStamp: ${event.timeStamp},\n type: "progress",\n lengthComputable: false,\n loaded: 0,\n total: 0\n}`,
],
];
diff --git a/op_crates/web/00_webidl.js b/op_crates/web/00_webidl.js
new file mode 100644
index 0000000000..72c58c3772
--- /dev/null
+++ b/op_crates/web/00_webidl.js
@@ -0,0 +1,671 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// Adapted from https://github.com/jsdom/webidl-conversions.
+// Copyright Domenic Denicola. Licensed under BSD-2-Clause License.
+// Original license at https://github.com/jsdom/webidl-conversions/blob/master/LICENSE.md.
+
+"use strict";
+
+((window) => {
+ function makeException(ErrorType, message, opts = {}) {
+ if (opts.globals) {
+ ErrorType = opts.globals[ErrorType.name];
+ }
+ return new ErrorType(
+ `${opts.prefix ? opts.prefix + ": " : ""}${
+ opts.context ? opts.context : "Value"
+ } ${message}.`,
+ );
+ }
+
+ function toNumber(value, opts = {}) {
+ if (!opts.globals) {
+ return +value;
+ }
+ if (typeof value === "bigint") {
+ throw opts.globals.TypeError("Cannot convert a BigInt value to a number");
+ }
+ return opts.globals.Number(value);
+ }
+
+ function type(V) {
+ if (V === null) {
+ return "Null";
+ }
+ switch (typeof V) {
+ case "undefined":
+ return "Undefined";
+ case "boolean":
+ return "Boolean";
+ case "number":
+ return "Number";
+ case "string":
+ return "String";
+ case "symbol":
+ return "Symbol";
+ case "bigint":
+ return "BigInt";
+ case "object":
+ // Falls through
+ case "function":
+ // Falls through
+ default:
+ // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for
+ // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for
+ // such cases. So treat the default case as an object.
+ return "Object";
+ }
+ }
+
+ // Round x to the nearest integer, choosing the even integer if it lies halfway between two.
+ function evenRound(x) {
+ // There are four cases for numbers with fractional part being .5:
+ //
+ // case | x | floor(x) | round(x) | expected | x <> 0 | x % 1 | x & 1 | example
+ // 1 | 2n + 0.5 | 2n | 2n + 1 | 2n | > | 0.5 | 0 | 0.5 -> 0
+ // 2 | 2n + 1.5 | 2n + 1 | 2n + 2 | 2n + 2 | > | 0.5 | 1 | 1.5 -> 2
+ // 3 | -2n - 0.5 | -2n - 1 | -2n | -2n | < | -0.5 | 0 | -0.5 -> 0
+ // 4 | -2n - 1.5 | -2n - 2 | -2n - 1 | -2n - 2 | < | -0.5 | 1 | -1.5 -> -2
+ // (where n is a non-negative integer)
+ //
+ // Branch here for cases 1 and 4
+ if (
+ (x > 0 && x % 1 === +0.5 && (x & 1) === 0) ||
+ (x < 0 && x % 1 === -0.5 && (x & 1) === 1)
+ ) {
+ return censorNegativeZero(Math.floor(x));
+ }
+
+ return censorNegativeZero(Math.round(x));
+ }
+
+ function integerPart(n) {
+ return censorNegativeZero(Math.trunc(n));
+ }
+
+ function sign(x) {
+ return x < 0 ? -1 : 1;
+ }
+
+ function modulo(x, y) {
+ // https://tc39.github.io/ecma262/#eqn-modulo
+ // Note that http://stackoverflow.com/a/4467559/3191 does NOT work for large modulos
+ const signMightNotMatch = x % y;
+ if (sign(y) !== sign(signMightNotMatch)) {
+ return signMightNotMatch + y;
+ }
+ return signMightNotMatch;
+ }
+
+ function censorNegativeZero(x) {
+ return x === 0 ? 0 : x;
+ }
+
+ function createIntegerConversion(bitLength, typeOpts) {
+ const isSigned = !typeOpts.unsigned;
+
+ let lowerBound;
+ let upperBound;
+ if (bitLength === 64) {
+ upperBound = Number.MAX_SAFE_INTEGER;
+ lowerBound = !isSigned ? 0 : Number.MIN_SAFE_INTEGER;
+ } else if (!isSigned) {
+ lowerBound = 0;
+ upperBound = Math.pow(2, bitLength) - 1;
+ } else {
+ lowerBound = -Math.pow(2, bitLength - 1);
+ upperBound = Math.pow(2, bitLength - 1) - 1;
+ }
+
+ const twoToTheBitLength = Math.pow(2, bitLength);
+ const twoToOneLessThanTheBitLength = Math.pow(2, bitLength - 1);
+
+ return (V, opts = {}) => {
+ let x = toNumber(V, opts);
+ x = censorNegativeZero(x);
+
+ if (opts.enforceRange) {
+ if (!Number.isFinite(x)) {
+ throw makeException(TypeError, "is not a finite number", opts);
+ }
+
+ x = integerPart(x);
+
+ if (x < lowerBound || x > upperBound) {
+ throw makeException(
+ TypeError,
+ `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`,
+ opts,
+ );
+ }
+
+ return x;
+ }
+
+ if (!Number.isNaN(x) && opts.clamp) {
+ x = Math.min(Math.max(x, lowerBound), upperBound);
+ x = evenRound(x);
+ return x;
+ }
+
+ if (!Number.isFinite(x) || x === 0) {
+ return 0;
+ }
+ x = integerPart(x);
+
+ // Math.pow(2, 64) is not accurately representable in JavaScript, so try to avoid these per-spec operations if
+ // possible. Hopefully it's an optimization for the non-64-bitLength cases too.
+ if (x >= lowerBound && x <= upperBound) {
+ return x;
+ }
+
+ // These will not work great for bitLength of 64, but oh well. See the README for more details.
+ x = modulo(x, twoToTheBitLength);
+ if (isSigned && x >= twoToOneLessThanTheBitLength) {
+ return x - twoToTheBitLength;
+ }
+ return x;
+ };
+ }
+
+ function createLongLongConversion(bitLength, { unsigned }) {
+ const upperBound = Number.MAX_SAFE_INTEGER;
+ const lowerBound = unsigned ? 0 : Number.MIN_SAFE_INTEGER;
+ const asBigIntN = unsigned ? BigInt.asUintN : BigInt.asIntN;
+
+ return (V, opts = {}) => {
+ let x = toNumber(V, opts);
+ x = censorNegativeZero(x);
+
+ if (opts.enforceRange) {
+ if (!Number.isFinite(x)) {
+ throw makeException(TypeError, "is not a finite number", opts);
+ }
+
+ x = integerPart(x);
+
+ if (x < lowerBound || x > upperBound) {
+ throw makeException(
+ TypeError,
+ `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`,
+ opts,
+ );
+ }
+
+ return x;
+ }
+
+ if (!Number.isNaN(x) && opts.clamp) {
+ x = Math.min(Math.max(x, lowerBound), upperBound);
+ x = evenRound(x);
+ return x;
+ }
+
+ if (!Number.isFinite(x) || x === 0) {
+ return 0;
+ }
+
+ let xBigInt = BigInt(integerPart(x));
+ xBigInt = asBigIntN(bitLength, xBigInt);
+ return Number(xBigInt);
+ };
+ }
+
+ const converters = [];
+
+ converters.any = (V) => {
+ return V;
+ };
+
+ converters.boolean = function (val) {
+ return !!val;
+ };
+
+ converters.byte = createIntegerConversion(8, { unsigned: false });
+ converters.octet = createIntegerConversion(8, { unsigned: true });
+
+ converters.short = createIntegerConversion(16, { unsigned: false });
+ converters["unsigned short"] = createIntegerConversion(16, {
+ unsigned: true,
+ });
+
+ converters.long = createIntegerConversion(32, { unsigned: false });
+ converters["unsigned long"] = createIntegerConversion(32, { unsigned: true });
+
+ converters["long long"] = createLongLongConversion(64, { unsigned: false });
+ converters["unsigned long long"] = createLongLongConversion(64, {
+ unsigned: true,
+ });
+
+ converters.float = (V, opts) => {
+ const x = toNumber(V, opts);
+
+ if (!Number.isFinite(x)) {
+ throw makeException(
+ TypeError,
+ "is not a finite floating-point value",
+ opts,
+ );
+ }
+
+ if (Object.is(x, -0)) {
+ return x;
+ }
+
+ const y = Math.fround(x);
+
+ if (!Number.isFinite(y)) {
+ throw makeException(
+ TypeError,
+ "is outside the range of a single-precision floating-point value",
+ opts,
+ );
+ }
+
+ return y;
+ };
+
+ converters["unrestricted float"] = (V, opts) => {
+ const x = toNumber(V, opts);
+
+ if (isNaN(x)) {
+ return x;
+ }
+
+ if (Object.is(x, -0)) {
+ return x;
+ }
+
+ return Math.fround(x);
+ };
+
+ converters.double = (V, opts) => {
+ const x = toNumber(V, opts);
+
+ if (!Number.isFinite(x)) {
+ throw makeException(
+ TypeError,
+ "is not a finite floating-point value",
+ opts,
+ );
+ }
+
+ return x;
+ };
+
+ converters["unrestricted double"] = (V, opts) => {
+ const x = toNumber(V, opts);
+
+ return x;
+ };
+
+ converters.DOMString = function (V, opts = {}) {
+ if (opts.treatNullAsEmptyString && V === null) {
+ return "";
+ }
+
+ if (typeof V === "symbol") {
+ throw makeException(
+ TypeError,
+ "is a symbol, which cannot be converted to a string",
+ opts,
+ );
+ }
+
+ const StringCtor = opts.globals ? opts.globals.String : String;
+ return StringCtor(V);
+ };
+
+ converters.ByteString = (V, opts) => {
+ const x = converters.DOMString(V, opts);
+ let c;
+ for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
+ if (c > 255) {
+ throw makeException(TypeError, "is not a valid ByteString", opts);
+ }
+ }
+
+ return x;
+ };
+
+ converters.USVString = (V, opts) => {
+ const S = converters.DOMString(V, opts);
+ const n = S.length;
+ const U = [];
+ for (let i = 0; i < n; ++i) {
+ const c = S.charCodeAt(i);
+ if (c < 0xd800 || c > 0xdfff) {
+ U.push(String.fromCodePoint(c));
+ } else if (0xdc00 <= c && c <= 0xdfff) {
+ U.push(String.fromCodePoint(0xfffd));
+ } else if (i === n - 1) {
+ U.push(String.fromCodePoint(0xfffd));
+ } else {
+ const d = S.charCodeAt(i + 1);
+ if (0xdc00 <= d && d <= 0xdfff) {
+ const a = c & 0x3ff;
+ const b = d & 0x3ff;
+ U.push(String.fromCodePoint((2 << 15) + (2 << 9) * a + b));
+ ++i;
+ } else {
+ U.push(String.fromCodePoint(0xfffd));
+ }
+ }
+ }
+
+ return U.join("");
+ };
+
+ converters.object = (V, opts) => {
+ if (type(V) !== "Object") {
+ throw makeException(TypeError, "is not an object", opts);
+ }
+
+ return V;
+ };
+
+ // Not exported, but used in Function and VoidFunction.
+
+ // Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so
+ // handling for that is omitted.
+ function convertCallbackFunction(V, opts) {
+ if (typeof V !== "function") {
+ throw makeException(TypeError, "is not a function", opts);
+ }
+ return V;
+ }
+
+ const abByteLengthGetter = Object.getOwnPropertyDescriptor(
+ ArrayBuffer.prototype,
+ "byteLength",
+ ).get;
+
+ function isNonSharedArrayBuffer(V) {
+ try {
+ // This will throw on SharedArrayBuffers, but not detached ArrayBuffers.
+ // (The spec says it should throw, but the spec conflicts with implementations: https://github.com/tc39/ecma262/issues/678)
+ abByteLengthGetter.call(V);
+
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ let sabByteLengthGetter;
+
+ function isSharedArrayBuffer(V) {
+ // TODO(lucacasonato): vulnerable to prototype pollution. Needs to happen
+ // here because SharedArrayBuffer is not available during snapshotting.
+ if (!sabByteLengthGetter) {
+ sabByteLengthGetter = Object.getOwnPropertyDescriptor(
+ SharedArrayBuffer.prototype,
+ "byteLength",
+ ).get;
+ }
+ try {
+ sabByteLengthGetter.call(V);
+ return true;
+ } catch {
+ return false;
+ }
+ }
+
+ function isArrayBufferDetached(V) {
+ try {
+ // eslint-disable-next-line no-new
+ new Uint8Array(V);
+ return false;
+ } catch {
+ return true;
+ }
+ }
+
+ converters.ArrayBuffer = (V, opts = {}) => {
+ if (!isNonSharedArrayBuffer(V)) {
+ if (opts.allowShared && !isSharedArrayBuffer(V)) {
+ throw makeException(
+ TypeError,
+ "is not an ArrayBuffer or SharedArrayBuffer",
+ opts,
+ );
+ }
+ throw makeException(TypeError, "is not an ArrayBuffer", opts);
+ }
+ if (isArrayBufferDetached(V)) {
+ throw makeException(TypeError, "is a detached ArrayBuffer", opts);
+ }
+
+ return V;
+ };
+
+ const dvByteLengthGetter = Object.getOwnPropertyDescriptor(
+ DataView.prototype,
+ "byteLength",
+ ).get;
+ converters.DataView = (V, opts = {}) => {
+ try {
+ dvByteLengthGetter.call(V);
+ } catch (e) {
+ throw makeException(TypeError, "is not a DataView", opts);
+ }
+
+ if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is backed by a SharedArrayBuffer, which is not allowed",
+ opts,
+ );
+ }
+ if (isArrayBufferDetached(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is backed by a detached ArrayBuffer",
+ opts,
+ );
+ }
+
+ return V;
+ };
+
+ // Returns the unforgeable `TypedArray` constructor name or `undefined`,
+ // if the `this` value isn't a valid `TypedArray` object.
+ //
+ // https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag
+ const typedArrayNameGetter = Object.getOwnPropertyDescriptor(
+ Object.getPrototypeOf(Uint8Array).prototype,
+ Symbol.toStringTag,
+ ).get;
+ [
+ Int8Array,
+ Int16Array,
+ Int32Array,
+ Uint8Array,
+ Uint16Array,
+ Uint32Array,
+ Uint8ClampedArray,
+ Float32Array,
+ Float64Array,
+ ].forEach((func) => {
+ const name = func.name;
+ const article = /^[AEIOU]/.test(name) ? "an" : "a";
+ converters[name] = (V, opts = {}) => {
+ if (!ArrayBuffer.isView(V) || typedArrayNameGetter.call(V) !== name) {
+ throw makeException(
+ TypeError,
+ `is not ${article} ${name} object`,
+ opts,
+ );
+ }
+ if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a SharedArrayBuffer, which is not allowed",
+ opts,
+ );
+ }
+ if (isArrayBufferDetached(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a detached ArrayBuffer",
+ opts,
+ );
+ }
+
+ return V;
+ };
+ });
+
+ // Common definitions
+
+ converters.ArrayBufferView = (V, opts = {}) => {
+ if (!ArrayBuffer.isView(V)) {
+ throw makeException(
+ TypeError,
+ "is not a view on an ArrayBuffer or SharedArrayBuffer",
+ opts,
+ );
+ }
+
+ if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a SharedArrayBuffer, which is not allowed",
+ opts,
+ );
+ }
+
+ if (isArrayBufferDetached(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a detached ArrayBuffer",
+ opts,
+ );
+ }
+ return V;
+ };
+
+ converters.BufferSource = (V, opts = {}) => {
+ if (ArrayBuffer.isView(V)) {
+ if (!opts.allowShared && isSharedArrayBuffer(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a SharedArrayBuffer, which is not allowed",
+ opts,
+ );
+ }
+
+ if (isArrayBufferDetached(V.buffer)) {
+ throw makeException(
+ TypeError,
+ "is a view on a detached ArrayBuffer",
+ opts,
+ );
+ }
+ return V;
+ }
+
+ if (!opts.allowShared && !isNonSharedArrayBuffer(V)) {
+ throw makeException(
+ TypeError,
+ "is not an ArrayBuffer or a view on one",
+ opts,
+ );
+ }
+ if (
+ opts.allowShared &&
+ !isSharedArrayBuffer(V) &&
+ !isNonSharedArrayBuffer(V)
+ ) {
+ throw makeException(
+ TypeError,
+ "is not an ArrayBuffer, SharedArrayBuffer, or a view on one",
+ opts,
+ );
+ }
+ if (isArrayBufferDetached(V)) {
+ throw makeException(TypeError, "is a detached ArrayBuffer", opts);
+ }
+
+ return V;
+ };
+
+ converters.DOMTimeStamp = converters["unsigned long long"];
+
+ converters.Function = convertCallbackFunction;
+
+ converters.VoidFunction = convertCallbackFunction;
+
+ function requiredArguments(length, required, opts = {}) {
+ if (length < required) {
+ const errMsg = `${
+ opts.prefix ? opts.prefix + ": " : ""
+ }${required} argument${
+ required === 1 ? "" : "s"
+ }, but only ${length} present.`;
+ throw new TypeError(errMsg);
+ }
+ }
+
+ function createDictionaryConverter(name, ...dictionaries) {
+ return function (V, opts = {}) {
+ const typeV = type(V);
+ switch (typeV) {
+ case "Undefined":
+ case "Null":
+ case "Object":
+ break;
+ default:
+ throw makeException(
+ TypeError,
+ "can not be converted to a dictionary",
+ opts,
+ );
+ }
+ const esDict = V;
+
+ const idlDict = {};
+
+ for (const members of dictionaries) {
+ for (const member of members) {
+ const key = member.key;
+
+ let esMemberValue;
+ if (typeV === "Undefined" || typeV === "Null") {
+ esMemberValue = undefined;
+ } else {
+ esMemberValue = esDict[key];
+ }
+
+ if (esMemberValue !== undefined) {
+ const converter = member.converter;
+ const idlMemberValue = converter(esMemberValue, {
+ ...opts,
+ context: `${key} of '${name}'${
+ opts.context ? `(${opts.context})` : ""
+ }`,
+ });
+ idlDict[key] = idlMemberValue;
+ } else if ("defaultValue" in member) {
+ const defaultValue = member.defaultValue;
+ const idlMemberValue = defaultValue;
+ idlDict[key] = idlMemberValue;
+ } else if (member.required) {
+ throw new TypeError(
+ `can not be converted to '${name}' because ${key} is required in '${name}'.`,
+ );
+ }
+ }
+ }
+
+ return idlDict;
+ };
+ }
+
+ window.__bootstrap = window.__bootstrap || {};
+ window.__bootstrap.webidl = {
+ converters,
+ requiredArguments,
+ createDictionaryConverter,
+ };
+})(this);
diff --git a/op_crates/web/00_dom_exception.js b/op_crates/web/01_dom_exception.js
similarity index 85%
rename from op_crates/web/00_dom_exception.js
rename to op_crates/web/01_dom_exception.js
index 22fd842fea..14f4ca8e93 100644
--- a/op_crates/web/00_dom_exception.js
+++ b/op_crates/web/01_dom_exception.js
@@ -1,7 +1,15 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+///
+///
+///
+
"use strict";
((window) => {
+ const webidl = window.__bootstrap.webidl;
+
const { defineProperty } = Object;
// Defined in WebIDL 4.3.
// https://heycam.github.io/webidl/#idl-DOMException
@@ -33,6 +41,7 @@
// Defined in WebIDL 2.8.1.
// https://heycam.github.io/webidl/#dfn-error-names-table
+ /** @type {Record} */
const nameToCodeMapping = {
IndexSizeError: INDEX_SIZE_ERR,
HierarchyRequestError: HIERARCHY_REQUEST_ERR,
@@ -67,9 +76,15 @@
constructor(message = "", name = "Error") {
super();
- this.#message = String(message);
- this.#name = name;
- this.#code = nameToCodeMapping[name] ?? 0;
+ this.#message = webidl.converters.DOMString(message, {
+ prefix: "Failed to construct 'DOMException'",
+ context: "Argument 1",
+ });
+ this.#name = webidl.converters.DOMString(name, {
+ prefix: "Failed to construct 'DOMException'",
+ context: "Argument 2",
+ });
+ this.#code = nameToCodeMapping[this.#name] ?? 0;
}
get message() {
diff --git a/op_crates/web/01_event.js b/op_crates/web/02_event.js
similarity index 82%
rename from op_crates/web/01_event.js
rename to op_crates/web/02_event.js
index 91ae2ef680..d253599e5e 100644
--- a/op_crates/web/01_event.js
+++ b/op_crates/web/02_event.js
@@ -7,95 +7,65 @@
"use strict";
((window) => {
- const eventData = new WeakMap();
-
- function requiredArguments(
- name,
- length,
- required,
- ) {
- if (length < required) {
- const errMsg = `${name} requires at least ${required} argument${
- required === 1 ? "" : "s"
- }, but only ${length} present`;
- throw new TypeError(errMsg);
- }
- }
+ const webidl = window.__bootstrap.webidl;
// accessors for non runtime visible data
function getDispatched(event) {
- return Boolean(eventData.get(event)?.dispatched);
+ return Boolean(event[_dispatched]);
}
function getPath(event) {
- return eventData.get(event)?.path ?? [];
+ return event[_path] ?? [];
}
function getStopImmediatePropagation(event) {
- return Boolean(eventData.get(event)?.stopImmediatePropagation);
+ return Boolean(event[_stopImmediatePropagationFlag]);
}
function setCurrentTarget(
event,
value,
) {
- event.currentTarget = value;
+ event[_attributes].currentTarget = value;
}
function setIsTrusted(event, value) {
- const data = eventData.get(event);
- if (data) {
- data.isTrusted = value;
- }
+ event[_isTrusted] = value;
}
function setDispatched(event, value) {
- const data = eventData.get(event);
- if (data) {
- data.dispatched = value;
- }
+ event[_dispatched] = value;
}
function setEventPhase(event, value) {
- event.eventPhase = value;
+ event[_attributes].eventPhase = value;
}
function setInPassiveListener(event, value) {
- const data = eventData.get(event);
- if (data) {
- data.inPassiveListener = value;
- }
+ event[_inPassiveListener] = value;
}
function setPath(event, value) {
- const data = eventData.get(event);
- if (data) {
- data.path = value;
- }
+ event[_path] = value;
}
function setRelatedTarget(
event,
value,
) {
- if ("relatedTarget" in event) {
- event.relatedTarget = value;
- }
+ event[_attributes].relatedTarget = value;
}
function setTarget(event, value) {
- event.target = value;
+ event[_attributes].target = value;
}
function setStopImmediatePropagation(
event,
value,
) {
- const data = eventData.get(event);
- if (data) {
- data.stopImmediatePropagation = value;
- }
+ event[_stopImmediatePropagationFlag] = value;
}
// Type guards that widen the event type
@@ -108,35 +78,65 @@
const isTrusted = Object.getOwnPropertyDescriptor({
get isTrusted() {
- return eventData.get(this).isTrusted;
+ return this[_isTrusted];
},
}, "isTrusted").get;
+ const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{
+ key: "bubbles",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ }, {
+ key: "cancelable",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ }, {
+ key: "composed",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ }]);
+
+ const _attributes = Symbol("[[attributes]]");
+ const _canceledFlag = Symbol("[[canceledFlag]]");
+ const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]");
+ const _stopImmediatePropagationFlag = Symbol(
+ "[[stopImmediatePropagationFlag]]",
+ );
+ const _inPassiveListener = Symbol("[[inPassiveListener]]");
+ const _dispatched = Symbol("[[dispatched]]");
+ const _isTrusted = Symbol("[[isTrusted]]");
+ const _path = Symbol("[[path]]");
+
class Event {
- #canceledFlag = false;
- #stopPropagationFlag = false;
- #attributes = {};
+ [_attributes] = {};
+ [_canceledFlag] = false;
+ [_stopPropagationFlag] = false;
+ [_stopImmediatePropagationFlag] = false;
+ [_inPassiveListener] = false;
+ [_dispatched] = false;
+ [_isTrusted] = false;
+ [_path] = [];
constructor(type, eventInitDict = {}) {
- requiredArguments("Event", arguments.length, 1);
- type = String(type);
- this.#attributes = {
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to construct 'Event'",
+ });
+ type = webidl.converters.DOMString(type, {
+ prefix: "Failed to construct 'Event'",
+ context: "Argument 1",
+ });
+ const eventInit = eventInitConverter(eventInitDict, {
+ prefix: "Failed to construct 'Event'",
+ context: "Argument 2",
+ });
+ this[_attributes] = {
type,
- bubbles: eventInitDict.bubbles ?? false,
- cancelable: eventInitDict.cancelable ?? false,
- composed: eventInitDict.composed ?? false,
+ ...eventInit,
currentTarget: null,
eventPhase: Event.NONE,
target: null,
timeStamp: Date.now(),
};
- eventData.set(this, {
- dispatched: false,
- inPassiveListener: false,
- isTrusted: false,
- path: [],
- stopImmediatePropagation: false,
- });
Reflect.defineProperty(this, "isTrusted", {
enumerable: true,
get: isTrusted,
@@ -147,95 +147,32 @@
return buildCustomInspectOutput(this, EVENT_PROPS, inspect);
}
- get bubbles() {
- return this.#attributes.bubbles;
- }
-
- get cancelBubble() {
- return this.#stopPropagationFlag;
- }
-
- set cancelBubble(value) {
- this.#stopPropagationFlag = value;
- }
-
- get cancelable() {
- return this.#attributes.cancelable;
- }
-
- get composed() {
- return this.#attributes.composed;
- }
-
- get currentTarget() {
- return this.#attributes.currentTarget;
- }
-
- set currentTarget(value) {
- this.#attributes = {
- type: this.type,
- bubbles: this.bubbles,
- cancelable: this.cancelable,
- composed: this.composed,
- currentTarget: value,
- eventPhase: this.eventPhase,
- target: this.target,
- timeStamp: this.timeStamp,
- };
- }
-
- get defaultPrevented() {
- return this.#canceledFlag;
- }
-
- get eventPhase() {
- return this.#attributes.eventPhase;
- }
-
- set eventPhase(value) {
- this.#attributes = {
- type: this.type,
- bubbles: this.bubbles,
- cancelable: this.cancelable,
- composed: this.composed,
- currentTarget: this.currentTarget,
- eventPhase: value,
- target: this.target,
- timeStamp: this.timeStamp,
- };
- }
-
- get initialized() {
- return true;
- }
-
- get target() {
- return this.#attributes.target;
- }
-
- set target(value) {
- this.#attributes = {
- type: this.type,
- bubbles: this.bubbles,
- cancelable: this.cancelable,
- composed: this.composed,
- currentTarget: this.currentTarget,
- eventPhase: this.eventPhase,
- target: value,
- timeStamp: this.timeStamp,
- };
- }
-
- get timeStamp() {
- return this.#attributes.timeStamp;
- }
-
get type() {
- return this.#attributes.type;
+ return this[_attributes].type;
+ }
+ set type(_) {
+ // this is a no-op because this member is readonly
+ }
+ get target() {
+ return this[_attributes].target;
+ }
+ set target(_) {
+ // this is a no-op because this member is readonly
+ }
+ get srcElement() {
+ return null;
+ }
+ set srcElement(_) {
+ // this is a no-op because this member is readonly
+ }
+ get currentTarget() {
+ return this[_attributes].currentTarget;
+ }
+ set currentTarget(_) {
+ // this is a no-op because this member is readonly
}
-
composedPath() {
- const path = eventData.get(this).path;
+ const path = this[_path];
if (path.length === 0) {
return [];
}
@@ -339,52 +276,123 @@
return composedPath.map((p) => p.item);
}
- preventDefault() {
- if (this.cancelable && !eventData.get(this).inPassiveListener) {
- this.#canceledFlag = true;
- }
- }
-
- stopPropagation() {
- this.#stopPropagationFlag = true;
- }
-
- stopImmediatePropagation() {
- this.#stopPropagationFlag = true;
- eventData.get(this).stopImmediatePropagation = true;
- }
-
get NONE() {
return Event.NONE;
}
-
+ set NONE(_) {
+ // this is a no-op because this member is readonly
+ }
get CAPTURING_PHASE() {
return Event.CAPTURING_PHASE;
}
-
+ set CAPTURING_PHASE(_) {
+ // this is a no-op because this member is readonly
+ }
get AT_TARGET() {
return Event.AT_TARGET;
}
-
+ set AT_TARGET(_) {
+ // this is a no-op because this member is readonly
+ }
get BUBBLING_PHASE() {
return Event.BUBBLING_PHASE;
}
-
+ set BUBBLING_PHASE(_) {
+ // this is a no-op because this member is readonly
+ }
static get NONE() {
return 0;
}
-
+ static set NONE(_) {
+ // this is a no-op because this member is readonly
+ }
static get CAPTURING_PHASE() {
return 1;
}
-
+ static set CAPTURING_PHASE(_) {
+ // this is a no-op because this member is readonly
+ }
static get AT_TARGET() {
return 2;
}
-
+ static set AT_TARGET(_) {
+ // this is a no-op because this member is readonly
+ }
static get BUBBLING_PHASE() {
return 3;
}
+ static set BUBBLING_PHASE(_) {
+ // this is a no-op because this member is readonly
+ }
+ get eventPhase() {
+ return this[_attributes].eventPhase;
+ }
+ set eventPhase(_) {
+ // this is a no-op because this member is readonly
+ }
+
+ stopPropagation() {
+ this[_stopPropagationFlag] = true;
+ }
+ get cancelBubble() {
+ return this[_stopPropagationFlag];
+ }
+ set cancelBubble(value) {
+ this[_stopPropagationFlag] = webidl.converters.boolean(value);
+ }
+ stopImmediatePropagation() {
+ this[_stopPropagationFlag] = true;
+ this[_stopImmediatePropagationFlag] = true;
+ }
+
+ get bubbles() {
+ return this[_attributes].bubbles;
+ }
+ set bubbles(_) {
+ // this is a no-op because this member is readonly
+ }
+ get cancelable() {
+ return this[_attributes].cancelable;
+ }
+ set cancelable(value) {
+ // this is a no-op because this member is readonly
+ }
+ get returnValue() {
+ return !this[_canceledFlag];
+ }
+ set returnValue(value) {
+ if (!webidl.converters.boolean(value)) {
+ this[_canceledFlag] = true;
+ }
+ }
+ preventDefault() {
+ if (this[_attributes].cancelable && !this[_inPassiveListener]) {
+ this[_canceledFlag] = true;
+ }
+ }
+ get defaultPrevented() {
+ return this[_canceledFlag];
+ }
+ set defaultPrevented(_) {
+ // this is a no-op because this member is readonly
+ }
+ get composed() {
+ return this[_attributes].composed;
+ }
+ set composed(_) {
+ // this is a no-op because this member is readonly
+ }
+
+ get initialized() {
+ return true;
+ }
+
+ get timeStamp() {
+ return this[_attributes].timeStamp;
+ }
+ set timeStamp(_) {
+ // this is a no-op because this member is readonly
+ }
}
function buildCustomInspectOutput(object, keys, inspect) {
@@ -408,7 +416,9 @@
"currentTarget",
"defaultPrevented",
"eventPhase",
+ "srcElement",
"target",
+ "returnValue",
"timeStamp",
"type",
];
@@ -875,7 +885,9 @@
callback,
options,
) {
- requiredArguments("EventTarget.addEventListener", arguments.length, 2);
+ webidl.requiredArguments(arguments.length, 2, {
+ prefix: "Failed to execute 'addEventListener' on 'EventTarget'",
+ });
if (callback === null) {
return;
}
@@ -919,7 +931,9 @@
callback,
options,
) {
- requiredArguments("EventTarget.removeEventListener", arguments.length, 2);
+ webidl.requiredArguments(arguments.length, 2, {
+ prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
+ });
const listeners = eventTargetData.get(this ?? globalThis).listeners;
if (callback !== null && type in listeners) {
@@ -948,7 +962,9 @@
}
dispatchEvent(event) {
- requiredArguments("EventTarget.dispatchEvent", arguments.length, 1);
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
+ });
const self = this ?? globalThis;
const listeners = eventTargetData.get(self).listeners;
@@ -1128,7 +1144,9 @@
constructor(type, eventInitDict = {}) {
super(type, eventInitDict);
- requiredArguments("CustomEvent", arguments.length, 1);
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to construct 'CustomEvent'",
+ });
const { detail } = eventInitDict;
this.#detail = detail;
}
diff --git a/op_crates/web/02_abort_signal.js b/op_crates/web/03_abort_signal.js
similarity index 100%
rename from op_crates/web/02_abort_signal.js
rename to op_crates/web/03_abort_signal.js
diff --git a/op_crates/web/03_global_interfaces.js b/op_crates/web/04_global_interfaces.js
similarity index 100%
rename from op_crates/web/03_global_interfaces.js
rename to op_crates/web/04_global_interfaces.js
diff --git a/op_crates/web/internal.d.ts b/op_crates/web/internal.d.ts
index 8f91601654..6f6849a7e7 100644
--- a/op_crates/web/internal.d.ts
+++ b/op_crates/web/internal.d.ts
@@ -1,10 +1,223 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+// deno-lint-ignore-file no-explicit-any ban-types
///
///
declare namespace globalThis {
declare namespace __bootstrap {
+ declare namespace webidl {
+ declare interface ConverterOpts {
+ /**
+ * The prefix for error messages created by this converter.
+ * Examples:
+ * - `Failed to construct 'Event'`
+ * - `Failed to execute 'removeEventListener' on 'EventTarget'`
+ */
+ prefix: string;
+ }
+ declare interface ValueConverterOpts extends ConverterOpts {
+ /**
+ * The context of this value error messages created by this converter.
+ * Examples:
+ * - `Argument 1`
+ * - `Argument 3`
+ */
+ context: string;
+ }
+ declare interface IntConverterOpts extends ValueConverterOpts {
+ /**
+ * Wether to throw if the number is outside of the acceptable values for
+ * this type.
+ */
+ enforceRange?: boolean;
+ /**
+ * Wether to clamp this number to the acceptable values for this type.
+ */
+ clamp?: boolean;
+ }
+ declare interface StringConverterOpts extends ValueConverterOpts {
+ /**
+ * Wether to treat `null` value as an empty string.
+ */
+ treatNullAsEmptyString?: boolean;
+ }
+ declare interface BufferConverterOpts extends ValueConverterOpts {
+ /**
+ * Wether to allow `SharedArrayBuffer` (not just `ArrayBuffer`).
+ */
+ allowShared?: boolean;
+ }
+ declare const converters: {
+ any(v: any): any;
+ /**
+ * Convert a value into a `boolean` (bool).
+ */
+ boolean(v: any, opts?: IntConverterOpts): boolean;
+ /**
+ * Convert a value into a `byte` (int8).
+ */
+ byte(v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `octet` (uint8).
+ */
+ octet(v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `short` (int16).
+ */
+ short(v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `unsigned short` (uint16).
+ */
+ ["unsigned short"](v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `long` (int32).
+ */
+ long(v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `unsigned long` (uint32).
+ */
+ ["unsigned long"](v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `long long` (int64).
+ * **Note this is truncated to a JS number (53 bit precision).**
+ */
+ ["long long"](v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `unsigned long long` (uint64).
+ * **Note this is truncated to a JS number (53 bit precision).**
+ */
+ ["unsigned long long"](v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `float` (f32).
+ */
+ float(v: any, opts?: ValueConverterOpts): number;
+ /**
+ * Convert a value into a `unrestricted float` (f32, infinity, or NaN).
+ */
+ ["unrestricted float"](v: any, opts?: ValueConverterOpts): number;
+ /**
+ * Convert a value into a `double` (f64).
+ */
+ double(v: any, opts?: ValueConverterOpts): number;
+ /**
+ * Convert a value into a `unrestricted double` (f64, infinity, or NaN).
+ */
+ ["unrestricted double"](v: any, opts?: ValueConverterOpts): number;
+ /**
+ * Convert a value into a `DOMString` (string).
+ */
+ DOMString(v: any, opts?: StringConverterOpts): string;
+ /**
+ * Convert a value into a `ByteString` (string with only u8 codepoints).
+ */
+ ByteString(v: any, opts?: StringConverterOpts): string;
+ /**
+ * Convert a value into a `USVString` (string with only valid non
+ * surrogate Unicode code points).
+ */
+ USVString(v: any, opts?: StringConverterOpts): string;
+ /**
+ * Convert a value into an `object` (object).
+ */
+ object(v: any, opts?: ValueConverterOpts): object;
+ /**
+ * Convert a value into an `ArrayBuffer` (ArrayBuffer).
+ */
+ ArrayBuffer(v: any, opts?: BufferConverterOpts): ArrayBuffer;
+ /**
+ * Convert a value into a `DataView` (ArrayBuffer).
+ */
+ DataView(v: any, opts?: BufferConverterOpts): DataView;
+ /**
+ * Convert a value into a `Int8Array` (Int8Array).
+ */
+ Int8Array(v: any, opts?: BufferConverterOpts): Int8Array;
+ /**
+ * Convert a value into a `Int16Array` (Int16Array).
+ */
+ Int16Array(v: any, opts?: BufferConverterOpts): Int16Array;
+ /**
+ * Convert a value into a `Int32Array` (Int32Array).
+ */
+ Int32Array(v: any, opts?: BufferConverterOpts): Int32Array;
+ /**
+ * Convert a value into a `Uint8Array` (Uint8Array).
+ */
+ Uint8Array(v: any, opts?: BufferConverterOpts): Uint8Array;
+ /**
+ * Convert a value into a `Uint16Array` (Uint16Array).
+ */
+ Uint16Array(v: any, opts?: BufferConverterOpts): Uint16Array;
+ /**
+ * Convert a value into a `Uint32Array` (Uint32Array).
+ */
+ Uint32Array(v: any, opts?: BufferConverterOpts): Uint32Array;
+ /**
+ * Convert a value into a `Uint8ClampedArray` (Uint8ClampedArray).
+ */
+ Uint8ClampedArray(
+ v: any,
+ opts?: BufferConverterOpts,
+ ): Uint8ClampedArray;
+ /**
+ * Convert a value into a `Float32Array` (Float32Array).
+ */
+ Float32Array(v: any, opts?: BufferConverterOpts): Float32Array;
+ /**
+ * Convert a value into a `Float64Array` (Float64Array).
+ */
+ Float64Array(v: any, opts?: BufferConverterOpts): Float64Array;
+ /**
+ * Convert a value into an `ArrayBufferView` (ArrayBufferView).
+ */
+ ArrayBufferView(v: any, opts?: BufferConverterOpts): ArrayBufferView;
+ /**
+ * Convert a value into a `BufferSource` (ArrayBuffer or ArrayBufferView).
+ */
+ BufferSource(
+ v: any,
+ opts?: BufferConverterOpts,
+ ): ArrayBuffer | ArrayBufferView;
+ /**
+ * Convert a value into a `DOMTimeStamp` (u64). Alias for unsigned long long
+ */
+ DOMTimeStamp(v: any, opts?: IntConverterOpts): number;
+ /**
+ * Convert a value into a `Function` ((...args: any[]) => any).
+ */
+ Function(v: any, opts?: ValueConverterOpts): (...args: any) => any;
+ /**
+ * Convert a value into a `VoidFunction` (() => void).
+ */
+ VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
+ };
+
+ /**
+ * Assert that the a function has at least a required amount of arguments.
+ */
+ declare function requiredArguments(
+ length: number,
+ required: number,
+ opts: ConverterOpts,
+ ): void;
+ declare type Dictionary = DictionaryMember[];
+ declare interface DictionaryMember {
+ key: string;
+ converter: (v: any, opts: ValueConverterOpts) => any;
+ defaultValue?: boolean;
+ required?: boolean;
+ }
+
+ /**ie
+ * Assert that the a function has at least a required amount of arguments.
+ */
+ declare function createDictionaryConverter(
+ name: string,
+ ...dictionaries: Dictionary[]
+ ): (v: any, opts: ValueConverterOpts) => T;
+ }
+
declare var url: {
URLSearchParams: typeof URLSearchParams;
};
diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs
index 209183d817..0008840920 100644
--- a/op_crates/web/lib.rs
+++ b/op_crates/web/lib.rs
@@ -41,20 +41,24 @@ pub fn op_domain_to_ascii(
pub fn init(isolate: &mut JsRuntime) {
let files = vec![
(
- "deno:op_crates/web/00_dom_exception.js",
- include_str!("00_dom_exception.js"),
+ "deno:op_crates/web/00_webidl.js",
+ include_str!("00_webidl.js"),
),
(
- "deno:op_crates/web/01_event.js",
- include_str!("01_event.js"),
+ "deno:op_crates/web/01_dom_exception.js",
+ include_str!("01_dom_exception.js"),
),
(
- "deno:op_crates/web/02_abort_signal.js",
- include_str!("02_abort_signal.js"),
+ "deno:op_crates/web/02_event.js",
+ include_str!("02_event.js"),
),
(
- "deno:op_crates/web/03_global_interfaces.js",
- include_str!("03_global_interfaces.js"),
+ "deno:op_crates/web/03_abort_signal.js",
+ include_str!("03_abort_signal.js"),
+ ),
+ (
+ "deno:op_crates/web/04_global_interfaces.js",
+ include_str!("04_global_interfaces.js"),
),
(
"deno:op_crates/web/08_text_encoding.js",
@@ -136,7 +140,7 @@ mod tests {
if let Err(error) = result {
let error_string = error.to_string();
// Test that the script specifier is a URL: `deno:`.
- assert!(error_string.contains("deno:op_crates/web/01_event.js"));
+ assert!(error_string.contains("deno:op_crates/web/02_event.js"));
assert!(error_string.contains("TypeError"));
} else {
unreachable!();
diff --git a/test_util/wpt b/test_util/wpt
index 81837c9a0f..182191819b 160000
--- a/test_util/wpt
+++ b/test_util/wpt
@@ -1 +1 @@
-Subproject commit 81837c9a0fb4a11a7bc3b062219c758b238f1b39
+Subproject commit 182191819bbfdc409bf5f29a5ea3bedb4545b91e
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index 510f71a52e..f719141756 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -22,7 +22,11 @@
"event-global-extra.window.js": true,
"event-global.worker.js": true,
"legacy-pre-activation-behavior.window.js": true,
- "relatedTarget.window.js": true
+ "relatedTarget.window.js": true,
+ "Event-constructors.any.js": [
+ "Untitled 2",
+ "Untitled 3"
+ ]
},
"idlharness.any.js": false,
"idlharness.window.js": false