diff --git a/cli/js/globals.ts b/cli/js/globals.ts
index 0aed3a2523..059a70ee77 100644
--- a/cli/js/globals.ts
+++ b/cli/js/globals.ts
@@ -1,10 +1,12 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import "./lib.deno.shared_globals.d.ts";
+
import * as blob from "./web/blob.ts";
import * as consoleTypes from "./web/console.ts";
import * as promiseTypes from "./web/promise.ts";
import * as customEvent from "./web/custom_event.ts";
-import * as domTypes from "./web/dom_types.ts";
+import * as domException from "./web/dom_exception.ts";
import * as domFile from "./web/dom_file.ts";
import * as event from "./web/event.ts";
import * as eventTarget from "./web/event_target.ts";
@@ -123,21 +125,13 @@ declare global {
// Only `var` variables show up in the `globalThis` type when doing a global
// scope augmentation.
/* eslint-disable no-var */
- var addEventListener: (
- type: string,
- callback: domTypes.EventListenerOrEventListenerObject | null,
- options?: boolean | domTypes.AddEventListenerOptions | undefined
- ) => void;
- var queueMicrotask: (callback: () => void) => void;
- var console: consoleTypes.Console;
- var location: domTypes.Location;
// Assigned to `window` global - main runtime
var Deno: {
core: DenoCore;
};
- var onload: ((e: domTypes.Event) => void) | undefined;
- var onunload: ((e: domTypes.Event) => void) | undefined;
+ var onload: ((e: Event) => void) | undefined;
+ var onunload: ((e: Event) => void) | undefined;
var bootstrapMainRuntime: (() => void) | undefined;
// Assigned to `self` global - worker runtime and compiler
@@ -150,7 +144,7 @@ declare global {
source: string,
lineno: number,
colno: number,
- e: domTypes.Event
+ e: Event
) => boolean | void)
| undefined;
@@ -163,9 +157,6 @@ declare global {
// Assigned to `self` global - compiler
var bootstrapTsCompilerRuntime: (() => void) | undefined;
var bootstrapWasmCompilerRuntime: (() => void) | undefined;
-
- var performance: performanceUtil.Performance;
- var setTimeout: typeof timers.setTimeout;
/* eslint-enable */
}
@@ -218,9 +209,10 @@ export const windowOrWorkerGlobalScopeProperties = {
console: writable(new consoleTypes.Console(core.print)),
Blob: nonEnumerable(blob.DenoBlob),
File: nonEnumerable(domFile.DomFileImpl),
- CustomEvent: nonEnumerable(customEvent.CustomEvent),
- Event: nonEnumerable(event.Event),
- EventTarget: nonEnumerable(eventTarget.EventTarget),
+ CustomEvent: nonEnumerable(customEvent.CustomEventImpl),
+ DOMException: nonEnumerable(domException.DOMExceptionImpl),
+ Event: nonEnumerable(event.EventImpl),
+ EventTarget: nonEnumerable(eventTarget.EventTargetImpl),
URL: nonEnumerable(url.URL),
URLSearchParams: nonEnumerable(urlSearchParams.URLSearchParams),
Headers: nonEnumerable(headers.Headers),
@@ -234,19 +226,17 @@ export const windowOrWorkerGlobalScopeProperties = {
Worker: nonEnumerable(workers.WorkerImpl),
};
-export const eventTargetProperties = {
- [domTypes.eventTargetHost]: nonEnumerable(null),
- [domTypes.eventTargetListeners]: nonEnumerable({}),
- [domTypes.eventTargetMode]: nonEnumerable(""),
- [domTypes.eventTargetNodeType]: nonEnumerable(0),
- [eventTarget.eventTargetAssignedSlot]: nonEnumerable(false),
- [eventTarget.eventTargetHasActivationBehavior]: nonEnumerable(false),
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function setEventTargetData(value: any): void {
+ eventTarget.eventTargetData.set(value, eventTarget.getDefaultTargetData());
+}
+export const eventTargetProperties = {
addEventListener: readOnly(
- eventTarget.EventTarget.prototype.addEventListener
+ eventTarget.EventTargetImpl.prototype.addEventListener
),
- dispatchEvent: readOnly(eventTarget.EventTarget.prototype.dispatchEvent),
+ dispatchEvent: readOnly(eventTarget.EventTargetImpl.prototype.dispatchEvent),
removeEventListener: readOnly(
- eventTarget.EventTarget.prototype.removeEventListener
+ eventTarget.EventTargetImpl.prototype.removeEventListener
),
};
diff --git a/cli/js/lib.deno.shared_globals.d.ts b/cli/js/lib.deno.shared_globals.d.ts
index 2027686a90..a1e834cc42 100644
--- a/cli/js/lib.deno.shared_globals.d.ts
+++ b/cli/js/lib.deno.shared_globals.d.ts
@@ -1,12 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, no-var */
///
-// TODO: we need to remove this, but Fetch::Response::Body implements Reader
-// which requires Deno.EOF, and we shouldn't be leaking that, but https_proxy
-// at the least requires the Reader interface on Body, which it shouldn't
-///
///
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
@@ -184,6 +180,7 @@ declare function setTimeout(
delay?: number,
...args: unknown[]
): number;
+
/** Repeatedly calls a function , with a fixed time delay between each call. */
declare function setInterval(
cb: (...args: unknown[]) => void,
@@ -194,8 +191,8 @@ declare function clearTimeout(id?: number): void;
declare function clearInterval(id?: number): void;
declare function queueMicrotask(func: Function): void;
-declare const console: Console;
-declare const location: Location;
+declare var console: Console;
+declare var location: Location;
declare function addEventListener(
type: string,
@@ -315,6 +312,12 @@ interface DOMStringList {
[index: number]: string;
}
+declare class DOMException extends Error {
+ constructor(message?: string, name?: string);
+ readonly name: string;
+ readonly message: string;
+}
+
/** The location (URL) of the object it is linked to. Changes done on it are
* reflected on the object it relates to. Both the Document and Window
* interface have such a linked Location, accessible via Document.location and
@@ -1060,122 +1063,81 @@ declare namespace performance {
export function now(): number;
}
-/** An event which takes place in the DOM. */
-interface Event {
- /**
- * Returns true or false depending on how event was initialized. True if
- * event goes through its target's ancestors in reverse tree order, and
- * false otherwise.
- */
- readonly bubbles: boolean;
-
- // TODO(ry) Remove cancelBubbleImmediately - non-standard extension.
- cancelBubbleImmediately: boolean;
-
- cancelBubble: boolean;
- /**
- * Returns true or false depending on how event was initialized. Its return
- * value does not always carry meaning, but true can indicate that part of
- * the operation during which event was dispatched, can be canceled by
- * invoking the preventDefault() method.
- */
- readonly cancelable: boolean;
- /**
- * Returns true or false depending on how event was initialized. True if
- * event invokes listeners past a ShadowRoot node that is the root of its
- * target, and false otherwise.
- */
- readonly composed: boolean;
- /**
- * Returns the object whose event listener's callback is currently being
- * invoked.
- */
- readonly currentTarget: EventTarget | null;
- /**
- * Returns true if preventDefault() was invoked successfully to indicate
- * cancelation, and false otherwise.
- */
- readonly defaultPrevented: boolean;
- /**
- * Returns the event's phase, which is one of NONE, CAPTURING_PHASE,
- * AT_TARGET, and BUBBLING_PHASE.
- */
- readonly eventPhase: number;
- /**
- * Returns true if event was dispatched by the user agent, and false
- * otherwise.
- */
- readonly isTrusted: boolean;
- returnValue: boolean;
- /** @deprecated */
- readonly srcElement: EventTarget | null;
- /**
- * Returns the object to which event is dispatched (its target).
- */
- readonly target: EventTarget | null;
- /**
- * Returns the event's timestamp as the number of milliseconds measured
- * relative to the time origin.
- */
- readonly timeStamp: number;
- /**
- * Returns the type of event, e.g. "click", "hashchange", or "submit".
- */
- readonly type: string;
- /**
- * Returns the invocation target objects of event's path (objects on which
- * listeners will be invoked), except for any nodes in shadow trees of which
- * the shadow root's mode is "closed" that are not reachable from event's
- * currentTarget.
- */
- composedPath(): EventTarget[];
- initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
- /**
- * If invoked when the cancelable attribute value is true, and while
- * executing a listener for the event with passive set to false, signals to
- * the operation that caused event to be dispatched that it needs to be
- * canceled.
- */
- preventDefault(): void;
- /**
- * Invoking this method prevents event from reaching any registered event
- * listeners after the current one finishes running and, when dispatched in
- * a tree, also prevents event from reaching any other objects.
- */
- stopImmediatePropagation(): void;
- /**
- * When dispatched in a tree, invoking this method prevents event from
- * reaching any objects other than the current object.
- */
- stopPropagation(): void;
- readonly AT_TARGET: number;
- readonly BUBBLING_PHASE: number;
- readonly CAPTURING_PHASE: number;
- readonly NONE: number;
-}
-
interface EventInit {
bubbles?: boolean;
cancelable?: boolean;
composed?: boolean;
}
-declare const Event: {
- prototype: Event;
- new (type: string, eventInitDict?: EventInit): Event;
+/** An event which takes place in the DOM. */
+declare class Event {
+ constructor(type: string, eventInitDict?: EventInit);
+ /** Returns true or false depending on how event was initialized. True if
+ * event goes through its target's ancestors in reverse tree order, and
+ * false otherwise. */
+ readonly bubbles: boolean;
+ cancelBubble: boolean;
+ /** Returns true or false depending on how event was initialized. Its return
+ * value does not always carry meaning, but true can indicate that part of the
+ * operation during which event was dispatched, can be canceled by invoking
+ * the preventDefault() method. */
+ readonly cancelable: boolean;
+ /** Returns true or false depending on how event was initialized. True if
+ * event invokes listeners past a ShadowRoot node that is the root of its
+ * target, and false otherwise. */
+ readonly composed: boolean;
+ /** Returns the object whose event listener's callback is currently being
+ * invoked. */
+ readonly currentTarget: EventTarget | null;
+ /** Returns true if preventDefault() was invoked successfully to indicate
+ * cancellation, and false otherwise. */
+ readonly defaultPrevented: boolean;
+ /** Returns the event's phase, which is one of NONE, CAPTURING_PHASE,
+ * AT_TARGET, and BUBBLING_PHASE. */
+ readonly eventPhase: number;
+ /** Returns true if event was dispatched by the user agent, and false
+ * otherwise. */
+ readonly isTrusted: boolean;
+ /** Returns the object to which event is dispatched (its target). */
+ readonly target: EventTarget | null;
+ /** Returns the event's timestamp as the number of milliseconds measured
+ * relative to the time origin. */
+ readonly timeStamp: number;
+ /** Returns the type of event, e.g. "click", "hashchange", or "submit". */
+ readonly type: string;
+ /** Returns the invocation target objects of event's path (objects on which
+ * listeners will be invoked), except for any nodes in shadow trees of which
+ * the shadow root's mode is "closed" that are not reachable from event's
+ * currentTarget. */
+ composedPath(): EventTarget[];
+ /** If invoked when the cancelable attribute value is true, and while
+ * executing a listener for the event with passive set to false, signals to
+ * the operation that caused event to be dispatched that it needs to be
+ * canceled. */
+ preventDefault(): void;
+ /** Invoking this method prevents event from reaching any registered event
+ * listeners after the current one finishes running and, when dispatched in a
+ * tree, also prevents event from reaching any other objects. */
+ stopImmediatePropagation(): void;
+ /** When dispatched in a tree, invoking this method prevents event from
+ * reaching any objects other than the current object. */
+ stopPropagation(): void;
readonly AT_TARGET: number;
readonly BUBBLING_PHASE: number;
readonly CAPTURING_PHASE: number;
readonly NONE: number;
-};
+ static readonly AT_TARGET: number;
+ static readonly BUBBLING_PHASE: number;
+ static readonly CAPTURING_PHASE: number;
+ static readonly NONE: number;
+}
/**
* EventTarget is a DOM interface implemented by objects that can receive events
* and may have listeners for them.
*/
-interface EventTarget {
- /**
- * Appends an event listener for events whose type attribute value is type.
+declare class EventTarget {
+ /** Appends an event listener for events whose type attribute value is type.
* The callback argument sets the callback that will be invoked when the event
* is dispatched.
*
@@ -1197,41 +1159,32 @@ interface EventTarget {
* invoked once after which the event listener will be removed.
*
* The event listener is appended to target's event listener list and is not
- * appended if it has the same type, callback, and capture.
- */
+ * appended if it has the same type, callback, and capture. */
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions
): void;
- /**
- * Dispatches a synthetic event event to target and returns true if either
+ /** Dispatches a synthetic event event to target and returns true if either
* event's cancelable attribute value is false or its preventDefault() method
- * was not invoked, and false otherwise.
- */
+ * was not invoked, and false otherwise. */
dispatchEvent(event: Event): boolean;
- /**
- * Removes the event listener in target's event listener list with the same
- * type, callback, and options.
- */
+ /** Removes the event listener in target's event listener list with the same
+ * type, callback, and options. */
removeEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void;
+ [Symbol.toStringTag]: string;
}
-declare const EventTarget: {
- prototype: EventTarget;
- new (): EventTarget;
-};
-
interface EventListener {
- (evt: Event): void;
+ (evt: Event): void | Promise;
}
interface EventListenerObject {
- handleEvent(evt: Event): void;
+ handleEvent(evt: Event): void | Promise;
}
declare type EventListenerOrEventListenerObject =
@@ -1257,27 +1210,16 @@ interface ProgressEvent extends Event {
readonly total: number;
}
-interface CustomEvent extends Event {
- /**
- * Returns any custom data event was created with. Typically used for synthetic events.
- */
- readonly detail: T;
- initCustomEvent(
- typeArg: string,
- canBubbleArg: boolean,
- cancelableArg: boolean,
- detailArg: T
- ): void;
-}
-
interface CustomEventInit extends EventInit {
detail?: T;
}
-declare const CustomEvent: {
- prototype: CustomEvent;
- new (typeArg: string, eventInitDict?: CustomEventInit): CustomEvent;
-};
+declare class CustomEvent extends Event {
+ constructor(typeArg: string, eventInitDict?: CustomEventInit);
+ /** Returns any custom data event was created with. Typically used for
+ * synthetic events. */
+ readonly detail: T;
+}
interface AbortSignalEventMap {
abort: Event;
diff --git a/cli/js/lib.deno.window.d.ts b/cli/js/lib.deno.window.d.ts
index 2b8e6f50fc..6377057d31 100644
--- a/cli/js/lib.deno.window.d.ts
+++ b/cli/js/lib.deno.window.d.ts
@@ -1,28 +1,28 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-explicit-any */
///
///
///
///
-declare interface Window {
- window: Window & typeof globalThis;
- self: Window & typeof globalThis;
- onload: Function | undefined;
- onunload: Function | undefined;
+declare interface Window extends EventTarget {
+ readonly window: Window & typeof globalThis;
+ readonly self: Window & typeof globalThis;
+ onload: ((this: Window, ev: Event) => any) | null;
+ onunload: ((this: Window, ev: Event) => any) | null;
location: Location;
crypto: Crypto;
close: () => void;
- closed: boolean;
+ readonly closed: boolean;
Deno: typeof Deno;
}
declare const window: Window & typeof globalThis;
declare const self: Window & typeof globalThis;
-declare const onload: Function | undefined;
-declare const onunload: Function | undefined;
+declare const onload: ((this: Window, ev: Event) => any) | null;
+declare const onunload: ((this: Window, ev: Event) => any) | null;
declare const crypto: Crypto;
declare interface Crypto {
@@ -45,4 +45,4 @@ declare interface Crypto {
): T;
}
-/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */
+/* eslint-enable @typescript-eslint/no-explicit-any */
diff --git a/cli/js/runtime_main.ts b/cli/js/runtime_main.ts
index edb02d2d65..0b0b1f75f4 100644
--- a/cli/js/runtime_main.ts
+++ b/cli/js/runtime_main.ts
@@ -8,7 +8,6 @@
// It sets up runtime by providing globals for `WindowScope` and adds `Deno` global.
import * as Deno from "./deno.ts";
-import * as domTypes from "./web/dom_types.ts";
import * as csprng from "./ops/get_random_values.ts";
import { exit } from "./ops/os.ts";
import {
@@ -18,6 +17,7 @@ import {
windowOrWorkerGlobalScopeMethods,
windowOrWorkerGlobalScopeProperties,
eventTargetProperties,
+ setEventTargetData,
} from "./globals.ts";
import { internalObject } from "./internals.ts";
import { setSignals } from "./signals.ts";
@@ -59,9 +59,9 @@ export const mainRuntimeGlobalProperties = {
self: readOnly(globalThis),
crypto: readOnly(csprng),
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
- // it seems those two properties should be availble to workers as well
- onload: writable(undefined),
- onunload: writable(undefined),
+ // it seems those two properties should be available to workers as well
+ onload: writable(null),
+ onunload: writable(null),
close: writable(windowClose),
closed: getterOnly(() => windowIsClosing),
};
@@ -78,15 +78,16 @@ export function bootstrapMainRuntime(): void {
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
Object.defineProperties(globalThis, eventTargetProperties);
Object.defineProperties(globalThis, mainRuntimeGlobalProperties);
+ setEventTargetData(globalThis);
// Registers the handler for window.onload function.
- globalThis.addEventListener("load", (e: domTypes.Event): void => {
+ globalThis.addEventListener("load", (e) => {
const { onload } = globalThis;
if (typeof onload === "function") {
onload(e);
}
});
// Registers the handler for window.onunload function.
- globalThis.addEventListener("unload", (e: domTypes.Event): void => {
+ globalThis.addEventListener("unload", (e) => {
const { onunload } = globalThis;
if (typeof onunload === "function") {
onunload(e);
diff --git a/cli/js/tests/dom_exception_test.ts b/cli/js/tests/dom_exception_test.ts
new file mode 100644
index 0000000000..2eb7633e10
--- /dev/null
+++ b/cli/js/tests/dom_exception_test.ts
@@ -0,0 +1,9 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+import { unitTest, assertEquals, assert } from "./test_util.ts";
+
+unitTest(function testDomError() {
+ const de = new DOMException("foo", "bar");
+ assert(de);
+ assertEquals(de.message, "foo");
+ assertEquals(de.name, "bar");
+});
diff --git a/cli/js/tests/event_target_test.ts b/cli/js/tests/event_target_test.ts
index ece4b5dba5..0c4eb4d0dd 100644
--- a/cli/js/tests/event_target_test.ts
+++ b/cli/js/tests/event_target_test.ts
@@ -35,18 +35,6 @@ unitTest(function constructedEventTargetCanBeUsedAsExpected(): void {
assertEquals(callCount, 2);
});
-// TODO(ry) Should AddEventListenerOptions and EventListenerOptions be exposed
-// from the public API?
-
-interface AddEventListenerOptions extends EventListenerOptions {
- once?: boolean;
- passive?: boolean;
-}
-
-interface EventListenerOptions {
- capture?: boolean;
-}
-
unitTest(function anEventTargetCanBeSubclassed(): void {
class NicerEventTarget extends EventTarget {
on(
diff --git a/cli/js/tests/event_test.ts b/cli/js/tests/event_test.ts
index 05a9ed5770..ce3076e58a 100644
--- a/cli/js/tests/event_test.ts
+++ b/cli/js/tests/event_test.ts
@@ -48,10 +48,8 @@ unitTest(function eventStopImmediatePropagationSuccess(): void {
const event = new Event(type);
assertEquals(event.cancelBubble, false);
- assertEquals(event.cancelBubbleImmediately, false);
event.stopImmediatePropagation();
assertEquals(event.cancelBubble, true);
- assertEquals(event.cancelBubbleImmediately, true);
});
unitTest(function eventPreventDefaultSuccess(): void {
diff --git a/cli/js/tests/unit_tests.ts b/cli/js/tests/unit_tests.ts
index 4cff3d1d87..ba3d6746a8 100644
--- a/cli/js/tests/unit_tests.ts
+++ b/cli/js/tests/unit_tests.ts
@@ -16,6 +16,7 @@ import "./custom_event_test.ts";
import "./dir_test.ts";
import "./dispatch_minimal_test.ts";
import "./dispatch_json_test.ts";
+import "./dom_exception_test.ts";
import "./error_stack_test.ts";
import "./event_test.ts";
import "./event_target_test.ts";
diff --git a/cli/js/web/blob.ts b/cli/js/web/blob.ts
index 7bdde8e28e..90480c89c0 100644
--- a/cli/js/web/blob.ts
+++ b/cli/js/web/blob.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { TextDecoder, TextEncoder } from "./text_encoding.ts";
import { build } from "../build.ts";
import { ReadableStream } from "./streams/mod.ts";
diff --git a/cli/js/web/body.ts b/cli/js/web/body.ts
index a16f872b95..2f6987592d 100644
--- a/cli/js/web/body.ts
+++ b/cli/js/web/body.ts
@@ -2,7 +2,7 @@ import * as formData from "./form_data.ts";
import * as blob from "./blob.ts";
import * as encoding from "./text_encoding.ts";
import * as headers from "./headers.ts";
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { ReadableStream } from "./streams/mod.ts";
const { Headers } = headers;
diff --git a/cli/js/web/custom_event.ts b/cli/js/web/custom_event.ts
index 418b7ea34f..ea76d2c94e 100644
--- a/cli/js/web/custom_event.ts
+++ b/cli/js/web/custom_event.ts
@@ -1,44 +1,28 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
-import * as event from "./event.ts";
+import { EventImpl as Event } from "./event.ts";
import { requiredArguments } from "./util.ts";
-export class CustomEvent extends event.Event implements domTypes.CustomEvent {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- #detail: any;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export class CustomEventImpl extends Event implements CustomEvent {
+ #detail: T;
- constructor(
- type: string,
- customEventInitDict: domTypes.CustomEventInit = {}
- ) {
- super(type, customEventInitDict);
+ constructor(type: string, eventInitDict: CustomEventInit = {}) {
+ super(type, eventInitDict);
requiredArguments("CustomEvent", arguments.length, 1);
- const { detail = null } = customEventInitDict;
- this.#detail = detail;
+ const { detail } = eventInitDict;
+ this.#detail = detail as T;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
- get detail(): any {
+ get detail(): T {
return this.#detail;
}
- initCustomEvent(
- _type: string,
- _bubbles?: boolean,
- _cancelable?: boolean,
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- detail?: any
- ): void {
- if (this.dispatched) {
- return;
- }
-
- this.#detail = detail;
- }
-
get [Symbol.toStringTag](): string {
return "CustomEvent";
}
}
-Reflect.defineProperty(CustomEvent.prototype, "detail", { enumerable: true });
+Reflect.defineProperty(CustomEventImpl.prototype, "detail", {
+ enumerable: true,
+});
diff --git a/cli/js/web/dom_exception.ts b/cli/js/web/dom_exception.ts
new file mode 100644
index 0000000000..e2c77d41ca
--- /dev/null
+++ b/cli/js/web/dom_exception.ts
@@ -0,0 +1,14 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+export class DOMExceptionImpl extends Error implements DOMException {
+ #name: string;
+
+ constructor(message = "", name = "Error") {
+ super(message);
+ this.#name = name;
+ }
+
+ get name(): string {
+ return this.#name;
+ }
+}
diff --git a/cli/js/web/dom_file.ts b/cli/js/web/dom_file.ts
index cf2a40398e..a3b43dad1f 100644
--- a/cli/js/web/dom_file.ts
+++ b/cli/js/web/dom_file.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import * as blob from "./blob.ts";
export class DomFileImpl extends blob.DenoBlob implements domTypes.DomFile {
diff --git a/cli/js/web/dom_iterable.ts b/cli/js/web/dom_iterable.ts
index 191958f111..fcbca307fc 100644
--- a/cli/js/web/dom_iterable.ts
+++ b/cli/js/web/dom_iterable.ts
@@ -1,11 +1,21 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-import { DomIterable } from "./dom_types.ts";
import { requiredArguments } from "./util.ts";
import { exposeForTest } from "../internals.ts";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-type Constructor = new (...args: any[]) => T;
+export type Constructor = new (...args: any[]) => T;
+
+export interface DomIterable {
+ keys(): IterableIterator;
+ values(): IterableIterator;
+ entries(): IterableIterator<[K, V]>;
+ [Symbol.iterator](): IterableIterator<[K, V]>;
+ forEach(
+ callback: (value: V, key: K, parent: this) => void,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ thisArg?: any
+ ): void;
+}
export function DomIterableMixin(
Base: TBase,
diff --git a/cli/js/web/dom_types.ts b/cli/js/web/dom_types.d.ts
similarity index 63%
rename from cli/js/web/dom_types.ts
rename to cli/js/web/dom_types.d.ts
index 94e26846fa..3ac025934c 100644
--- a/cli/js/web/dom_types.ts
+++ b/cli/js/web/dom_types.d.ts
@@ -23,7 +23,7 @@ export type HeadersInit =
| Headers
| Array<[string, string]>
| Record;
-export type URLSearchParamsInit = string | string[][] | Record;
+
type BodyInit =
| Blob
| BufferSource
@@ -31,7 +31,9 @@ type BodyInit =
| URLSearchParams
| ReadableStream
| string;
+
export type RequestInfo = Request | string;
+
type ReferrerPolicy =
| ""
| "no-referrer"
@@ -39,21 +41,12 @@ type ReferrerPolicy =
| "origin-only"
| "origin-when-cross-origin"
| "unsafe-url";
+
export type BlobPart = BufferSource | Blob | string;
+
export type FormDataEntryValue = DomFile | string;
-export interface DomIterable {
- keys(): IterableIterator;
- values(): IterableIterator;
- entries(): IterableIterator<[K, V]>;
- [Symbol.iterator](): IterableIterator<[K, V]>;
- forEach(
- callback: (value: V, key: K, parent: this) => void,
- thisArg?: any
- ): void;
-}
-
-type EndingType = "transparent" | "native";
+export type EndingType = "transparent" | "native";
export interface BlobPropertyBag {
type?: string;
@@ -64,67 +57,16 @@ interface AbortSignalEventMap {
abort: ProgressEvent;
}
-// https://dom.spec.whatwg.org/#node
-export enum NodeType {
- ELEMENT_NODE = 1,
- TEXT_NODE = 3,
- DOCUMENT_FRAGMENT_NODE = 11,
-}
-
-export const eventTargetHost: unique symbol = Symbol();
-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
- (evt: Event): void | Promise;
-}
-
-export interface EventListenerObject {
- // Different from lib.dom.d.ts. Added Promise
- handleEvent(evt: Event): void | Promise;
-}
-
-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]: EventTargetListener[] };
- [eventTargetMode]: string;
- [eventTargetNodeType]: NodeType;
- addEventListener(
- type: string,
- listener: EventListenerOrEventListenerObject | null,
- options?: boolean | AddEventListenerOptions
- ): void;
- dispatchEvent(event: Event): boolean;
- removeEventListener(
- type: string,
- listener: EventListenerOrEventListenerObject | null,
- options?: EventListenerOptions | boolean
- ): void;
-}
-
export interface ProgressEventInit extends EventInit {
lengthComputable?: boolean;
loaded?: number;
total?: number;
}
-export interface URLSearchParams extends DomIterable {
+export class URLSearchParams {
+ constructor(
+ init?: string[][] | Record | string | URLSearchParams
+ );
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
@@ -137,72 +79,229 @@ export interface URLSearchParams extends DomIterable {
callbackfn: (value: string, key: string, parent: this) => void,
thisArg?: any
): void;
+ [Symbol.iterator](): IterableIterator<[string, string]>;
+ entries(): IterableIterator<[string, string]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
+ static toString(): string;
}
-export interface EventInit {
- bubbles?: boolean;
- cancelable?: boolean;
+export interface UIEventInit extends EventInit {
+ detail?: number;
+ // adjust Window -> Node
+ view?: Node | null;
+}
+
+export class UIEvent extends Event {
+ constructor(type: string, eventInitDict?: UIEventInit);
+ readonly detail: number;
+ // adjust Window -> Node
+ readonly view: Node | null;
+}
+
+export interface FocusEventInit extends UIEventInit {
+ relatedTarget?: EventTarget | null;
+}
+
+export class FocusEvent extends UIEvent {
+ constructor(type: string, eventInitDict?: FocusEventInit);
+ readonly relatedTarget: EventTarget | null;
+}
+
+export interface EventModifierInit extends UIEventInit {
+ altKey?: boolean;
+ ctrlKey?: boolean;
+ metaKey?: boolean;
+ modifierAltGraph?: boolean;
+ modifierCapsLock?: boolean;
+ modifierFn?: boolean;
+ modifierFnLock?: boolean;
+ modifierHyper?: boolean;
+ modifierNumLock?: boolean;
+ modifierScrollLock?: boolean;
+ modifierSuper?: boolean;
+ modifierSymbol?: boolean;
+ modifierSymbolLock?: boolean;
+ shiftKey?: boolean;
+}
+
+export interface MouseEventInit extends EventModifierInit {
+ button?: number;
+ buttons?: number;
+ clientX?: number;
+ clientY?: number;
+ movementX?: number;
+ movementY?: number;
+ relatedTarget?: EventTarget | null;
+ screenX?: number;
+ screenY?: number;
+}
+
+export class MouseEvent extends UIEvent {
+ constructor(type: string, eventInitDict?: MouseEventInit);
+ readonly altKey: boolean;
+ readonly button: number;
+ readonly buttons: number;
+ readonly clientX: number;
+ readonly clientY: number;
+ readonly ctrlKey: boolean;
+ readonly metaKey: boolean;
+ readonly movementX: number;
+ readonly movementY: number;
+ readonly offsetX: number;
+ readonly offsetY: number;
+ readonly pageX: number;
+ readonly pageY: number;
+ readonly relatedTarget: EventTarget | null;
+ readonly screenX: number;
+ readonly screenY: number;
+ readonly shiftKey: boolean;
+ readonly x: number;
+ readonly y: number;
+ getModifierState(keyArg: string): boolean;
+}
+
+interface GetRootNodeOptions {
composed?: boolean;
}
-export interface CustomEventInit extends EventInit {
- detail?: any;
+export class Node extends EventTarget {
+ readonly baseURI: string;
+ readonly childNodes: NodeListOf;
+ readonly firstChild: ChildNode | null;
+ readonly isConnected: boolean;
+ readonly lastChild: ChildNode | null;
+ readonly nextSibling: ChildNode | null;
+ readonly nodeName: string;
+ readonly nodeType: number;
+ nodeValue: string | null;
+ // adjusted: Document -> Node
+ readonly ownerDocument: Node | null;
+ // adjusted: HTMLElement -> Node
+ readonly parentElement: Node | null;
+ readonly parentNode: (Node & ParentNode) | null;
+ readonly previousSibling: ChildNode | null;
+ textContent: string | null;
+ appendChild(newChild: T): T;
+ cloneNode(deep?: boolean): Node;
+ compareDocumentPosition(other: Node): number;
+ contains(other: Node | null): boolean;
+ getRootNode(options?: GetRootNodeOptions): Node;
+ hasChildNodes(): boolean;
+ insertBefore(newChild: T, refChild: Node | null): T;
+ isDefaultNamespace(namespace: string | null): boolean;
+ isEqualNode(otherNode: Node | null): boolean;
+ isSameNode(otherNode: Node | null): boolean;
+ lookupNamespaceURI(prefix: string | null): string | null;
+ lookupPrefix(namespace: string | null): string | null;
+ normalize(): void;
+ removeChild(oldChild: T): T;
+ replaceChild(newChild: Node, oldChild: T): T;
+ readonly ATTRIBUTE_NODE: number;
+ readonly CDATA_SECTION_NODE: number;
+ readonly COMMENT_NODE: number;
+ readonly DOCUMENT_FRAGMENT_NODE: number;
+ readonly DOCUMENT_NODE: number;
+ readonly DOCUMENT_POSITION_CONTAINED_BY: number;
+ readonly DOCUMENT_POSITION_CONTAINS: number;
+ readonly DOCUMENT_POSITION_DISCONNECTED: number;
+ readonly DOCUMENT_POSITION_FOLLOWING: number;
+ readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
+ readonly DOCUMENT_POSITION_PRECEDING: number;
+ readonly DOCUMENT_TYPE_NODE: number;
+ readonly ELEMENT_NODE: number;
+ readonly ENTITY_NODE: number;
+ readonly ENTITY_REFERENCE_NODE: number;
+ readonly NOTATION_NODE: number;
+ readonly PROCESSING_INSTRUCTION_NODE: number;
+ readonly TEXT_NODE: number;
+ static readonly ATTRIBUTE_NODE: number;
+ static readonly CDATA_SECTION_NODE: number;
+ static readonly COMMENT_NODE: number;
+ static readonly DOCUMENT_FRAGMENT_NODE: number;
+ static readonly DOCUMENT_NODE: number;
+ static readonly DOCUMENT_POSITION_CONTAINED_BY: number;
+ static readonly DOCUMENT_POSITION_CONTAINS: number;
+ static readonly DOCUMENT_POSITION_DISCONNECTED: number;
+ static readonly DOCUMENT_POSITION_FOLLOWING: number;
+ static readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
+ static readonly DOCUMENT_POSITION_PRECEDING: number;
+ static readonly DOCUMENT_TYPE_NODE: number;
+ static readonly ELEMENT_NODE: number;
+ static readonly ENTITY_NODE: number;
+ static readonly ENTITY_REFERENCE_NODE: number;
+ static readonly NOTATION_NODE: number;
+ static readonly PROCESSING_INSTRUCTION_NODE: number;
+ static readonly TEXT_NODE: number;
}
-export enum EventPhase {
- NONE = 0,
- CAPTURING_PHASE = 1,
- AT_TARGET = 2,
- BUBBLING_PHASE = 3,
+interface Slotable {
+ // adjusted: HTMLSlotElement -> Node
+ readonly assignedSlot: Node | null;
}
-export interface EventPath {
- item: EventTarget;
- itemInShadowTree: boolean;
- relatedTarget: EventTarget | null;
- rootOfClosedTree: boolean;
- slotInClosedTree: boolean;
- target: EventTarget | null;
- touchTargetList: EventTarget[];
+interface ChildNode extends Node {
+ after(...nodes: Array): void;
+ before(...nodes: Array): void;
+ remove(): void;
+ replaceWith(...nodes: Array): void;
}
-export interface Event {
- readonly type: string;
- target: EventTarget | null;
- currentTarget: EventTarget | null;
- composedPath(): EventPath[];
-
- eventPhase: number;
-
- stopPropagation(): void;
- stopImmediatePropagation(): void;
-
- readonly bubbles: boolean;
- readonly cancelable: boolean;
- preventDefault(): void;
- readonly defaultPrevented: boolean;
- readonly composed: boolean;
-
- isTrusted: boolean;
- readonly timeStamp: Date;
-
- dispatched: boolean;
- readonly initialized: boolean;
- inPassiveListener: boolean;
- cancelBubble: boolean;
- cancelBubbleImmediately: boolean;
- path: EventPath[];
- relatedTarget: EventTarget | null;
+interface ParentNode {
+ readonly childElementCount: number;
+ // not currently supported
+ // readonly children: HTMLCollection;
+ // adjusted: Element -> Node
+ readonly firstElementChild: Node | null;
+ // adjusted: Element -> Node
+ readonly lastElementChild: Node | null;
+ append(...nodes: Array): void;
+ prepend(...nodes: Array): void;
+ // not currently supported
+ // querySelector(
+ // selectors: K,
+ // ): HTMLElementTagNameMap[K] | null;
+ // querySelector(
+ // selectors: K,
+ // ): SVGElementTagNameMap[K] | null;
+ // querySelector(selectors: string): E | null;
+ // querySelectorAll(
+ // selectors: K,
+ // ): NodeListOf;
+ // querySelectorAll(
+ // selectors: K,
+ // ): NodeListOf;
+ // querySelectorAll(
+ // selectors: string,
+ // ): NodeListOf;
}
-export interface CustomEvent extends Event {
- readonly detail: any;
- initCustomEvent(
- type: string,
- bubbles?: boolean,
- cancelable?: boolean,
- detail?: any | null
+interface NodeList {
+ readonly length: number;
+ item(index: number): Node | null;
+ forEach(
+ callbackfn: (value: Node, key: number, parent: NodeList) => void,
+ thisArg?: any
): void;
+ [index: number]: Node;
+ [Symbol.iterator](): IterableIterator;
+ entries(): IterableIterator<[number, Node]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
+}
+
+interface NodeListOf extends NodeList {
+ length: number;
+ item(index: number): TNode;
+ forEach(
+ callbackfn: (value: TNode, key: number, parent: NodeListOf) => void,
+ thisArg?: any
+ ): void;
+ [index: number]: TNode;
+ [Symbol.iterator](): IterableIterator;
+ entries(): IterableIterator<[number, TNode]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
}
export interface DomFile extends Blob {
@@ -225,15 +324,6 @@ interface ProgressEvent extends Event {
readonly total: number;
}
-export interface EventListenerOptions {
- capture?: boolean;
-}
-
-export interface AddEventListenerOptions extends EventListenerOptions {
- once?: boolean;
- passive?: boolean;
-}
-
export interface AbortSignal extends EventTarget {
readonly aborted: boolean;
onabort: ((this: AbortSignal, ev: ProgressEvent) => any) | null;
@@ -259,18 +349,17 @@ export interface AbortSignal extends EventTarget {
): void;
}
-export interface FormData extends DomIterable {
+export class FormData {
append(name: string, value: string | Blob, fileName?: string): void;
delete(name: string): void;
get(name: string): FormDataEntryValue | null;
getAll(name: string): FormDataEntryValue[];
has(name: string): boolean;
set(name: string, value: string | Blob, fileName?: string): void;
-}
-
-export interface FormDataConstructor {
- new (): FormData;
- prototype: FormData;
+ [Symbol.iterator](): IterableIterator<[string, FormDataEntryValue]>;
+ entries(): IterableIterator<[string, FormDataEntryValue]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
}
export interface Blob {
@@ -493,6 +582,7 @@ export interface WritableStreamDefaultController {
error(error?: any): void;
}
*/
+
export interface QueuingStrategy {
highWaterMark?: number;
size?: QueuingStrategySizeCallback;
@@ -502,25 +592,21 @@ export interface QueuingStrategySizeCallback {
(chunk: T): number;
}
-export interface Headers extends DomIterable {
+export class Headers {
+ constructor(init?: HeadersInit);
append(name: string, value: string): void;
delete(name: string): void;
- entries(): IterableIterator<[string, string]>;
get(name: string): string | null;
has(name: string): boolean;
- keys(): IterableIterator;
set(name: string, value: string): void;
- values(): IterableIterator;
forEach(
callbackfn: (value: string, key: string, parent: this) => void,
thisArg?: any
): void;
[Symbol.iterator](): IterableIterator<[string, string]>;
-}
-
-export interface HeadersConstructor {
- new (init?: HeadersInit): Headers;
- prototype: Headers;
+ entries(): IterableIterator<[string, string]>;
+ keys(): IterableIterator;
+ values(): IterableIterator;
}
type RequestCache =
@@ -582,11 +668,6 @@ export interface ResponseInit {
statusText?: string;
}
-export interface RequestConstructor {
- new (input: RequestInfo, init?: RequestInit): Request;
- prototype: Request;
-}
-
export interface Request extends Body {
readonly cache?: RequestCache;
readonly credentials?: RequestCredentials;
@@ -606,6 +687,11 @@ export interface Request extends Body {
clone(): Request;
}
+export interface RequestConstructor {
+ new (input: RequestInfo, init?: RequestInit): Request;
+ prototype: Request;
+}
+
export interface Response extends Body {
readonly headers: Headers;
readonly ok: boolean;
@@ -618,14 +704,22 @@ export interface Response extends Body {
clone(): Response;
}
-export interface DOMStringList {
+export interface ResponseConstructor {
+ prototype: Response;
+ new (body?: BodyInit | null, init?: ResponseInit): Response;
+ error(): Response;
+ redirect(url: string, status?: number): Response;
+}
+
+export class DOMStringList {
readonly length: number;
contains(string: string): boolean;
item(index: number): string | null;
[index: number]: string;
+ [Symbol.iterator](): IterableIterator;
}
-export interface Location {
+export class Location {
readonly ancestorOrigins: DOMStringList;
hash: string;
host: string;
@@ -642,7 +736,8 @@ export interface Location {
replace(url: string): void;
}
-export interface URL {
+export class URL {
+ constructor(url: string, base?: string | URL);
hash: string;
host: string;
hostname: string;
@@ -657,54 +752,6 @@ export interface URL {
readonly searchParams: URLSearchParams;
username: string;
toJSON(): string;
-}
-
-export interface URLSearchParams {
- /**
- * Appends a specified key/value pair as a new search parameter.
- */
- append(name: string, value: string): void;
- /**
- * Deletes the given search parameter, and its associated value, from the list of all search parameters.
- */
- delete(name: string): void;
- /**
- * Returns the first value associated to the given search parameter.
- */
- get(name: string): string | null;
- /**
- * Returns all the values association with a given search parameter.
- */
- getAll(name: string): string[];
- /**
- * Returns a Boolean indicating if such a search parameter exists.
- */
- has(name: string): boolean;
- /**
- * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others.
- */
- set(name: string, value: string): void;
- sort(): void;
- /**
- * Returns a string containing a query string suitable for use in a URL. Does not include the question mark.
- */
- toString(): string;
- forEach(
- callbackfn: (value: string, key: string, parent: URLSearchParams) => void,
- thisArg?: any
- ): void;
-
- [Symbol.iterator](): IterableIterator<[string, string]>;
- /**
- * Returns an array of key, value pairs for every entry in the search params.
- */
- entries(): IterableIterator<[string, string]>;
- /**
- * Returns a list of keys in the search params.
- */
- keys(): IterableIterator;
- /**
- * Returns a list of values in the search params.
- */
- values(): IterableIterator;
+ static createObjectURL(object: any): string;
+ static revokeObjectURL(url: string): void;
}
diff --git a/cli/js/web/dom_util.ts b/cli/js/web/dom_util.ts
index 40a8c618f6..d12593e8e8 100644
--- a/cli/js/web/dom_util.ts
+++ b/cli/js/web/dom_util.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-// Utility functions for DOM nodes
-import * as domTypes from "./dom_types.ts";
+
+import * as domTypes from "./dom_types.d.ts";
export function getDOMStringList(arr: string[]): domTypes.DOMStringList {
Object.defineProperties(arr, {
@@ -16,87 +16,5 @@ export function getDOMStringList(arr: string[]): domTypes.DOMStringList {
},
},
});
- return (arr as unknown) as domTypes.DOMStringList;
-}
-
-export function isNode(nodeImpl: domTypes.EventTarget | null): boolean {
- return Boolean(nodeImpl && "nodeType" in nodeImpl);
-}
-
-export function isShadowRoot(nodeImpl: domTypes.EventTarget | null): boolean {
- return Boolean(
- nodeImpl &&
- nodeImpl[domTypes.eventTargetNodeType] ===
- domTypes.NodeType.DOCUMENT_FRAGMENT_NODE &&
- nodeImpl[domTypes.eventTargetHost] != null
- );
-}
-
-export function isSlotable(nodeImpl: domTypes.EventTarget | null): boolean {
- return Boolean(
- nodeImpl &&
- (nodeImpl[domTypes.eventTargetNodeType] ===
- domTypes.NodeType.ELEMENT_NODE ||
- nodeImpl[domTypes.eventTargetNodeType] === domTypes.NodeType.TEXT_NODE)
- );
-}
-
-// https://dom.spec.whatwg.org/#node-trees
-// const domSymbolTree = Symbol("DOM Symbol Tree");
-
-// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
-export function isShadowInclusiveAncestor(
- ancestor: domTypes.EventTarget | null,
- node: domTypes.EventTarget | null
-): boolean {
- while (isNode(node)) {
- if (node === ancestor) {
- return true;
- }
-
- if (isShadowRoot(node)) {
- node = node && node[domTypes.eventTargetHost];
- } else {
- node = null; // domSymbolTree.parent(node);
- }
- }
-
- return false;
-}
-
-export function getRoot(
- node: domTypes.EventTarget | null
-): domTypes.EventTarget | null {
- const root = node;
-
- // for (const ancestor of domSymbolTree.ancestorsIterator(node)) {
- // root = ancestor;
- // }
-
- return root;
-}
-
-// https://dom.spec.whatwg.org/#retarget
-export function retarget(
- a: domTypes.EventTarget | null,
- b: domTypes.EventTarget
-): domTypes.EventTarget | null {
- while (true) {
- if (!isNode(a)) {
- return a;
- }
-
- const aRoot = getRoot(a);
-
- if (aRoot) {
- if (
- !isShadowRoot(aRoot) ||
- (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
- ) {
- return a;
- }
-
- a = aRoot[domTypes.eventTargetHost];
- }
- }
+ return arr as string[] & domTypes.DOMStringList;
}
diff --git a/cli/js/web/event.ts b/cli/js/web/event.ts
index b063efa600..b57c0f9013 100644
--- a/cli/js/web/event.ts
+++ b/cli/js/web/event.ts
@@ -1,45 +1,153 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
-import { getPrivateValue, requiredArguments } from "./util.ts";
-// WeakMaps are recommended for private attributes (see MDN link below)
-// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps
-export const eventAttributes = new WeakMap();
+import * as domTypes from "./dom_types.d.ts";
+import { defineEnumerableProps, requiredArguments } from "./util.ts";
+import { assert } from "../util.ts";
-function isTrusted(this: Event): boolean {
- return getPrivateValue(this, eventAttributes, "isTrusted");
+/** Stores a non-accessible view of the event path which is used internally in
+ * the logic for determining the path of an event. */
+export interface EventPath {
+ item: EventTarget;
+ itemInShadowTree: boolean;
+ relatedTarget: EventTarget | null;
+ rootOfClosedTree: boolean;
+ slotInClosedTree: boolean;
+ target: EventTarget | null;
+ touchTargetList: EventTarget[];
}
-export class Event implements domTypes.Event {
+interface EventAttributes {
+ type: string;
+ bubbles: boolean;
+ cancelable: boolean;
+ composed: boolean;
+ currentTarget: EventTarget | null;
+ eventPhase: number;
+ target: EventTarget | null;
+ timeStamp: number;
+}
+
+interface EventData {
+ dispatched: boolean;
+ inPassiveListener: boolean;
+ isTrusted: boolean;
+ path: EventPath[];
+ stopImmediatePropagation: boolean;
+}
+
+const eventData = new WeakMap();
+
+// accessors for non runtime visible data
+
+export function getDispatched(event: Event): boolean {
+ return Boolean(eventData.get(event)?.dispatched);
+}
+
+export function getPath(event: Event): EventPath[] {
+ return eventData.get(event)?.path ?? [];
+}
+
+export function getStopImmediatePropagation(event: Event): boolean {
+ return Boolean(eventData.get(event)?.stopImmediatePropagation);
+}
+
+export function setCurrentTarget(
+ event: Event,
+ value: EventTarget | null
+): void {
+ (event as EventImpl).currentTarget = value;
+}
+
+export function setDispatched(event: Event, value: boolean): void {
+ const data = eventData.get(event as Event);
+ if (data) {
+ data.dispatched = value;
+ }
+}
+
+export function setEventPhase(event: Event, value: number): void {
+ (event as EventImpl).eventPhase = value;
+}
+
+export function setInPassiveListener(event: Event, value: boolean): void {
+ const data = eventData.get(event as Event);
+ if (data) {
+ data.inPassiveListener = value;
+ }
+}
+
+export function setPath(event: Event, value: EventPath[]): void {
+ const data = eventData.get(event as Event);
+ if (data) {
+ data.path = value;
+ }
+}
+
+export function setRelatedTarget(
+ event: T,
+ value: EventTarget | null
+): void {
+ if ("relatedTarget" in event) {
+ (event as T & {
+ relatedTarget: EventTarget | null;
+ }).relatedTarget = value;
+ }
+}
+
+export function setTarget(event: Event, value: EventTarget | null): void {
+ (event as EventImpl).target = value;
+}
+
+export function setStopImmediatePropagation(
+ event: Event,
+ value: boolean
+): void {
+ const data = eventData.get(event as Event);
+ if (data) {
+ data.stopImmediatePropagation = value;
+ }
+}
+
+// Type guards that widen the event type
+
+export function hasRelatedTarget(
+ event: Event
+): event is domTypes.FocusEvent | domTypes.MouseEvent {
+ return "relatedTarget" in event;
+}
+
+function isTrusted(this: Event): boolean {
+ return eventData.get(this)!.isTrusted;
+}
+
+export class EventImpl implements Event {
// The default value is `false`.
// Use `defineProperty` to define on each instance, NOT on the prototype.
isTrusted!: boolean;
- // Each event has the following associated flags
- private _canceledFlag = false;
- private _dispatchedFlag = false;
- private _initializedFlag = false;
- private _inPassiveListenerFlag = false;
- private _stopImmediatePropagationFlag = false;
- private _stopPropagationFlag = false;
- // Property for objects on which listeners will be invoked
- private _path: domTypes.EventPath[] = [];
+ #canceledFlag = false;
+ #stopPropagationFlag = false;
+ #attributes: EventAttributes;
- constructor(type: string, eventInitDict: domTypes.EventInit = {}) {
+ constructor(type: string, eventInitDict: EventInit = {}) {
requiredArguments("Event", arguments.length, 1);
type = String(type);
- this._initializedFlag = true;
- eventAttributes.set(this, {
+ this.#attributes = {
type,
- bubbles: eventInitDict.bubbles || false,
- cancelable: eventInitDict.cancelable || false,
- composed: eventInitDict.composed || false,
+ bubbles: eventInitDict.bubbles ?? false,
+ cancelable: eventInitDict.cancelable ?? false,
+ composed: eventInitDict.composed ?? false,
currentTarget: null,
- eventPhase: domTypes.EventPhase.NONE,
- isTrusted: false,
- relatedTarget: 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,
@@ -48,151 +156,100 @@ export class Event implements domTypes.Event {
}
get bubbles(): boolean {
- return getPrivateValue(this, eventAttributes, "bubbles");
+ return this.#attributes.bubbles;
}
get cancelBubble(): boolean {
- return this._stopPropagationFlag;
+ return this.#stopPropagationFlag;
}
set cancelBubble(value: boolean) {
- this._stopPropagationFlag = value;
- }
-
- get cancelBubbleImmediately(): boolean {
- return this._stopImmediatePropagationFlag;
- }
-
- set cancelBubbleImmediately(value: boolean) {
- this._stopImmediatePropagationFlag = value;
+ this.#stopPropagationFlag = value;
}
get cancelable(): boolean {
- return getPrivateValue(this, eventAttributes, "cancelable");
+ return this.#attributes.cancelable;
}
get composed(): boolean {
- return getPrivateValue(this, eventAttributes, "composed");
+ return this.#attributes.composed;
}
- get currentTarget(): domTypes.EventTarget {
- return getPrivateValue(this, eventAttributes, "currentTarget");
+ get currentTarget(): EventTarget | null {
+ return this.#attributes.currentTarget;
}
- set currentTarget(value: domTypes.EventTarget) {
- eventAttributes.set(this, {
+ set currentTarget(value: EventTarget | null) {
+ this.#attributes = {
type: this.type,
bubbles: this.bubbles,
cancelable: this.cancelable,
composed: this.composed,
currentTarget: value,
eventPhase: this.eventPhase,
- isTrusted: this.isTrusted,
- relatedTarget: this.relatedTarget,
target: this.target,
timeStamp: this.timeStamp,
- });
+ };
}
get defaultPrevented(): boolean {
- return this._canceledFlag;
- }
-
- get dispatched(): boolean {
- return this._dispatchedFlag;
- }
-
- set dispatched(value: boolean) {
- this._dispatchedFlag = value;
+ return this.#canceledFlag;
}
get eventPhase(): number {
- return getPrivateValue(this, eventAttributes, "eventPhase");
+ return this.#attributes.eventPhase;
}
set eventPhase(value: number) {
- eventAttributes.set(this, {
+ this.#attributes = {
type: this.type,
bubbles: this.bubbles,
cancelable: this.cancelable,
composed: this.composed,
currentTarget: this.currentTarget,
eventPhase: value,
- isTrusted: this.isTrusted,
- relatedTarget: this.relatedTarget,
target: this.target,
timeStamp: this.timeStamp,
- });
+ };
}
get initialized(): boolean {
- return this._initializedFlag;
+ return true;
}
- set inPassiveListener(value: boolean) {
- this._inPassiveListenerFlag = value;
+ get target(): EventTarget | null {
+ return this.#attributes.target;
}
- get path(): domTypes.EventPath[] {
- return this._path;
- }
-
- set path(value: domTypes.EventPath[]) {
- this._path = value;
- }
-
- get relatedTarget(): domTypes.EventTarget {
- return getPrivateValue(this, eventAttributes, "relatedTarget");
- }
-
- set relatedTarget(value: domTypes.EventTarget) {
- eventAttributes.set(this, {
+ set target(value: EventTarget | null) {
+ this.#attributes = {
type: this.type,
bubbles: this.bubbles,
cancelable: this.cancelable,
composed: this.composed,
currentTarget: this.currentTarget,
eventPhase: this.eventPhase,
- isTrusted: this.isTrusted,
- relatedTarget: value,
- target: this.target,
- timeStamp: this.timeStamp,
- });
- }
-
- get target(): domTypes.EventTarget {
- return getPrivateValue(this, eventAttributes, "target");
- }
-
- set target(value: domTypes.EventTarget) {
- eventAttributes.set(this, {
- type: this.type,
- bubbles: this.bubbles,
- cancelable: this.cancelable,
- composed: this.composed,
- currentTarget: this.currentTarget,
- eventPhase: this.eventPhase,
- isTrusted: this.isTrusted,
- relatedTarget: this.relatedTarget,
target: value,
timeStamp: this.timeStamp,
- });
+ };
}
- get timeStamp(): Date {
- return getPrivateValue(this, eventAttributes, "timeStamp");
+ get timeStamp(): number {
+ return this.#attributes.timeStamp;
}
get type(): string {
- return getPrivateValue(this, eventAttributes, "type");
+ return this.#attributes.type;
}
- composedPath(): domTypes.EventPath[] {
- if (this._path.length === 0) {
+ composedPath(): EventTarget[] {
+ const path = eventData.get(this)!.path;
+ if (path.length === 0) {
return [];
}
- const composedPath: domTypes.EventPath[] = [
+ assert(this.currentTarget);
+ const composedPath: EventPath[] = [
{
item: this.currentTarget,
itemInShadowTree: false,
@@ -207,8 +264,8 @@ export class Event implements domTypes.Event {
let currentTargetIndex = 0;
let currentTargetHiddenSubtreeLevel = 0;
- for (let index = this._path.length - 1; index >= 0; index--) {
- const { item, rootOfClosedTree, slotInClosedTree } = this._path[index];
+ for (let index = path.length - 1; index >= 0; index--) {
+ const { item, rootOfClosedTree, slotInClosedTree } = path[index];
if (rootOfClosedTree) {
currentTargetHiddenSubtreeLevel++;
@@ -228,7 +285,7 @@ export class Event implements domTypes.Event {
let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
for (let i = currentTargetIndex - 1; i >= 0; i--) {
- const { item, rootOfClosedTree, slotInClosedTree } = this._path[i];
+ const { item, rootOfClosedTree, slotInClosedTree } = path[i];
if (rootOfClosedTree) {
currentHiddenLevel++;
@@ -258,12 +315,8 @@ export class Event implements domTypes.Event {
currentHiddenLevel = currentTargetHiddenSubtreeLevel;
maxHiddenLevel = currentTargetHiddenSubtreeLevel;
- for (
- let index = currentTargetIndex + 1;
- index < this._path.length;
- index++
- ) {
- const { item, rootOfClosedTree, slotInClosedTree } = this._path[index];
+ for (let index = currentTargetIndex + 1; index < path.length; index++) {
+ const { item, rootOfClosedTree, slotInClosedTree } = path[index];
if (slotInClosedTree) {
currentHiddenLevel++;
@@ -289,35 +342,65 @@ export class Event implements domTypes.Event {
}
}
}
-
- return composedPath;
+ return composedPath.map((p) => p.item);
}
preventDefault(): void {
- if (this.cancelable && !this._inPassiveListenerFlag) {
- this._canceledFlag = true;
+ if (this.cancelable && !eventData.get(this)!.inPassiveListener) {
+ this.#canceledFlag = true;
}
}
stopPropagation(): void {
- this._stopPropagationFlag = true;
+ this.#stopPropagationFlag = true;
}
stopImmediatePropagation(): void {
- this._stopPropagationFlag = true;
- this._stopImmediatePropagationFlag = true;
+ this.#stopPropagationFlag = true;
+ eventData.get(this)!.stopImmediatePropagation = true;
+ }
+
+ get NONE(): number {
+ return Event.NONE;
+ }
+
+ get CAPTURING_PHASE(): number {
+ return Event.CAPTURING_PHASE;
+ }
+
+ get AT_TARGET(): number {
+ return Event.AT_TARGET;
+ }
+
+ get BUBBLING_PHASE(): number {
+ return Event.BUBBLING_PHASE;
+ }
+
+ static get NONE(): number {
+ return 0;
+ }
+
+ static get CAPTURING_PHASE(): number {
+ return 1;
+ }
+
+ static get AT_TARGET(): number {
+ return 2;
+ }
+
+ static get BUBBLING_PHASE(): number {
+ return 3;
}
}
-Reflect.defineProperty(Event.prototype, "bubbles", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "cancelable", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "composed", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "currentTarget", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "defaultPrevented", {
- enumerable: true,
-});
-Reflect.defineProperty(Event.prototype, "dispatched", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "eventPhase", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "target", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "timeStamp", { enumerable: true });
-Reflect.defineProperty(Event.prototype, "type", { enumerable: true });
+defineEnumerableProps(EventImpl, [
+ "bubbles",
+ "cancelable",
+ "composed",
+ "currentTarget",
+ "defaultPrevented",
+ "eventPhase",
+ "target",
+ "timeStamp",
+ "type",
+]);
diff --git a/cli/js/web/event_target.ts b/cli/js/web/event_target.ts
index 605504a3a8..1a560dfbeb 100644
--- a/cli/js/web/event_target.ts
+++ b/cli/js/web/event_target.ts
@@ -1,115 +1,549 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
-import { hasOwnProperty, requiredArguments } from "./util.ts";
-import {
- getRoot,
- isNode,
- isShadowRoot,
- isShadowInclusiveAncestor,
- isSlotable,
- retarget,
-} from "./dom_util.ts";
-// https://dom.spec.whatwg.org/#get-the-parent
-// Note: Nodes, shadow roots, and documents override this algorithm so we set it to null.
-function getEventTargetParent(
- _eventTarget: domTypes.EventTarget,
- _event: domTypes.Event
-): null {
- return null;
+// This module follows most of the WHATWG Living Standard for the DOM logic.
+// Many parts of the DOM are not implemented in Deno, but the logic for those
+// parts still exists. This means you will observe a lot of strange structures
+// and impossible logic branches based on what Deno currently supports.
+
+import { DOMExceptionImpl as DOMException } from "./dom_exception.ts";
+import * as domTypes from "./dom_types.d.ts";
+import {
+ EventImpl as Event,
+ EventPath,
+ getDispatched,
+ getPath,
+ getStopImmediatePropagation,
+ hasRelatedTarget,
+ setCurrentTarget,
+ setDispatched,
+ setEventPhase,
+ setInPassiveListener,
+ setPath,
+ setRelatedTarget,
+ setStopImmediatePropagation,
+ setTarget,
+} from "./event.ts";
+import { defineEnumerableProps, requiredArguments } from "./util.ts";
+
+// This is currently the only node type we are using, so instead of implementing
+// the whole of the Node interface at the moment, this just gives us the one
+// value to power the standards based logic
+const DOCUMENT_FRAGMENT_NODE = 11;
+
+// DOM Logic Helper functions and type guards
+
+/** Get the parent node, for event targets that have a parent.
+ *
+ * Ref: https://dom.spec.whatwg.org/#get-the-parent */
+function getParent(eventTarget: EventTarget): EventTarget | null {
+ return isNode(eventTarget) ? eventTarget.parentNode : null;
}
-export const eventTargetAssignedSlot: unique symbol = Symbol();
-export const eventTargetHasActivationBehavior: unique symbol = Symbol();
+function getRoot(eventTarget: EventTarget): EventTarget | null {
+ return isNode(eventTarget)
+ ? eventTarget.getRootNode({ composed: true })
+ : null;
+}
-export class EventTarget implements domTypes.EventTarget {
- public [domTypes.eventTargetHost]: domTypes.EventTarget | null = null;
- public [domTypes.eventTargetListeners]: {
- [type in string]: domTypes.EventTargetListener[];
- } = {};
- public [domTypes.eventTargetMode] = "";
- public [domTypes.eventTargetNodeType]: domTypes.NodeType =
- domTypes.NodeType.DOCUMENT_FRAGMENT_NODE;
- private [eventTargetAssignedSlot] = false;
- private [eventTargetHasActivationBehavior] = false;
+function isNode(
+ eventTarget: T | null
+): eventTarget is T & domTypes.Node {
+ return Boolean(eventTarget && "nodeType" in eventTarget);
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
+function isShadowInclusiveAncestor(
+ ancestor: EventTarget | null,
+ node: EventTarget | null
+): boolean {
+ while (isNode(node)) {
+ if (node === ancestor) {
+ return true;
+ }
+
+ if (isShadowRoot(node)) {
+ node = node && getHost(node);
+ } else {
+ node = getParent(node);
+ }
+ }
+
+ return false;
+}
+
+function isShadowRoot(nodeImpl: EventTarget | null): boolean {
+ return Boolean(
+ nodeImpl &&
+ isNode(nodeImpl) &&
+ nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
+ getHost(nodeImpl) != null
+ );
+}
+
+function isSlotable(
+ nodeImpl: T | null
+): nodeImpl is T & domTypes.Node & domTypes.Slotable {
+ return Boolean(isNode(nodeImpl) && "assignedSlot" in nodeImpl);
+}
+
+// DOM Logic functions
+
+/** Append a path item to an event's path.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-path-append
+ */
+function appendToEventPath(
+ eventImpl: Event,
+ target: EventTarget,
+ targetOverride: EventTarget | null,
+ relatedTarget: EventTarget | null,
+ touchTargets: EventTarget[],
+ slotInClosedTree: boolean
+): void {
+ const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
+ const rootOfClosedTree = isShadowRoot(target) && getMode(target) === "closed";
+
+ getPath(eventImpl).push({
+ item: target,
+ itemInShadowTree,
+ target: targetOverride,
+ relatedTarget,
+ touchTargetList: touchTargets,
+ rootOfClosedTree,
+ slotInClosedTree,
+ });
+}
+
+function dispatch(
+ targetImpl: EventTarget,
+ eventImpl: Event,
+ targetOverride?: EventTarget
+): boolean {
+ let clearTargets = false;
+ let activationTarget: EventTarget | null = null;
+
+ setDispatched(eventImpl, true);
+
+ targetOverride = targetOverride ?? targetImpl;
+ const eventRelatedTarget = hasRelatedTarget(eventImpl)
+ ? eventImpl.relatedTarget
+ : null;
+ let relatedTarget = retarget(eventRelatedTarget, targetImpl);
+
+ if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
+ const touchTargets: EventTarget[] = [];
+
+ appendToEventPath(
+ eventImpl,
+ targetImpl,
+ targetOverride,
+ relatedTarget,
+ touchTargets,
+ false
+ );
+
+ const isActivationEvent = eventImpl.type === "click";
+
+ if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
+ activationTarget = targetImpl;
+ }
+
+ let slotInClosedTree = false;
+ let slotable =
+ isSlotable(targetImpl) && getAssignedSlot(targetImpl) ? targetImpl : null;
+ let parent = getParent(targetImpl);
+
+ // Populate event path
+ // https://dom.spec.whatwg.org/#event-path
+ while (parent !== null) {
+ if (slotable !== null) {
+ slotable = null;
+
+ const parentRoot = getRoot(parent);
+ if (
+ isShadowRoot(parentRoot) &&
+ parentRoot &&
+ getMode(parentRoot) === "closed"
+ ) {
+ slotInClosedTree = true;
+ }
+ }
+
+ relatedTarget = retarget(eventRelatedTarget, parent);
+
+ if (
+ isNode(parent) &&
+ isShadowInclusiveAncestor(getRoot(targetImpl), parent)
+ ) {
+ appendToEventPath(
+ eventImpl,
+ parent,
+ null,
+ relatedTarget,
+ touchTargets,
+ slotInClosedTree
+ );
+ } else if (parent === relatedTarget) {
+ parent = null;
+ } else {
+ targetImpl = parent;
+
+ if (
+ isActivationEvent &&
+ activationTarget === null &&
+ getHasActivationBehavior(targetImpl)
+ ) {
+ activationTarget = targetImpl;
+ }
+
+ appendToEventPath(
+ eventImpl,
+ parent,
+ targetImpl,
+ relatedTarget,
+ touchTargets,
+ slotInClosedTree
+ );
+ }
+
+ if (parent !== null) {
+ parent = getParent(parent);
+ }
+
+ slotInClosedTree = false;
+ }
+
+ let clearTargetsTupleIndex = -1;
+ const path = getPath(eventImpl);
+ for (
+ let i = path.length - 1;
+ i >= 0 && clearTargetsTupleIndex === -1;
+ i--
+ ) {
+ if (path[i].target !== null) {
+ clearTargetsTupleIndex = i;
+ }
+ }
+ const clearTargetsTuple = path[clearTargetsTupleIndex];
+
+ clearTargets =
+ (isNode(clearTargetsTuple.target) &&
+ isShadowRoot(getRoot(clearTargetsTuple.target))) ||
+ (isNode(clearTargetsTuple.relatedTarget) &&
+ isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
+
+ setEventPhase(eventImpl, Event.CAPTURING_PHASE);
+
+ for (let i = path.length - 1; i >= 0; --i) {
+ const tuple = path[i];
+
+ if (tuple.target === null) {
+ invokeEventListeners(tuple, eventImpl);
+ }
+ }
+
+ for (let i = 0; i < path.length; i++) {
+ const tuple = path[i];
+
+ if (tuple.target !== null) {
+ setEventPhase(eventImpl, Event.AT_TARGET);
+ } else {
+ setEventPhase(eventImpl, Event.BUBBLING_PHASE);
+ }
+
+ if (
+ (eventImpl.eventPhase === Event.BUBBLING_PHASE && eventImpl.bubbles) ||
+ eventImpl.eventPhase === Event.AT_TARGET
+ ) {
+ invokeEventListeners(tuple, eventImpl);
+ }
+ }
+ }
+
+ setEventPhase(eventImpl, Event.NONE);
+ setCurrentTarget(eventImpl, null);
+ setPath(eventImpl, []);
+ setDispatched(eventImpl, false);
+ eventImpl.cancelBubble = false;
+ setStopImmediatePropagation(eventImpl, false);
+
+ if (clearTargets) {
+ setTarget(eventImpl, null);
+ setRelatedTarget(eventImpl, null);
+ }
+
+ // TODO: invoke activation targets if HTML nodes will be implemented
+ // if (activationTarget !== null) {
+ // if (!eventImpl.defaultPrevented) {
+ // activationTarget._activationBehavior();
+ // }
+ // }
+
+ return !eventImpl.defaultPrevented;
+}
+
+/** Inner invoking of the event listeners where the resolved listeners are
+ * called.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
+function innerInvokeEventListeners(
+ eventImpl: Event,
+ targetListeners: Record
+): boolean {
+ let found = false;
+
+ const { type } = eventImpl;
+
+ if (!targetListeners || !targetListeners[type]) {
+ return found;
+ }
+
+ // Copy event listeners before iterating since the list can be modified during the iteration.
+ const handlers = targetListeners[type].slice();
+
+ for (let i = 0; i < handlers.length; i++) {
+ const listener = handlers[i];
+
+ let capture, once, passive;
+ if (typeof listener.options === "boolean") {
+ capture = listener.options;
+ once = false;
+ passive = false;
+ } else {
+ capture = listener.options.capture;
+ once = listener.options.once;
+ passive = listener.options.passive;
+ }
+
+ // Check if the event listener has been removed since the listeners has been cloned.
+ if (!targetListeners[type].includes(listener)) {
+ continue;
+ }
+
+ found = true;
+
+ if (
+ (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
+ (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
+ ) {
+ continue;
+ }
+
+ if (once) {
+ targetListeners[type].splice(targetListeners[type].indexOf(listener), 1);
+ }
+
+ if (passive) {
+ setInPassiveListener(eventImpl, true);
+ }
+
+ if (typeof listener.callback === "object") {
+ if (typeof listener.callback.handleEvent === "function") {
+ listener.callback.handleEvent(eventImpl);
+ }
+ } else {
+ listener.callback.call(eventImpl.currentTarget, eventImpl);
+ }
+
+ setInPassiveListener(eventImpl, false);
+
+ if (getStopImmediatePropagation(eventImpl)) {
+ return found;
+ }
+ }
+
+ return found;
+}
+
+/** Invokes the listeners on a given event path with the supplied event.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
+function invokeEventListeners(tuple: EventPath, eventImpl: Event): void {
+ const path = getPath(eventImpl);
+ const tupleIndex = path.indexOf(tuple);
+ for (let i = tupleIndex; i >= 0; i--) {
+ const t = path[i];
+ if (t.target) {
+ setTarget(eventImpl, t.target);
+ break;
+ }
+ }
+
+ setRelatedTarget(eventImpl, tuple.relatedTarget);
+
+ if (eventImpl.cancelBubble) {
+ return;
+ }
+
+ setCurrentTarget(eventImpl, tuple.item);
+
+ innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
+}
+
+function normalizeAddEventHandlerOptions(
+ options: boolean | AddEventListenerOptions | undefined
+): AddEventListenerOptions {
+ if (typeof options === "boolean" || typeof options === "undefined") {
+ return {
+ capture: Boolean(options),
+ once: false,
+ passive: false,
+ };
+ } else {
+ return options;
+ }
+}
+
+function normalizeEventHandlerOptions(
+ options: boolean | EventListenerOptions | undefined
+): EventListenerOptions {
+ if (typeof options === "boolean" || typeof options === "undefined") {
+ return {
+ capture: Boolean(options),
+ };
+ } else {
+ return options;
+ }
+}
+
+/** Retarget the target following the spec logic.
+ *
+ * Ref: https://dom.spec.whatwg.org/#retarget */
+function retarget(a: EventTarget | null, b: EventTarget): EventTarget | null {
+ while (true) {
+ if (!isNode(a)) {
+ return a;
+ }
+
+ const aRoot = a.getRootNode();
+
+ if (aRoot) {
+ if (
+ !isShadowRoot(aRoot) ||
+ (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
+ ) {
+ return a;
+ }
+
+ a = getHost(aRoot);
+ }
+ }
+}
+
+// Non-public state information for an event target that needs to held onto.
+// Some of the information should be moved to other entities (like Node,
+// ShowRoot, UIElement, etc.).
+interface EventTargetData {
+ assignedSlot: boolean;
+ hasActivationBehavior: boolean;
+ host: EventTarget | null;
+ listeners: Record;
+ mode: string;
+}
+
+interface Listener {
+ callback: EventListenerOrEventListenerObject;
+ options: AddEventListenerOptions;
+}
+
+// Accessors for non-public data
+
+export const eventTargetData = new WeakMap();
+
+function getAssignedSlot(target: EventTarget): boolean {
+ return Boolean(eventTargetData.get(target as EventTarget)?.assignedSlot);
+}
+
+function getHasActivationBehavior(target: EventTarget): boolean {
+ return Boolean(
+ eventTargetData.get(target as EventTarget)?.hasActivationBehavior
+ );
+}
+
+function getHost(target: EventTarget): EventTarget | null {
+ return eventTargetData.get(target as EventTarget)?.host ?? null;
+}
+
+function getListeners(target: EventTarget): Record {
+ return eventTargetData.get(target as EventTarget)?.listeners ?? {};
+}
+
+function getMode(target: EventTarget): string | null {
+ return eventTargetData.get(target as EventTarget)?.mode ?? null;
+}
+
+export function getDefaultTargetData(): Readonly {
+ return {
+ assignedSlot: false,
+ hasActivationBehavior: false,
+ host: null,
+ listeners: Object.create(null),
+ mode: "",
+ };
+}
+
+export class EventTargetImpl implements EventTarget {
+ constructor() {
+ eventTargetData.set(this, getDefaultTargetData());
+ }
public addEventListener(
type: string,
- callback: domTypes.EventListenerOrEventListenerObject | null,
- options?: domTypes.AddEventListenerOptions | boolean
+ callback: EventListenerOrEventListenerObject | null,
+ options?: AddEventListenerOptions | boolean
): void {
- const this_ = this || globalThis;
-
requiredArguments("EventTarget.addEventListener", arguments.length, 2);
- const normalizedOptions: domTypes.AddEventListenerOptions = eventTargetHelpers.normalizeAddEventHandlerOptions(
- options
- );
-
if (callback === null) {
return;
}
- const listeners = this_[domTypes.eventTargetListeners];
+ options = normalizeAddEventHandlerOptions(options);
+ const { listeners } = eventTargetData.get(this ?? globalThis)!;
- if (!hasOwnProperty(listeners, type)) {
+ if (!(type in listeners)) {
listeners[type] = [];
}
- for (let i = 0; i < listeners[type].length; ++i) {
- const listener = listeners[type][i];
+ for (const listener of listeners[type]) {
if (
((typeof listener.options === "boolean" &&
- listener.options === normalizedOptions.capture) ||
+ listener.options === options.capture) ||
(typeof listener.options === "object" &&
- listener.options.capture === normalizedOptions.capture)) &&
+ listener.options.capture === options.capture)) &&
listener.callback === callback
) {
return;
}
}
- listeners[type].push({
- callback,
- options: normalizedOptions,
- });
+ listeners[type].push({ callback, options });
}
public removeEventListener(
type: string,
- callback: domTypes.EventListenerOrEventListenerObject | null,
- options?: domTypes.EventListenerOptions | boolean
+ callback: EventListenerOrEventListenerObject | null,
+ options?: EventListenerOptions | boolean
): void {
- const this_ = this || globalThis;
-
requiredArguments("EventTarget.removeEventListener", arguments.length, 2);
- const listeners = this_[domTypes.eventTargetListeners];
- if (hasOwnProperty(listeners, type) && callback !== null) {
+
+ const listeners = eventTargetData.get(this ?? globalThis)!.listeners;
+ if (callback !== null && type in listeners) {
listeners[type] = listeners[type].filter(
- (listener): boolean => listener.callback !== callback
+ (listener) => listener.callback !== callback
);
- }
-
- const normalizedOptions: domTypes.EventListenerOptions = eventTargetHelpers.normalizeEventHandlerOptions(
- options
- );
-
- if (callback === null) {
- // Optimization, not in the spec.
+ } else if (callback === null || !listeners[type]) {
return;
}
- if (!listeners[type]) {
- return;
- }
+ options = normalizeEventHandlerOptions(options);
for (let i = 0; i < listeners[type].length; ++i) {
const listener = listeners[type][i];
-
if (
((typeof listener.options === "boolean" &&
- listener.options === normalizedOptions.capture) ||
+ listener.options === options.capture) ||
(typeof listener.options === "object" &&
- listener.options.capture === normalizedOptions.capture)) &&
+ listener.options.capture === options.capture)) &&
listener.callback === callback
) {
listeners[type].splice(i, 1);
@@ -118,380 +552,37 @@ export class EventTarget implements domTypes.EventTarget {
}
}
- public dispatchEvent(event: domTypes.Event): boolean {
- const this_ = this || globalThis;
-
+ public dispatchEvent(event: Event): boolean {
requiredArguments("EventTarget.dispatchEvent", arguments.length, 1);
- const listeners = this_[domTypes.eventTargetListeners];
- if (!hasOwnProperty(listeners, event.type)) {
+ const self = this ?? globalThis;
+
+ const listeners = eventTargetData.get(self)!.listeners;
+ if (!(event.type in listeners)) {
return true;
}
- if (event.dispatched || !event.initialized) {
- // TODO(bartlomieju): very likely that different error
- // should be thrown here (DOMException?)
- throw new TypeError("Tried to dispatch an uninitialized event");
+ if (getDispatched(event)) {
+ throw new DOMException("Invalid event state.", "InvalidStateError");
}
- if (event.eventPhase !== domTypes.EventPhase.NONE) {
- // TODO(bartlomieju): very likely that different error
- // should be thrown here (DOMException?)
- throw new TypeError("Tried to dispatch a dispatching event");
+ if (event.eventPhase !== Event.NONE) {
+ throw new DOMException("Invalid event state.", "InvalidStateError");
}
- return eventTargetHelpers.dispatch(this_, event);
+ return dispatch(self, event);
}
get [Symbol.toStringTag](): string {
return "EventTarget";
}
+
+ protected getParent(_event: Event): EventTarget | null {
+ return null;
+ }
}
-const eventTargetHelpers = {
- // https://dom.spec.whatwg.org/#concept-event-dispatch
- dispatch(
- targetImpl: EventTarget,
- eventImpl: domTypes.Event,
- targetOverride?: domTypes.EventTarget
- ): boolean {
- let clearTargets = false;
- let activationTarget = null;
-
- eventImpl.dispatched = true;
-
- targetOverride = targetOverride || targetImpl;
- let relatedTarget = retarget(eventImpl.relatedTarget, targetImpl);
-
- if (
- targetImpl !== relatedTarget ||
- targetImpl === eventImpl.relatedTarget
- ) {
- const touchTargets: domTypes.EventTarget[] = [];
-
- eventTargetHelpers.appendToEventPath(
- eventImpl,
- targetImpl,
- targetOverride,
- relatedTarget,
- touchTargets,
- false
- );
-
- const isActivationEvent = eventImpl.type === "click";
-
- if (isActivationEvent && targetImpl[eventTargetHasActivationBehavior]) {
- activationTarget = targetImpl;
- }
-
- let slotInClosedTree = false;
- let slotable =
- isSlotable(targetImpl) && targetImpl[eventTargetAssignedSlot]
- ? targetImpl
- : null;
- let parent = getEventTargetParent(targetImpl, eventImpl);
-
- // Populate event path
- // https://dom.spec.whatwg.org/#event-path
- while (parent !== null) {
- if (slotable !== null) {
- slotable = null;
-
- const parentRoot = getRoot(parent);
- if (
- isShadowRoot(parentRoot) &&
- parentRoot &&
- parentRoot[domTypes.eventTargetMode] === "closed"
- ) {
- slotInClosedTree = true;
- }
- }
-
- relatedTarget = retarget(eventImpl.relatedTarget, parent);
-
- if (
- isNode(parent) &&
- isShadowInclusiveAncestor(getRoot(targetImpl), parent)
- ) {
- eventTargetHelpers.appendToEventPath(
- eventImpl,
- parent,
- null,
- relatedTarget,
- touchTargets,
- slotInClosedTree
- );
- } else if (parent === relatedTarget) {
- parent = null;
- } else {
- targetImpl = parent;
-
- if (
- isActivationEvent &&
- activationTarget === null &&
- targetImpl[eventTargetHasActivationBehavior]
- ) {
- activationTarget = targetImpl;
- }
-
- eventTargetHelpers.appendToEventPath(
- eventImpl,
- parent,
- targetImpl,
- relatedTarget,
- touchTargets,
- slotInClosedTree
- );
- }
-
- if (parent !== null) {
- parent = getEventTargetParent(parent, eventImpl);
- }
-
- slotInClosedTree = false;
- }
-
- let clearTargetsTupleIndex = -1;
- for (
- let i = eventImpl.path.length - 1;
- i >= 0 && clearTargetsTupleIndex === -1;
- i--
- ) {
- if (eventImpl.path[i].target !== null) {
- clearTargetsTupleIndex = i;
- }
- }
- const clearTargetsTuple = eventImpl.path[clearTargetsTupleIndex];
-
- clearTargets =
- (isNode(clearTargetsTuple.target) &&
- isShadowRoot(getRoot(clearTargetsTuple.target))) ||
- (isNode(clearTargetsTuple.relatedTarget) &&
- isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
-
- eventImpl.eventPhase = domTypes.EventPhase.CAPTURING_PHASE;
-
- for (let i = eventImpl.path.length - 1; i >= 0; --i) {
- const tuple = eventImpl.path[i];
-
- if (tuple.target === null) {
- eventTargetHelpers.invokeEventListeners(targetImpl, tuple, eventImpl);
- }
- }
-
- for (let i = 0; i < eventImpl.path.length; i++) {
- const tuple = eventImpl.path[i];
-
- if (tuple.target !== null) {
- eventImpl.eventPhase = domTypes.EventPhase.AT_TARGET;
- } else {
- eventImpl.eventPhase = domTypes.EventPhase.BUBBLING_PHASE;
- }
-
- if (
- (eventImpl.eventPhase === domTypes.EventPhase.BUBBLING_PHASE &&
- eventImpl.bubbles) ||
- eventImpl.eventPhase === domTypes.EventPhase.AT_TARGET
- ) {
- eventTargetHelpers.invokeEventListeners(targetImpl, tuple, eventImpl);
- }
- }
- }
-
- eventImpl.eventPhase = domTypes.EventPhase.NONE;
-
- eventImpl.currentTarget = null;
- eventImpl.path = [];
- eventImpl.dispatched = false;
- eventImpl.cancelBubble = false;
- eventImpl.cancelBubbleImmediately = false;
-
- if (clearTargets) {
- eventImpl.target = null;
- eventImpl.relatedTarget = null;
- }
-
- // TODO: invoke activation targets if HTML nodes will be implemented
- // if (activationTarget !== null) {
- // if (!eventImpl.defaultPrevented) {
- // activationTarget._activationBehavior();
- // }
- // }
-
- return !eventImpl.defaultPrevented;
- },
-
- // https://dom.spec.whatwg.org/#concept-event-listener-invoke
- invokeEventListeners(
- targetImpl: EventTarget,
- tuple: domTypes.EventPath,
- eventImpl: domTypes.Event
- ): void {
- const tupleIndex = eventImpl.path.indexOf(tuple);
- for (let i = tupleIndex; i >= 0; i--) {
- const t = eventImpl.path[i];
- if (t.target) {
- eventImpl.target = t.target;
- break;
- }
- }
-
- eventImpl.relatedTarget = tuple.relatedTarget;
-
- if (eventImpl.cancelBubble) {
- return;
- }
-
- eventImpl.currentTarget = tuple.item;
-
- eventTargetHelpers.innerInvokeEventListeners(
- targetImpl,
- eventImpl,
- tuple.item[domTypes.eventTargetListeners]
- );
- },
-
- // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
- innerInvokeEventListeners(
- targetImpl: EventTarget,
- eventImpl: domTypes.Event,
- targetListeners: { [type in string]: domTypes.EventTargetListener[] }
- ): boolean {
- let found = false;
-
- const { type } = eventImpl;
-
- if (!targetListeners || !targetListeners[type]) {
- return found;
- }
-
- // Copy event listeners before iterating since the list can be modified during the iteration.
- const handlers = targetListeners[type].slice();
-
- for (let i = 0; i < handlers.length; i++) {
- const listener = handlers[i];
-
- let capture, once, passive;
- if (typeof listener.options === "boolean") {
- capture = listener.options;
- once = false;
- passive = false;
- } else {
- capture = listener.options.capture;
- once = listener.options.once;
- passive = listener.options.passive;
- }
-
- // Check if the event listener has been removed since the listeners has been cloned.
- if (!targetListeners[type].includes(listener)) {
- continue;
- }
-
- found = true;
-
- if (
- (eventImpl.eventPhase === domTypes.EventPhase.CAPTURING_PHASE &&
- !capture) ||
- (eventImpl.eventPhase === domTypes.EventPhase.BUBBLING_PHASE && capture)
- ) {
- continue;
- }
-
- if (once) {
- targetListeners[type].splice(
- targetListeners[type].indexOf(listener),
- 1
- );
- }
-
- if (passive) {
- eventImpl.inPassiveListener = true;
- }
-
- try {
- 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?)
- throw new Error(error.message);
- }
-
- eventImpl.inPassiveListener = false;
-
- if (eventImpl.cancelBubbleImmediately) {
- return found;
- }
- }
-
- return found;
- },
-
- normalizeAddEventHandlerOptions(
- options: boolean | domTypes.AddEventListenerOptions | undefined
- ): domTypes.AddEventListenerOptions {
- if (typeof options === "boolean" || typeof options === "undefined") {
- const returnValue: domTypes.AddEventListenerOptions = {
- capture: Boolean(options),
- once: false,
- passive: false,
- };
-
- return returnValue;
- } else {
- return options;
- }
- },
-
- normalizeEventHandlerOptions(
- options: boolean | domTypes.EventListenerOptions | undefined
- ): domTypes.EventListenerOptions {
- if (typeof options === "boolean" || typeof options === "undefined") {
- const returnValue: domTypes.EventListenerOptions = {
- capture: Boolean(options),
- };
-
- return returnValue;
- } else {
- return options;
- }
- },
-
- // https://dom.spec.whatwg.org/#concept-event-path-append
- appendToEventPath(
- eventImpl: domTypes.Event,
- target: domTypes.EventTarget,
- targetOverride: domTypes.EventTarget | null,
- relatedTarget: domTypes.EventTarget | null,
- touchTargets: domTypes.EventTarget[],
- slotInClosedTree: boolean
- ): void {
- const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
- const rootOfClosedTree =
- isShadowRoot(target) && target[domTypes.eventTargetMode] === "closed";
-
- eventImpl.path.push({
- item: target,
- itemInShadowTree,
- target: targetOverride,
- relatedTarget,
- touchTargetList: touchTargets,
- rootOfClosedTree,
- slotInClosedTree,
- });
- },
-};
-
-Reflect.defineProperty(EventTarget.prototype, "addEventListener", {
- enumerable: true,
-});
-Reflect.defineProperty(EventTarget.prototype, "removeEventListener", {
- enumerable: true,
-});
-Reflect.defineProperty(EventTarget.prototype, "dispatchEvent", {
- enumerable: true,
-});
+defineEnumerableProps(EventTargetImpl, [
+ "addEventListener",
+ "removeEventListener",
+ "dispatchEvent",
+]);
diff --git a/cli/js/web/fetch.ts b/cli/js/web/fetch.ts
index 26c5ff0537..112bae48f6 100644
--- a/cli/js/web/fetch.ts
+++ b/cli/js/web/fetch.ts
@@ -1,7 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { assert, createResolvable, notImplemented } from "../util.ts";
import { isTypedArray } from "./util.ts";
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { TextDecoder, TextEncoder } from "./text_encoding.ts";
import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts";
import { Headers } from "./headers.ts";
diff --git a/cli/js/web/form_data.ts b/cli/js/web/form_data.ts
index 4517c2a332..42f4194031 100644
--- a/cli/js/web/form_data.ts
+++ b/cli/js/web/form_data.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import * as blob from "./blob.ts";
import * as domFile from "./dom_file.ts";
import { DomIterableMixin } from "./dom_iterable.ts";
diff --git a/cli/js/web/headers.ts b/cli/js/web/headers.ts
index e1d81393d8..1f750faa3a 100644
--- a/cli/js/web/headers.ts
+++ b/cli/js/web/headers.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { DomIterableMixin } from "./dom_iterable.ts";
import { requiredArguments } from "./util.ts";
import { customInspect } from "./console.ts";
diff --git a/cli/js/web/location.ts b/cli/js/web/location.ts
index 862a4c1e49..9b59842b74 100644
--- a/cli/js/web/location.ts
+++ b/cli/js/web/location.ts
@@ -1,7 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { URL } from "./url.ts";
import { notImplemented } from "../util.ts";
-import { DOMStringList, Location } from "./dom_types.ts";
+import { DOMStringList, Location } from "./dom_types.d.ts";
import { getDOMStringList } from "./dom_util.ts";
export class LocationImpl implements Location {
diff --git a/cli/js/web/request.ts b/cli/js/web/request.ts
index 96edaf59e3..6a5d92a2b8 100644
--- a/cli/js/web/request.ts
+++ b/cli/js/web/request.ts
@@ -1,7 +1,7 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import * as headers from "./headers.ts";
import * as body from "./body.ts";
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import * as streams from "./streams/mod.ts";
const { Headers } = headers;
diff --git a/cli/js/web/streams/pipe-to.ts b/cli/js/web/streams/pipe-to.ts
index 1d55792174..17fb8e5bd1 100644
--- a/cli/js/web/streams/pipe-to.ts
+++ b/cli/js/web/streams/pipe-to.ts
@@ -18,7 +18,7 @@
// import { ReadableStreamDefaultReader } from "./readable-stream-default-reader.ts";
// import { WritableStreamDefaultWriter } from "./writable-stream-default-writer.ts";
-// import { PipeOptions } from "../dom_types.ts";
+// import { PipeOptions } from "../dom_types.d.ts";
// import { Err } from "../errors.ts";
// // add a wrapper to handle falsy rejections
diff --git a/cli/js/web/streams/readable-byte-stream-controller.ts b/cli/js/web/streams/readable-byte-stream-controller.ts
index 1b473b77ac..19f2594843 100644
--- a/cli/js/web/streams/readable-byte-stream-controller.ts
+++ b/cli/js/web/streams/readable-byte-stream-controller.ts
@@ -9,7 +9,7 @@ import * as q from "./queue-mixin.ts";
import * as shared from "./shared-internals.ts";
import { ReadableStreamBYOBRequest } from "./readable-stream-byob-request.ts";
import { Queue } from "./queue.ts";
-import { UnderlyingByteSource } from "../dom_types.ts";
+import { UnderlyingByteSource } from "../dom_types.d.ts";
export class ReadableByteStreamController
implements rs.SDReadableByteStreamController {
diff --git a/cli/js/web/streams/readable-internals.ts b/cli/js/web/streams/readable-internals.ts
index f46c798504..571ce50ede 100644
--- a/cli/js/web/streams/readable-internals.ts
+++ b/cli/js/web/streams/readable-internals.ts
@@ -11,7 +11,7 @@ import {
QueuingStrategySizeCallback,
UnderlyingSource,
UnderlyingByteSource,
-} from "../dom_types.ts";
+} from "../dom_types.d.ts";
// ReadableStreamDefaultController
export const controlledReadableStream_ = Symbol("controlledReadableStream_");
diff --git a/cli/js/web/streams/readable-stream-default-controller.ts b/cli/js/web/streams/readable-stream-default-controller.ts
index d33226a9bd..5d07dba530 100644
--- a/cli/js/web/streams/readable-stream-default-controller.ts
+++ b/cli/js/web/streams/readable-stream-default-controller.ts
@@ -8,7 +8,10 @@ import * as rs from "./readable-internals.ts";
import * as shared from "./shared-internals.ts";
import * as q from "./queue-mixin.ts";
import { Queue } from "./queue.ts";
-import { QueuingStrategySizeCallback, UnderlyingSource } from "../dom_types.ts";
+import {
+ QueuingStrategySizeCallback,
+ UnderlyingSource,
+} from "../dom_types.d.ts";
export class ReadableStreamDefaultController
implements rs.SDReadableStreamDefaultController {
diff --git a/cli/js/web/streams/readable-stream.ts b/cli/js/web/streams/readable-stream.ts
index 50753260d3..a003f0a176 100644
--- a/cli/js/web/streams/readable-stream.ts
+++ b/cli/js/web/streams/readable-stream.ts
@@ -12,7 +12,7 @@ import {
QueuingStrategySizeCallback,
UnderlyingSource,
UnderlyingByteSource,
-} from "../dom_types.ts";
+} from "../dom_types.d.ts";
import {
ReadableStreamDefaultController,
diff --git a/cli/js/web/streams/shared-internals.ts b/cli/js/web/streams/shared-internals.ts
index 7b0de22748..db0a082f42 100644
--- a/cli/js/web/streams/shared-internals.ts
+++ b/cli/js/web/streams/shared-internals.ts
@@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// TODO don't disable this warning
-import { AbortSignal, QueuingStrategySizeCallback } from "../dom_types.ts";
+import { AbortSignal, QueuingStrategySizeCallback } from "../dom_types.d.ts";
// common stream fields
diff --git a/cli/js/web/streams/strategies.ts b/cli/js/web/streams/strategies.ts
index 98fe0f91a4..4c5b402c5f 100644
--- a/cli/js/web/streams/strategies.ts
+++ b/cli/js/web/streams/strategies.ts
@@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// TODO reenable this lint here
-import { QueuingStrategy } from "../dom_types.ts";
+import { QueuingStrategy } from "../dom_types.d.ts";
export class ByteLengthQueuingStrategy
implements QueuingStrategy {
diff --git a/cli/js/web/streams/transform-internals.ts b/cli/js/web/streams/transform-internals.ts
index 4c5e3657d6..9c17db8f6c 100644
--- a/cli/js/web/streams/transform-internals.ts
+++ b/cli/js/web/streams/transform-internals.ts
@@ -19,7 +19,7 @@
// import { createReadableStream } from "./readable-stream.ts";
// import { createWritableStream } from "./writable-stream.ts";
-// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.ts";
+// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.d.ts";
// export const state_ = Symbol("transformState_");
// export const backpressure_ = Symbol("backpressure_");
diff --git a/cli/js/web/streams/transform-stream.ts b/cli/js/web/streams/transform-stream.ts
index 090f781358..c27430db1b 100644
--- a/cli/js/web/streams/transform-stream.ts
+++ b/cli/js/web/streams/transform-stream.ts
@@ -17,7 +17,7 @@
// import * as ts from "./transform-internals.ts";
// import * as shared from "./shared-internals.ts";
// import { TransformStreamDefaultController } from "./transform-stream-default-controller.ts";
-// import { QueuingStrategy } from "../dom_types.ts";
+// import { QueuingStrategy } from "../dom_types.d.ts";
// export class TransformStream {
// [ts.backpressure_]: boolean | undefined; // Whether there was backpressure on [[readable]] the last time it was observed
diff --git a/cli/js/web/streams/writable-internals.ts b/cli/js/web/streams/writable-internals.ts
index 78bb19a282..4d442d0f54 100644
--- a/cli/js/web/streams/writable-internals.ts
+++ b/cli/js/web/streams/writable-internals.ts
@@ -15,7 +15,7 @@
// import * as shared from "./shared-internals.ts";
// import * as q from "./queue-mixin.ts";
-// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.ts";
+// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.d.ts";
// export const backpressure_ = Symbol("backpressure_");
// export const closeRequest_ = Symbol("closeRequest_");
diff --git a/cli/js/web/streams/writable-stream-default-controller.ts b/cli/js/web/streams/writable-stream-default-controller.ts
index 57ffe08fda..181edede8f 100644
--- a/cli/js/web/streams/writable-stream-default-controller.ts
+++ b/cli/js/web/streams/writable-stream-default-controller.ts
@@ -16,7 +16,7 @@
// import * as shared from "./shared-internals.ts";
// import * as q from "./queue-mixin.ts";
// import { Queue } from "./queue.ts";
-// import { QueuingStrategySizeCallback } from "../dom_types.ts";
+// import { QueuingStrategySizeCallback } from "../dom_types.d.ts";
// export class WritableStreamDefaultController
// implements ws.WritableStreamDefaultController {
diff --git a/cli/js/web/streams/writable-stream.ts b/cli/js/web/streams/writable-stream.ts
index a6131c5d02..f231d78dc6 100644
--- a/cli/js/web/streams/writable-stream.ts
+++ b/cli/js/web/streams/writable-stream.ts
@@ -16,7 +16,7 @@
// setUpWritableStreamDefaultControllerFromUnderlyingSink
// } from "./writable-stream-default-controller.ts";
// import { WritableStreamDefaultWriter } from "./writable-stream-default-writer.ts";
-// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.ts";
+// import { QueuingStrategy, QueuingStrategySizeCallback } from "../dom_types.d.ts";
// export class WritableStream {
// [shared.state_]: ws.WritableStreamState;
diff --git a/cli/js/web/text_encoding.ts b/cli/js/web/text_encoding.ts
index 6fd498e596..b0630bf950 100644
--- a/cli/js/web/text_encoding.ts
+++ b/cli/js/web/text_encoding.ts
@@ -25,7 +25,7 @@
import * as base64 from "./base64.ts";
import { decodeUtf8 } from "./decode_utf8.ts";
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { core } from "../core.ts";
const CONTINUE = null;
@@ -348,7 +348,7 @@ encodingIndexes.set("windows-1252", [
252,
253,
254,
- 255
+ 255,
]);
for (const [key, index] of encodingIndexes) {
decoders.set(
diff --git a/cli/js/web/url.ts b/cli/js/web/url.ts
index 2b6a0d341d..1a6f4eb9dc 100644
--- a/cli/js/web/url.ts
+++ b/cli/js/web/url.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { customInspect } from "./console.ts";
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { urls, URLSearchParams } from "./url_search_params.ts";
import { getRandomValues } from "../ops/get_random_values.ts";
diff --git a/cli/js/web/url_search_params.ts b/cli/js/web/url_search_params.ts
index aad59bb8c5..0e41bdbf2e 100644
--- a/cli/js/web/url_search_params.ts
+++ b/cli/js/web/url_search_params.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-import * as domTypes from "./dom_types.ts";
+import * as domTypes from "./dom_types.d.ts";
import { URL, parts } from "./url.ts";
import { isIterable, requiredArguments } from "./util.ts";
diff --git a/cli/js/web/util.ts b/cli/js/web/util.ts
index 2d63b4d606..32e73c4433 100644
--- a/cli/js/web/util.ts
+++ b/cli/js/web/util.ts
@@ -11,6 +11,7 @@ export type TypedArray =
| Float32Array
| Float64Array;
+// @internal
export function isTypedArray(x: unknown): x is TypedArray {
return (
x instanceof Int8Array ||
@@ -54,19 +55,8 @@ export function immutableDefine(
});
}
-// Returns values from a WeakMap to emulate private properties in JavaScript
-export function getPrivateValue<
- K extends object,
- V extends object,
- W extends keyof V
->(instance: K, weakMap: WeakMap, key: W): V[W] {
- if (weakMap.has(instance)) {
- return weakMap.get(instance)![key];
- }
- throw new TypeError("Illegal invocation");
-}
-
-export function hasOwnProperty(obj: T, v: PropertyKey): boolean {
+// @internal
+export function hasOwnProperty(obj: unknown, v: PropertyKey): boolean {
if (obj == null) {
return false;
}
@@ -87,3 +77,19 @@ export function isIterable(
typeof ((o as unknown) as Iterable<[P, K]>)[Symbol.iterator] === "function"
);
}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+interface GenericConstructor {
+ prototype: T;
+}
+
+/** A helper function which ensures accessors are enumerable, as they normally
+ * are not. */
+export function defineEnumerableProps(
+ Ctor: GenericConstructor,
+ props: string[]
+): void {
+ for (const prop of props) {
+ Reflect.defineProperty(Ctor.prototype, prop, { enumerable: true });
+ }
+}
diff --git a/cli/js/web/workers.ts b/cli/js/web/workers.ts
index 7a0abbbdb9..054c26193b 100644
--- a/cli/js/web/workers.ts
+++ b/cli/js/web/workers.ts
@@ -11,8 +11,8 @@ import { TextDecoder, TextEncoder } from "./text_encoding.ts";
/*
import { blobURLMap } from "./web/url.ts";
*/
-import { Event } from "./event.ts";
-import { EventTarget } from "./event_target.ts";
+import { EventImpl as Event } from "./event.ts";
+import { EventTargetImpl as EventTarget } from "./event_target.ts";
const encoder = new TextEncoder();
const decoder = new TextDecoder();