diff options
Diffstat (limited to 'op_crates')
-rw-r--r-- | op_crates/web/02_abort_signal.js | 76 | ||||
-rw-r--r-- | op_crates/web/abort_controller_test.js | 75 | ||||
-rw-r--r-- | op_crates/web/lib.deno_web.d.ts | 50 | ||||
-rw-r--r-- | op_crates/web/lib.rs | 19 |
4 files changed, 220 insertions, 0 deletions
diff --git a/op_crates/web/02_abort_signal.js b/op_crates/web/02_abort_signal.js new file mode 100644 index 000000000..908e85ac9 --- /dev/null +++ b/op_crates/web/02_abort_signal.js @@ -0,0 +1,76 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +((window) => { + const add = Symbol("add"); + const signalAbort = Symbol("signalAbort"); + const remove = Symbol("remove"); + + class AbortSignal extends EventTarget { + #aborted = false; + #abortAlgorithms = new Set(); + + [add](algorithm) { + this.#abortAlgorithms.add(algorithm); + } + + [signalAbort]() { + if (this.#aborted) { + return; + } + this.#aborted = true; + for (const algorithm of this.#abortAlgorithms) { + algorithm(); + } + this.#abortAlgorithms.clear(); + this.dispatchEvent(new Event("abort")); + } + + [remove](algorithm) { + this.#abortAlgorithms.delete(algorithm); + } + + constructor() { + super(); + this.onabort = null; + this.addEventListener("abort", (evt) => { + const { onabort } = this; + if (typeof onabort === "function") { + onabort.call(this, evt); + } + }); + } + + get aborted() { + return Boolean(this.#aborted); + } + + get [Symbol.toStringTag]() { + return "AbortSignal"; + } + } + + class AbortController { + #signal = new AbortSignal(); + + get signal() { + return this.#signal; + } + + abort() { + this.#signal[signalAbort](); + } + + get [Symbol.toStringTag]() { + return "AbortController"; + } + } + + window.AbortSignal = AbortSignal; + window.AbortController = AbortController; + window.__bootstrap = window.__bootstrap || {}; + window.__bootstrap.abortSignal = { + add, + signalAbort, + remove, + }; +})(this); diff --git a/op_crates/web/abort_controller_test.js b/op_crates/web/abort_controller_test.js new file mode 100644 index 000000000..a2fb12c65 --- /dev/null +++ b/op_crates/web/abort_controller_test.js @@ -0,0 +1,75 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +function assert(cond) { + if (!cond) { + throw Error("assert"); + } +} + +function assertEquals(left, right) { + assert(left === right); +} + +function basicAbortController() { + controller = new AbortController(); + assert(controller); + const { signal } = controller; + assert(signal); + assertEquals(signal.aborted, false); + controller.abort(); + assertEquals(signal.aborted, true); +} + +function signalCallsOnabort() { + const controller = new AbortController(); + const { signal } = controller; + let called = false; + signal.onabort = (evt) => { + assert(evt); + assertEquals(evt.type, "abort"); + called = true; + }; + controller.abort(); + assert(called); +} + +function signalEventListener() { + const controller = new AbortController(); + const { signal } = controller; + let called = false; + signal.addEventListener("abort", function (ev) { + assert(this === signal); + assertEquals(ev.type, "abort"); + called = true; + }); + controller.abort(); + assert(called); +} + +function onlyAbortsOnce() { + const controller = new AbortController(); + const { signal } = controller; + let called = 0; + signal.addEventListener("abort", () => called++); + signal.onabort = () => { + called++; + }; + controller.abort(); + assertEquals(called, 2); + controller.abort(); + assertEquals(called, 2); +} + +function controllerHasProperToString() { + const actual = Object.prototype.toString.call(new AbortController()); + assertEquals(actual, "[object AbortController]"); +} + +function main() { + basicAbortController(); + signalCallsOnabort(); + signalEventListener(); + onlyAbortsOnce(); + controllerHasProperToString(); +} + +main(); diff --git a/op_crates/web/lib.deno_web.d.ts b/op_crates/web/lib.deno_web.d.ts index b402529e2..d24a3b76f 100644 --- a/op_crates/web/lib.deno_web.d.ts +++ b/op_crates/web/lib.deno_web.d.ts @@ -1,5 +1,7 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +/* eslint-disable @typescript-eslint/no-explicit-any */ + /// <reference no-default-lib="true" /> /// <reference lib="esnext" /> @@ -185,3 +187,51 @@ declare class TextEncoder { ): { read: number; written: number }; readonly [Symbol.toStringTag]: string; } + +/** A controller object that allows you to abort one or more DOM requests as and + * when desired. */ +declare class AbortController { + /** Returns the AbortSignal object associated with this object. */ + readonly signal: AbortSignal; + /** Invoking this method will set this object's AbortSignal's aborted flag and + * signal to any observers that the associated activity is to be aborted. */ + abort(): void; +} + +interface AbortSignalEventMap { + abort: Event; +} + +/** A signal object that allows you to communicate with a DOM request (such as a + * Fetch) and abort it if required via an AbortController object. */ +interface AbortSignal extends EventTarget { + /** Returns true if this AbortSignal's AbortController has signaled to abort, + * and false otherwise. */ + readonly aborted: boolean; + onabort: ((this: AbortSignal, ev: Event) => any) | null; + addEventListener<K extends keyof AbortSignalEventMap>( + type: K, + listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener<K extends keyof AbortSignalEventMap>( + type: K, + listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; +} + +declare const AbortSignal: { + prototype: AbortSignal; + new (): AbortSignal; +}; diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs index 4cfe8d090..03c0c89af 100644 --- a/op_crates/web/lib.rs +++ b/op_crates/web/lib.rs @@ -6,6 +6,7 @@ use std::path::PathBuf; crate_modules!(); pub struct WebScripts { + pub abort_signal: String, pub declaration: String, pub dom_exception: String, pub event: String, @@ -21,6 +22,7 @@ fn get_str_path(file_name: &str) -> String { pub fn get_scripts() -> WebScripts { WebScripts { + abort_signal: get_str_path("02_abort_signal.js"), declaration: get_str_path("lib.deno_web.d.ts"), dom_exception: get_str_path("00_dom_exception.js"), event: get_str_path("01_event.js"), @@ -53,6 +55,9 @@ mod tests { ); js_check(isolate.execute("01_event.js", include_str!("01_event.js"))); js_check( + isolate.execute("02_abort_signal.js", include_str!("02_abort_signal.js")), + ); + js_check( isolate .execute("08_text_encoding.js", include_str!("08_text_encoding.js")), ); @@ -60,6 +65,20 @@ mod tests { } #[test] + fn test_abort_controller() { + run_in_task(|mut cx| { + let mut isolate = setup(); + js_check(isolate.execute( + "abort_controller_test.js", + include_str!("abort_controller_test.js"), + )); + if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + unreachable!(); + } + }); + } + + #[test] fn test_event() { run_in_task(|mut cx| { let mut isolate = setup(); |