mirror of
https://github.com/denoland/deno.git
synced 2025-10-03 07:34:36 +00:00
Op crate for Web APIs (#6906)
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
parent
d7dcbab3ef
commit
41215eb29c
19 changed files with 1159 additions and 344 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -382,6 +382,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"deno_lint",
|
"deno_lint",
|
||||||
|
"deno_web",
|
||||||
"dissimilar",
|
"dissimilar",
|
||||||
"dlopen",
|
"dlopen",
|
||||||
"dprint-plugin-typescript",
|
"dprint-plugin-typescript",
|
||||||
|
@ -459,6 +460,14 @@ dependencies = [
|
||||||
"swc_ecmascript",
|
"swc_ecmascript",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deno_web"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"deno_core",
|
||||||
|
"futures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_deref"
|
name = "derive_deref"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
||||||
"core",
|
"core",
|
||||||
"test_plugin",
|
"test_plugin",
|
||||||
"test_util",
|
"test_util",
|
||||||
|
"op_crates/web",
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"std/hash/_wasm"
|
"std/hash/_wasm"
|
||||||
|
|
|
@ -16,6 +16,7 @@ path = "main.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
deno_core = { path = "../core", version = "0.51.0" }
|
deno_core = { path = "../core", version = "0.51.0" }
|
||||||
|
deno_web = { path = "../op_crates/web", version = "0.1.0" }
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
75
cli/build.rs
75
cli/build.rs
|
@ -38,6 +38,11 @@ fn create_compiler_snapshot(
|
||||||
) {
|
) {
|
||||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||||
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
||||||
|
let web_scripts = deno_web::get_scripts();
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.web.d.ts".to_string(),
|
||||||
|
PathBuf::from(web_scripts.declaration),
|
||||||
|
);
|
||||||
custom_libs.insert(
|
custom_libs.insert(
|
||||||
"lib.deno.window.d.ts".to_string(),
|
"lib.deno.window.d.ts".to_string(),
|
||||||
cwd.join("dts/lib.deno.window.d.ts"),
|
cwd.join("dts/lib.deno.window.d.ts"),
|
||||||
|
@ -80,6 +85,10 @@ fn main() {
|
||||||
// op_fetch_asset::trace_serializer();
|
// op_fetch_asset::trace_serializer();
|
||||||
|
|
||||||
println!("cargo:rustc-env=TS_VERSION={}", ts_version());
|
println!("cargo:rustc-env=TS_VERSION={}", ts_version());
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-env=DENO_WEB_LIB_PATH={}",
|
||||||
|
deno_web::get_scripts().declaration
|
||||||
|
);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"cargo:rustc-env=TARGET={}",
|
"cargo:rustc-env=TARGET={}",
|
||||||
|
@ -93,7 +102,7 @@ fn main() {
|
||||||
let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||||
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
||||||
|
|
||||||
let js_files = get_js_files("rt");
|
let js_files = get_js_files_for_rt();
|
||||||
create_runtime_snapshot(&runtime_snapshot_path, js_files);
|
create_runtime_snapshot(&runtime_snapshot_path, js_files);
|
||||||
|
|
||||||
let js_files = get_js_files("tsc");
|
let js_files = get_js_files("tsc");
|
||||||
|
@ -123,3 +132,67 @@ fn get_js_files(d: &str) -> Vec<String> {
|
||||||
js_files.sort();
|
js_files.sort();
|
||||||
js_files
|
js_files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_js_files_for_rt() -> Vec<String> {
|
||||||
|
let web_scripts = deno_web::get_scripts();
|
||||||
|
|
||||||
|
let f = vec![
|
||||||
|
"rt/00_bootstrap_namespace.js",
|
||||||
|
&web_scripts.dom_exception,
|
||||||
|
"rt/01_build.js",
|
||||||
|
"rt/01_colors.js",
|
||||||
|
"rt/01_errors.js",
|
||||||
|
&web_scripts.event,
|
||||||
|
"rt/01_internals.js",
|
||||||
|
"rt/01_version.js",
|
||||||
|
"rt/01_web_util.js",
|
||||||
|
"rt/02_abort_signal.js",
|
||||||
|
"rt/02_console.js",
|
||||||
|
"rt/03_dom_iterable.js",
|
||||||
|
"rt/06_util.js",
|
||||||
|
&web_scripts.text_encoding,
|
||||||
|
"rt/10_dispatch_json.js",
|
||||||
|
"rt/10_dispatch_minimal.js",
|
||||||
|
"rt/11_crypto.js",
|
||||||
|
"rt/11_resources.js",
|
||||||
|
"rt/11_streams.js",
|
||||||
|
"rt/11_timers.js",
|
||||||
|
"rt/11_url.js",
|
||||||
|
"rt/11_workers.js",
|
||||||
|
"rt/12_io.js",
|
||||||
|
"rt/13_buffer.js",
|
||||||
|
"rt/20_blob.js",
|
||||||
|
"rt/20_headers.js",
|
||||||
|
"rt/20_streams_queuing_strategy.js",
|
||||||
|
"rt/21_dom_file.js",
|
||||||
|
"rt/22_form_data.js",
|
||||||
|
"rt/23_multipart.js",
|
||||||
|
"rt/24_body.js",
|
||||||
|
"rt/25_request.js",
|
||||||
|
"rt/26_fetch.js",
|
||||||
|
"rt/30_files.js",
|
||||||
|
"rt/30_fs.js",
|
||||||
|
"rt/30_metrics.js",
|
||||||
|
"rt/30_net.js",
|
||||||
|
"rt/30_os.js",
|
||||||
|
"rt/40_compiler_api.js",
|
||||||
|
"rt/40_diagnostics.js",
|
||||||
|
"rt/40_error_stack.js",
|
||||||
|
"rt/40_fs_events.js",
|
||||||
|
"rt/40_net_unstable.js",
|
||||||
|
"rt/40_performance.js",
|
||||||
|
"rt/40_permissions.js",
|
||||||
|
"rt/40_plugins.js",
|
||||||
|
"rt/40_process.js",
|
||||||
|
"rt/40_read_file.js",
|
||||||
|
"rt/40_repl.js",
|
||||||
|
"rt/40_signals.js",
|
||||||
|
"rt/40_testing.js",
|
||||||
|
"rt/40_tls.js",
|
||||||
|
"rt/40_tty.js",
|
||||||
|
"rt/40_write_file.js",
|
||||||
|
"rt/90_deno_ns.js",
|
||||||
|
"rt/99_main.js",
|
||||||
|
];
|
||||||
|
f.iter().map(|p| p.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
184
cli/dts/lib.deno.shared_globals.d.ts
vendored
184
cli/dts/lib.deno.shared_globals.d.ts
vendored
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
/// <reference lib="deno.web" />
|
||||||
|
|
||||||
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
|
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
|
||||||
// and: https://webassembly.github.io/spec/web-api/
|
// and: https://webassembly.github.io/spec/web-api/
|
||||||
|
@ -529,12 +530,6 @@ interface DOMStringList {
|
||||||
[index: number]: string;
|
[index: number]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare class DOMException extends Error {
|
|
||||||
constructor(message?: string, name?: string);
|
|
||||||
readonly name: string;
|
|
||||||
readonly message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type BufferSource = ArrayBufferView | ArrayBuffer;
|
type BufferSource = ArrayBufferView | ArrayBuffer;
|
||||||
type BlobPart = BufferSource | Blob | string;
|
type BlobPart = BufferSource | Blob | string;
|
||||||
|
|
||||||
|
@ -1030,46 +1025,6 @@ declare function fetch(
|
||||||
init?: RequestInit,
|
init?: RequestInit,
|
||||||
): Promise<Response>;
|
): Promise<Response>;
|
||||||
|
|
||||||
/** Decodes a string of data which has been encoded using base-64 encoding.
|
|
||||||
*
|
|
||||||
* console.log(atob("aGVsbG8gd29ybGQ=")); // outputs 'hello world'
|
|
||||||
*/
|
|
||||||
declare function atob(s: string): string;
|
|
||||||
|
|
||||||
/** Creates a base-64 ASCII encoded string from the input string.
|
|
||||||
*
|
|
||||||
* console.log(btoa("hello world")); // outputs "aGVsbG8gd29ybGQ="
|
|
||||||
*/
|
|
||||||
declare function btoa(s: string): string;
|
|
||||||
|
|
||||||
declare class TextDecoder {
|
|
||||||
/** Returns encoding's name, lowercased. */
|
|
||||||
readonly encoding: string;
|
|
||||||
/** Returns `true` if error mode is "fatal", and `false` otherwise. */
|
|
||||||
readonly fatal: boolean;
|
|
||||||
/** Returns `true` if ignore BOM flag is set, and `false` otherwise. */
|
|
||||||
readonly ignoreBOM = false;
|
|
||||||
constructor(
|
|
||||||
label?: string,
|
|
||||||
options?: { fatal?: boolean; ignoreBOM?: boolean },
|
|
||||||
);
|
|
||||||
/** Returns the result of running encoding's decoder. */
|
|
||||||
decode(input?: BufferSource, options?: { stream?: false }): string;
|
|
||||||
readonly [Symbol.toStringTag]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class TextEncoder {
|
|
||||||
/** Returns "utf-8". */
|
|
||||||
readonly encoding = "utf-8";
|
|
||||||
/** Returns the result of running UTF-8's encoder. */
|
|
||||||
encode(input?: string): Uint8Array;
|
|
||||||
encodeInto(
|
|
||||||
input: string,
|
|
||||||
dest: Uint8Array,
|
|
||||||
): { read: number; written: number };
|
|
||||||
readonly [Symbol.toStringTag]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface URLSearchParams {
|
interface URLSearchParams {
|
||||||
/** Appends a specified key/value pair as a new search parameter.
|
/** Appends a specified key/value pair as a new search parameter.
|
||||||
*
|
*
|
||||||
|
@ -1444,143 +1399,6 @@ declare class PerformanceMeasure extends PerformanceEntry {
|
||||||
readonly entryType: "measure";
|
readonly entryType: "measure";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventInit {
|
|
||||||
bubbles?: boolean;
|
|
||||||
cancelable?: boolean;
|
|
||||||
composed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* The options argument sets listener-specific options. For compatibility this
|
|
||||||
* can be a boolean, in which case the method behaves exactly as if the value
|
|
||||||
* was specified as options's capture.
|
|
||||||
*
|
|
||||||
* When set to true, options's capture prevents callback from being invoked
|
|
||||||
* when the event's eventPhase attribute value is BUBBLING_PHASE. When false
|
|
||||||
* (or not present), callback will not be invoked when event's eventPhase
|
|
||||||
* attribute value is CAPTURING_PHASE. Either way, callback will be invoked if
|
|
||||||
* event's eventPhase attribute value is AT_TARGET.
|
|
||||||
*
|
|
||||||
* When set to true, options's passive indicates that the callback will not
|
|
||||||
* cancel the event by invoking preventDefault(). This is used to enable
|
|
||||||
* performance optimizations described in § 2.8 Observing event listeners.
|
|
||||||
*
|
|
||||||
* When set to true, options's once indicates that the callback will only be
|
|
||||||
* 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. */
|
|
||||||
addEventListener(
|
|
||||||
type: string,
|
|
||||||
listener: EventListenerOrEventListenerObject | null,
|
|
||||||
options?: boolean | AddEventListenerOptions,
|
|
||||||
): void;
|
|
||||||
/** 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. */
|
|
||||||
dispatchEvent(event: Event): boolean;
|
|
||||||
/** 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventListener {
|
|
||||||
(evt: Event): void | Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventListenerObject {
|
|
||||||
handleEvent(evt: Event): void | Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare type EventListenerOrEventListenerObject =
|
|
||||||
| EventListener
|
|
||||||
| EventListenerObject;
|
|
||||||
|
|
||||||
interface AddEventListenerOptions extends EventListenerOptions {
|
|
||||||
once?: boolean;
|
|
||||||
passive?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventListenerOptions {
|
|
||||||
capture?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Events measuring progress of an underlying process, like an HTTP request
|
/** Events measuring progress of an underlying process, like an HTTP request
|
||||||
* (for an XMLHttpRequest, or the loading of the underlying resource of an
|
* (for an XMLHttpRequest, or the loading of the underlying resource of an
|
||||||
* <img>, <audio>, <video>, <style> or <link>). */
|
* <img>, <audio>, <video>, <style> or <link>). */
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub static CLI_SNAPSHOT: &[u8] =
|
||||||
pub static COMPILER_SNAPSHOT: &[u8] =
|
pub static COMPILER_SNAPSHOT: &[u8] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin"));
|
||||||
pub static DENO_NS_LIB: &str = include_str!("dts/lib.deno.ns.d.ts");
|
pub static DENO_NS_LIB: &str = include_str!("dts/lib.deno.ns.d.ts");
|
||||||
|
pub static DENO_WEB_LIB: &str = include_str!(env!("DENO_WEB_LIB_PATH"));
|
||||||
pub static SHARED_GLOBALS_LIB: &str =
|
pub static SHARED_GLOBALS_LIB: &str =
|
||||||
include_str!("dts/lib.deno.shared_globals.d.ts");
|
include_str!("dts/lib.deno.shared_globals.d.ts");
|
||||||
pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts");
|
pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts");
|
||||||
|
|
|
@ -287,8 +287,9 @@ async fn print_file_info(
|
||||||
|
|
||||||
fn get_types(unstable: bool) -> String {
|
fn get_types(unstable: bool) -> String {
|
||||||
let mut types = format!(
|
let mut types = format!(
|
||||||
"{}\n{}\n{}",
|
"{}\n{}\n{}\n{}",
|
||||||
crate::js::DENO_NS_LIB,
|
crate::js::DENO_NS_LIB,
|
||||||
|
crate::js::DENO_WEB_LIB,
|
||||||
crate::js::SHARED_GLOBALS_LIB,
|
crate::js::SHARED_GLOBALS_LIB,
|
||||||
crate::js::WINDOW_LIB,
|
crate::js::WINDOW_LIB,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,157 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Forked from https://github.com/beatgammit/base64-js
|
|
||||||
// Copyright (c) 2014 Jameson Little. MIT License.
|
|
||||||
|
|
||||||
((window) => {
|
|
||||||
const lookup = [];
|
|
||||||
const revLookup = [];
|
|
||||||
|
|
||||||
const code =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
for (let i = 0, len = code.length; i < len; ++i) {
|
|
||||||
lookup[i] = code[i];
|
|
||||||
revLookup[code.charCodeAt(i)] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support decoding URL-safe base64 strings, as Node.js does.
|
|
||||||
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
|
||||||
revLookup["-".charCodeAt(0)] = 62;
|
|
||||||
revLookup["_".charCodeAt(0)] = 63;
|
|
||||||
|
|
||||||
function getLens(b64) {
|
|
||||||
const len = b64.length;
|
|
||||||
|
|
||||||
if (len % 4 > 0) {
|
|
||||||
throw new Error("Invalid string. Length must be a multiple of 4");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim off extra bytes after placeholder bytes are found
|
|
||||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
||||||
let validLen = b64.indexOf("=");
|
|
||||||
if (validLen === -1) validLen = len;
|
|
||||||
|
|
||||||
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
|
|
||||||
|
|
||||||
return [validLen, placeHoldersLen];
|
|
||||||
}
|
|
||||||
|
|
||||||
// base64 is 4/3 + up to two characters of the original data
|
|
||||||
function byteLength(b64) {
|
|
||||||
const lens = getLens(b64);
|
|
||||||
const validLen = lens[0];
|
|
||||||
const placeHoldersLen = lens[1];
|
|
||||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _byteLength(
|
|
||||||
b64,
|
|
||||||
validLen,
|
|
||||||
placeHoldersLen,
|
|
||||||
) {
|
|
||||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toByteArray(b64) {
|
|
||||||
let tmp;
|
|
||||||
const lens = getLens(b64);
|
|
||||||
const validLen = lens[0];
|
|
||||||
const placeHoldersLen = lens[1];
|
|
||||||
|
|
||||||
const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen));
|
|
||||||
|
|
||||||
let curByte = 0;
|
|
||||||
|
|
||||||
// if there are placeholders, only get up to the last complete 4 chars
|
|
||||||
const len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
|
||||||
|
|
||||||
let i;
|
|
||||||
for (i = 0; i < len; i += 4) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 18) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
|
||||||
revLookup[b64.charCodeAt(i + 3)];
|
|
||||||
arr[curByte++] = (tmp >> 16) & 0xff;
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 2) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 2) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] >> 4);
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 1) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 10) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] >> 2);
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tripletToBase64(num) {
|
|
||||||
return (
|
|
||||||
lookup[(num >> 18) & 0x3f] +
|
|
||||||
lookup[(num >> 12) & 0x3f] +
|
|
||||||
lookup[(num >> 6) & 0x3f] +
|
|
||||||
lookup[num & 0x3f]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeChunk(uint8, start, end) {
|
|
||||||
let tmp;
|
|
||||||
const output = [];
|
|
||||||
for (let i = start; i < end; i += 3) {
|
|
||||||
tmp = ((uint8[i] << 16) & 0xff0000) +
|
|
||||||
((uint8[i + 1] << 8) & 0xff00) +
|
|
||||||
(uint8[i + 2] & 0xff);
|
|
||||||
output.push(tripletToBase64(tmp));
|
|
||||||
}
|
|
||||||
return output.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromByteArray(uint8) {
|
|
||||||
let tmp;
|
|
||||||
const len = uint8.length;
|
|
||||||
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
|
||||||
const parts = [];
|
|
||||||
const maxChunkLength = 16383; // must be multiple of 3
|
|
||||||
|
|
||||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
||||||
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
||||||
parts.push(
|
|
||||||
encodeChunk(
|
|
||||||
uint8,
|
|
||||||
i,
|
|
||||||
i + maxChunkLength > len2 ? len2 : i + maxChunkLength,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
||||||
if (extraBytes === 1) {
|
|
||||||
tmp = uint8[len - 1];
|
|
||||||
parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + "==");
|
|
||||||
} else if (extraBytes === 2) {
|
|
||||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
|
|
||||||
parts.push(
|
|
||||||
lookup[tmp >> 10] +
|
|
||||||
lookup[(tmp >> 4) & 0x3f] +
|
|
||||||
lookup[(tmp << 2) & 0x3f] +
|
|
||||||
"=",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.base64 = {
|
|
||||||
byteLength,
|
|
||||||
toByteArray,
|
|
||||||
fromByteArray,
|
|
||||||
};
|
|
||||||
})(this);
|
|
|
@ -1,5 +1,5 @@
|
||||||
[WILDCARD]
|
[WILDCARD]
|
||||||
Files: 43
|
Files: 44
|
||||||
Nodes: [WILDCARD]
|
Nodes: [WILDCARD]
|
||||||
Identifiers: [WILDCARD]
|
Identifiers: [WILDCARD]
|
||||||
Symbols: [WILDCARD]
|
Symbols: [WILDCARD]
|
||||||
|
|
|
@ -776,6 +776,7 @@ delete Object.prototype.__proto__;
|
||||||
// as these are internal APIs of TypeScript which maintain valid libs
|
// as these are internal APIs of TypeScript which maintain valid libs
|
||||||
ts.libs.push("deno.ns", "deno.window", "deno.worker", "deno.shared_globals");
|
ts.libs.push("deno.ns", "deno.window", "deno.worker", "deno.shared_globals");
|
||||||
ts.libMap.set("deno.ns", "lib.deno.ns.d.ts");
|
ts.libMap.set("deno.ns", "lib.deno.ns.d.ts");
|
||||||
|
ts.libMap.set("deno.web", "lib.deno.web.d.ts");
|
||||||
ts.libMap.set("deno.window", "lib.deno.window.d.ts");
|
ts.libMap.set("deno.window", "lib.deno.window.d.ts");
|
||||||
ts.libMap.set("deno.worker", "lib.deno.worker.d.ts");
|
ts.libMap.set("deno.worker", "lib.deno.worker.d.ts");
|
||||||
ts.libMap.set("deno.shared_globals", "lib.deno.shared_globals.d.ts");
|
ts.libMap.set("deno.shared_globals", "lib.deno.shared_globals.d.ts");
|
||||||
|
@ -787,6 +788,10 @@ delete Object.prototype.__proto__;
|
||||||
`${ASSETS}/lib.deno.ns.d.ts`,
|
`${ASSETS}/lib.deno.ns.d.ts`,
|
||||||
ts.ScriptTarget.ESNext,
|
ts.ScriptTarget.ESNext,
|
||||||
);
|
);
|
||||||
|
SNAPSHOT_HOST.getSourceFile(
|
||||||
|
`${ASSETS}/lib.deno.web.d.ts`,
|
||||||
|
ts.ScriptTarget.ESNext,
|
||||||
|
);
|
||||||
SNAPSHOT_HOST.getSourceFile(
|
SNAPSHOT_HOST.getSourceFile(
|
||||||
`${ASSETS}/lib.deno.window.d.ts`,
|
`${ASSETS}/lib.deno.window.d.ts`,
|
||||||
ts.ScriptTarget.ESNext,
|
ts.ScriptTarget.ESNext,
|
||||||
|
|
|
@ -1038,6 +1038,10 @@
|
||||||
window.EventTarget = EventTarget;
|
window.EventTarget = EventTarget;
|
||||||
window.ErrorEvent = ErrorEvent;
|
window.ErrorEvent = ErrorEvent;
|
||||||
window.CustomEvent = CustomEvent;
|
window.CustomEvent = CustomEvent;
|
||||||
|
window.dispatchEvent = EventTarget.prototype.dispatchEvent;
|
||||||
|
window.addEventListener = EventTarget.prototype.addEventListener;
|
||||||
|
window.removeEventListener = EventTarget.prototype.removeEventListener;
|
||||||
|
window.__bootstrap = (window.__bootstrap || {});
|
||||||
window.__bootstrap.eventTarget = {
|
window.__bootstrap.eventTarget = {
|
||||||
setEventTargetData,
|
setEventTargetData,
|
||||||
};
|
};
|
|
@ -26,7 +26,6 @@
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = Deno.core;
|
const core = Deno.core;
|
||||||
const base64 = window.__bootstrap.base64;
|
|
||||||
|
|
||||||
const CONTINUE = null;
|
const CONTINUE = null;
|
||||||
const END_OF_STREAM = -1;
|
const END_OF_STREAM = -1;
|
||||||
|
@ -679,6 +678,159 @@
|
||||||
return outString;
|
return outString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Following code is forked from https://github.com/beatgammit/base64-js
|
||||||
|
// Copyright (c) 2014 Jameson Little. MIT License.
|
||||||
|
const lookup = [];
|
||||||
|
const revLookup = [];
|
||||||
|
|
||||||
|
const code =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
for (let i = 0, len = code.length; i < len; ++i) {
|
||||||
|
lookup[i] = code[i];
|
||||||
|
revLookup[code.charCodeAt(i)] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support decoding URL-safe base64 strings, as Node.js does.
|
||||||
|
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
||||||
|
revLookup["-".charCodeAt(0)] = 62;
|
||||||
|
revLookup["_".charCodeAt(0)] = 63;
|
||||||
|
|
||||||
|
function getLens(b64) {
|
||||||
|
const len = b64.length;
|
||||||
|
|
||||||
|
if (len % 4 > 0) {
|
||||||
|
throw new Error("Invalid string. Length must be a multiple of 4");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim off extra bytes after placeholder bytes are found
|
||||||
|
// See: https://github.com/beatgammit/base64-js/issues/42
|
||||||
|
let validLen = b64.indexOf("=");
|
||||||
|
if (validLen === -1) validLen = len;
|
||||||
|
|
||||||
|
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
|
||||||
|
|
||||||
|
return [validLen, placeHoldersLen];
|
||||||
|
}
|
||||||
|
|
||||||
|
// base64 is 4/3 + up to two characters of the original data
|
||||||
|
function byteLength(b64) {
|
||||||
|
const lens = getLens(b64);
|
||||||
|
const validLen = lens[0];
|
||||||
|
const placeHoldersLen = lens[1];
|
||||||
|
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _byteLength(
|
||||||
|
b64,
|
||||||
|
validLen,
|
||||||
|
placeHoldersLen,
|
||||||
|
) {
|
||||||
|
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toByteArray(b64) {
|
||||||
|
let tmp;
|
||||||
|
const lens = getLens(b64);
|
||||||
|
const validLen = lens[0];
|
||||||
|
const placeHoldersLen = lens[1];
|
||||||
|
|
||||||
|
const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen));
|
||||||
|
|
||||||
|
let curByte = 0;
|
||||||
|
|
||||||
|
// if there are placeholders, only get up to the last complete 4 chars
|
||||||
|
const len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
||||||
|
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < len; i += 4) {
|
||||||
|
tmp = (revLookup[b64.charCodeAt(i)] << 18) |
|
||||||
|
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
||||||
|
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
||||||
|
revLookup[b64.charCodeAt(i + 3)];
|
||||||
|
arr[curByte++] = (tmp >> 16) & 0xff;
|
||||||
|
arr[curByte++] = (tmp >> 8) & 0xff;
|
||||||
|
arr[curByte++] = tmp & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeHoldersLen === 2) {
|
||||||
|
tmp = (revLookup[b64.charCodeAt(i)] << 2) |
|
||||||
|
(revLookup[b64.charCodeAt(i + 1)] >> 4);
|
||||||
|
arr[curByte++] = tmp & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeHoldersLen === 1) {
|
||||||
|
tmp = (revLookup[b64.charCodeAt(i)] << 10) |
|
||||||
|
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
||||||
|
(revLookup[b64.charCodeAt(i + 2)] >> 2);
|
||||||
|
arr[curByte++] = (tmp >> 8) & 0xff;
|
||||||
|
arr[curByte++] = tmp & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tripletToBase64(num) {
|
||||||
|
return (
|
||||||
|
lookup[(num >> 18) & 0x3f] +
|
||||||
|
lookup[(num >> 12) & 0x3f] +
|
||||||
|
lookup[(num >> 6) & 0x3f] +
|
||||||
|
lookup[num & 0x3f]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeChunk(uint8, start, end) {
|
||||||
|
let tmp;
|
||||||
|
const output = [];
|
||||||
|
for (let i = start; i < end; i += 3) {
|
||||||
|
tmp = ((uint8[i] << 16) & 0xff0000) +
|
||||||
|
((uint8[i + 1] << 8) & 0xff00) +
|
||||||
|
(uint8[i + 2] & 0xff);
|
||||||
|
output.push(tripletToBase64(tmp));
|
||||||
|
}
|
||||||
|
return output.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromByteArray(uint8) {
|
||||||
|
let tmp;
|
||||||
|
const len = uint8.length;
|
||||||
|
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
||||||
|
const parts = [];
|
||||||
|
const maxChunkLength = 16383; // must be multiple of 3
|
||||||
|
|
||||||
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
||||||
|
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
||||||
|
parts.push(
|
||||||
|
encodeChunk(
|
||||||
|
uint8,
|
||||||
|
i,
|
||||||
|
i + maxChunkLength > len2 ? len2 : i + maxChunkLength,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
||||||
|
if (extraBytes === 1) {
|
||||||
|
tmp = uint8[len - 1];
|
||||||
|
parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + "==");
|
||||||
|
} else if (extraBytes === 2) {
|
||||||
|
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
|
||||||
|
parts.push(
|
||||||
|
lookup[tmp >> 10] +
|
||||||
|
lookup[(tmp >> 4) & 0x3f] +
|
||||||
|
lookup[(tmp << 2) & 0x3f] +
|
||||||
|
"=",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64 = {
|
||||||
|
byteLength,
|
||||||
|
toByteArray,
|
||||||
|
fromByteArray,
|
||||||
|
};
|
||||||
|
|
||||||
window.TextEncoder = TextEncoder;
|
window.TextEncoder = TextEncoder;
|
||||||
window.TextDecoder = TextDecoder;
|
window.TextDecoder = TextDecoder;
|
||||||
window.atob = atob;
|
window.atob = atob;
|
20
op_crates/web/Cargo.toml
Normal file
20
op_crates/web/Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "deno_web"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
description = "Collection of Web APIs"
|
||||||
|
authors = ["the Deno authors"]
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/denoland/deno"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
deno_core = { version = "0.51.0", path = "../../core" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
futures = "0.3.5"
|
244
op_crates/web/event_target_test.js
Normal file
244
op_crates/web/event_target_test.js
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
function assert(cond) {
|
||||||
|
if (!cond) {
|
||||||
|
throw Error("assert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEventListenerTest() {
|
||||||
|
const document = new EventTarget();
|
||||||
|
|
||||||
|
assert(document.addEventListener("x", null, false) === undefined);
|
||||||
|
assert(document.addEventListener("x", null, true) === undefined);
|
||||||
|
assert(document.addEventListener("x", null) === undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructedEventTargetCanBeUsedAsExpected() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = (e) => {
|
||||||
|
assert(e === event);
|
||||||
|
++callCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
target.addEventListener("foo", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
|
||||||
|
target.removeEventListener("foo", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function anEventTargetCanBeSubclassed() {
|
||||||
|
class NicerEventTarget extends EventTarget {
|
||||||
|
on(
|
||||||
|
type,
|
||||||
|
callback,
|
||||||
|
options,
|
||||||
|
) {
|
||||||
|
this.addEventListener(type, callback, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
off(
|
||||||
|
type,
|
||||||
|
callback,
|
||||||
|
options,
|
||||||
|
) {
|
||||||
|
this.removeEventListener(type, callback, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = new NicerEventTarget();
|
||||||
|
new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = () => {
|
||||||
|
++callCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
target.on("foo", listener);
|
||||||
|
assert(callCount === 0);
|
||||||
|
|
||||||
|
target.off("foo", listener);
|
||||||
|
assert(callCount === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removingNullEventListenerShouldSucceed() {
|
||||||
|
const document = new EventTarget();
|
||||||
|
assert(document.removeEventListener("x", null, false) === undefined);
|
||||||
|
assert(document.removeEventListener("x", null, true) === undefined);
|
||||||
|
assert(document.removeEventListener("x", null) === undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
function constructedEventTargetUseObjectPrototype() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("toString", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = (e) => {
|
||||||
|
assert(e === event);
|
||||||
|
++callCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
target.addEventListener("toString", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
|
||||||
|
target.removeEventListener("toString", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toStringShouldBeWebCompatible() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
assert(target.toString() === "[object EventTarget]");
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchEventShouldNotThrowError() {
|
||||||
|
let hasThrown = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("hasOwnProperty", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: false,
|
||||||
|
});
|
||||||
|
const listener = () => {};
|
||||||
|
target.addEventListener("hasOwnProperty", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
} catch {
|
||||||
|
hasThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(hasThrown === false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetThisShouldDefaultToWindow() {
|
||||||
|
const {
|
||||||
|
addEventListener,
|
||||||
|
dispatchEvent,
|
||||||
|
removeEventListener,
|
||||||
|
} = EventTarget.prototype;
|
||||||
|
let n = 1;
|
||||||
|
const event = new Event("hello");
|
||||||
|
const listener = () => {
|
||||||
|
n = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener("hello", listener);
|
||||||
|
globalThis.dispatchEvent(event);
|
||||||
|
assert(n === 2);
|
||||||
|
n = 1;
|
||||||
|
removeEventListener("hello", listener);
|
||||||
|
globalThis.dispatchEvent(event);
|
||||||
|
assert(n === 1);
|
||||||
|
|
||||||
|
globalThis.addEventListener("hello", listener);
|
||||||
|
dispatchEvent(event);
|
||||||
|
assert(n === 2);
|
||||||
|
n = 1;
|
||||||
|
globalThis.removeEventListener("hello", listener);
|
||||||
|
dispatchEvent(event);
|
||||||
|
assert(n === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetShouldAcceptEventListenerObject() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = {
|
||||||
|
handleEvent(e) {
|
||||||
|
assert(e === event);
|
||||||
|
++callCount;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
target.addEventListener("foo", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
|
||||||
|
target.removeEventListener("foo", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetShouldAcceptAsyncFunction() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = (e) => {
|
||||||
|
assert(e === event);
|
||||||
|
++callCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
target.addEventListener("foo", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
|
||||||
|
target.removeEventListener("foo", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventTargetShouldAcceptAsyncFunctionForEventListenerObject() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const listener = {
|
||||||
|
handleEvent(e) {
|
||||||
|
assert(e === event);
|
||||||
|
++callCount;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
target.addEventListener("foo", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
|
||||||
|
target.removeEventListener("foo", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assert(callCount === 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
globalThis.__bootstrap.eventTarget.setEventTargetData(globalThis);
|
||||||
|
addEventListenerTest();
|
||||||
|
constructedEventTargetCanBeUsedAsExpected();
|
||||||
|
anEventTargetCanBeSubclassed();
|
||||||
|
removingNullEventListenerShouldSucceed();
|
||||||
|
constructedEventTargetUseObjectPrototype();
|
||||||
|
toStringShouldBeWebCompatible();
|
||||||
|
dispatchEventShouldNotThrowError();
|
||||||
|
eventTargetThisShouldDefaultToWindow();
|
||||||
|
eventTargetShouldAcceptEventListenerObject();
|
||||||
|
eventTargetShouldAcceptAsyncFunction();
|
||||||
|
eventTargetShouldAcceptAsyncFunctionForEventListenerObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
111
op_crates/web/event_test.js
Normal file
111
op_crates/web/event_test.js
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
function assert(cond) {
|
||||||
|
if (!cond) {
|
||||||
|
throw Error("assert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventInitializedWithType() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assert(event.isTrusted === false);
|
||||||
|
assert(event.target === null);
|
||||||
|
assert(event.currentTarget === null);
|
||||||
|
assert(event.type === "click");
|
||||||
|
assert(event.bubbles === false);
|
||||||
|
assert(event.cancelable === false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventInitializedWithTypeAndDict() {
|
||||||
|
const init = "submit";
|
||||||
|
const eventInit = { bubbles: true, cancelable: true };
|
||||||
|
const event = new Event(init, eventInit);
|
||||||
|
|
||||||
|
assert(event.isTrusted === false);
|
||||||
|
assert(event.target === null);
|
||||||
|
assert(event.currentTarget === null);
|
||||||
|
assert(event.type === "submit");
|
||||||
|
assert(event.bubbles === true);
|
||||||
|
assert(event.cancelable === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventComposedPathSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
const composedPath = event.composedPath();
|
||||||
|
|
||||||
|
assert(composedPath.length === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventStopPropagationSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assert(event.cancelBubble === false);
|
||||||
|
event.stopPropagation();
|
||||||
|
assert(event.cancelBubble === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventStopImmediatePropagationSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assert(event.cancelBubble === false);
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
assert(event.cancelBubble === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventPreventDefaultSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assert(event.defaultPrevented === false);
|
||||||
|
event.preventDefault();
|
||||||
|
assert(event.defaultPrevented === false);
|
||||||
|
|
||||||
|
const eventInit = { bubbles: true, cancelable: true };
|
||||||
|
const cancelableEvent = new Event(type, eventInit);
|
||||||
|
assert(cancelableEvent.defaultPrevented === false);
|
||||||
|
cancelableEvent.preventDefault();
|
||||||
|
assert(cancelableEvent.defaultPrevented === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eventInitializedWithNonStringType() {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const type = undefined;
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assert(event.isTrusted === false);
|
||||||
|
assert(event.target === null);
|
||||||
|
assert(event.currentTarget === null);
|
||||||
|
assert(event.type === "undefined");
|
||||||
|
assert(event.bubbles === false);
|
||||||
|
assert(event.cancelable === false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref https://github.com/web-platform-tests/wpt/blob/master/dom/events/Event-isTrusted.any.js
|
||||||
|
function eventIsTrusted() {
|
||||||
|
const desc1 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted");
|
||||||
|
assert(desc1);
|
||||||
|
assert(typeof desc1.get === "function");
|
||||||
|
|
||||||
|
const desc2 = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted");
|
||||||
|
assert(desc2);
|
||||||
|
assert(typeof desc2.get === "function");
|
||||||
|
|
||||||
|
assert(desc1.get === desc2.get);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
eventInitializedWithType();
|
||||||
|
eventInitializedWithTypeAndDict();
|
||||||
|
eventComposedPathSuccess();
|
||||||
|
eventStopPropagationSuccess();
|
||||||
|
eventStopImmediatePropagationSuccess();
|
||||||
|
eventPreventDefaultSuccess();
|
||||||
|
eventInitializedWithNonStringType();
|
||||||
|
eventIsTrusted();
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
187
op_crates/web/lib.deno_web.d.ts
vendored
Normal file
187
op_crates/web/lib.deno_web.d.ts
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
/// <reference no-default-lib="true" />
|
||||||
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
|
declare class DOMException extends Error {
|
||||||
|
constructor(message?: string, name?: string);
|
||||||
|
readonly name: string;
|
||||||
|
readonly message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventInit {
|
||||||
|
bubbles?: boolean;
|
||||||
|
cancelable?: boolean;
|
||||||
|
composed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* The options argument sets listener-specific options. For compatibility this
|
||||||
|
* can be a boolean, in which case the method behaves exactly as if the value
|
||||||
|
* was specified as options's capture.
|
||||||
|
*
|
||||||
|
* When set to true, options's capture prevents callback from being invoked
|
||||||
|
* when the event's eventPhase attribute value is BUBBLING_PHASE. When false
|
||||||
|
* (or not present), callback will not be invoked when event's eventPhase
|
||||||
|
* attribute value is CAPTURING_PHASE. Either way, callback will be invoked if
|
||||||
|
* event's eventPhase attribute value is AT_TARGET.
|
||||||
|
*
|
||||||
|
* When set to true, options's passive indicates that the callback will not
|
||||||
|
* cancel the event by invoking preventDefault(). This is used to enable
|
||||||
|
* performance optimizations described in § 2.8 Observing event listeners.
|
||||||
|
*
|
||||||
|
* When set to true, options's once indicates that the callback will only be
|
||||||
|
* 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. */
|
||||||
|
addEventListener(
|
||||||
|
type: string,
|
||||||
|
listener: EventListenerOrEventListenerObject | null,
|
||||||
|
options?: boolean | AddEventListenerOptions,
|
||||||
|
): void;
|
||||||
|
/** 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. */
|
||||||
|
dispatchEvent(event: Event): boolean;
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventListener {
|
||||||
|
(evt: Event): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventListenerObject {
|
||||||
|
handleEvent(evt: Event): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare type EventListenerOrEventListenerObject =
|
||||||
|
| EventListener
|
||||||
|
| EventListenerObject;
|
||||||
|
|
||||||
|
interface AddEventListenerOptions extends EventListenerOptions {
|
||||||
|
once?: boolean;
|
||||||
|
passive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventListenerOptions {
|
||||||
|
capture?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Decodes a string of data which has been encoded using base-64 encoding.
|
||||||
|
*
|
||||||
|
* console.log(atob("aGVsbG8gd29ybGQ=")); // outputs 'hello world'
|
||||||
|
*/
|
||||||
|
declare function atob(s: string): string;
|
||||||
|
|
||||||
|
/** Creates a base-64 ASCII encoded string from the input string.
|
||||||
|
*
|
||||||
|
* console.log(btoa("hello world")); // outputs "aGVsbG8gd29ybGQ="
|
||||||
|
*/
|
||||||
|
declare function btoa(s: string): string;
|
||||||
|
|
||||||
|
declare class TextDecoder {
|
||||||
|
/** Returns encoding's name, lowercased. */
|
||||||
|
readonly encoding: string;
|
||||||
|
/** Returns `true` if error mode is "fatal", and `false` otherwise. */
|
||||||
|
readonly fatal: boolean;
|
||||||
|
/** Returns `true` if ignore BOM flag is set, and `false` otherwise. */
|
||||||
|
readonly ignoreBOM = false;
|
||||||
|
constructor(
|
||||||
|
label?: string,
|
||||||
|
options?: { fatal?: boolean; ignoreBOM?: boolean },
|
||||||
|
);
|
||||||
|
/** Returns the result of running encoding's decoder. */
|
||||||
|
decode(input?: BufferSource, options?: { stream?: false }): string;
|
||||||
|
readonly [Symbol.toStringTag]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare class TextEncoder {
|
||||||
|
/** Returns "utf-8". */
|
||||||
|
readonly encoding = "utf-8";
|
||||||
|
/** Returns the result of running UTF-8's encoder. */
|
||||||
|
encode(input?: string): Uint8Array;
|
||||||
|
encodeInto(
|
||||||
|
input: string,
|
||||||
|
dest: Uint8Array,
|
||||||
|
): { read: number; written: number };
|
||||||
|
readonly [Symbol.toStringTag]: string;
|
||||||
|
}
|
102
op_crates/web/lib.rs
Normal file
102
op_crates/web/lib.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::crate_modules;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
crate_modules!();
|
||||||
|
|
||||||
|
pub struct WebScripts {
|
||||||
|
pub declaration: String,
|
||||||
|
pub dom_exception: String,
|
||||||
|
pub event: String,
|
||||||
|
pub text_encoding: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_str_path(file_name: &str) -> String {
|
||||||
|
PathBuf::from(DENO_CRATE_PATH)
|
||||||
|
.join(file_name)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_scripts() -> WebScripts {
|
||||||
|
WebScripts {
|
||||||
|
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"),
|
||||||
|
text_encoding: get_str_path("08_text_encoding.js"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use deno_core::js_check;
|
||||||
|
use deno_core::CoreIsolate;
|
||||||
|
use deno_core::StartupData;
|
||||||
|
use futures::future::lazy;
|
||||||
|
use futures::future::FutureExt;
|
||||||
|
use futures::task::Context;
|
||||||
|
use futures::task::Poll;
|
||||||
|
|
||||||
|
fn run_in_task<F>(f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Context) + Send + 'static,
|
||||||
|
{
|
||||||
|
futures::executor::block_on(lazy(move |cx| f(cx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup() -> CoreIsolate {
|
||||||
|
let mut isolate = CoreIsolate::new(StartupData::None, false);
|
||||||
|
js_check(
|
||||||
|
isolate
|
||||||
|
.execute("00_dom_exception.js", include_str!("00_dom_exception.js")),
|
||||||
|
);
|
||||||
|
js_check(isolate.execute("01_event.js", include_str!("01_event.js")));
|
||||||
|
js_check(
|
||||||
|
isolate
|
||||||
|
.execute("08_text_encoding.js", include_str!("08_text_encoding.js")),
|
||||||
|
);
|
||||||
|
isolate
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_event() {
|
||||||
|
run_in_task(|mut cx| {
|
||||||
|
let mut isolate = setup();
|
||||||
|
js_check(isolate.execute("event_test.js", include_str!("event_test.js")));
|
||||||
|
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_event_target() {
|
||||||
|
run_in_task(|mut cx| {
|
||||||
|
let mut isolate = setup();
|
||||||
|
js_check(
|
||||||
|
isolate.execute(
|
||||||
|
"event_target_test.js",
|
||||||
|
include_str!("event_target_test.js"),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_text_encoding() {
|
||||||
|
run_in_task(|mut cx| {
|
||||||
|
let mut isolate = setup();
|
||||||
|
js_check(isolate.execute(
|
||||||
|
"text_encoding_test.js",
|
||||||
|
include_str!("text_encoding_test.js"),
|
||||||
|
));
|
||||||
|
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
243
op_crates/web/text_encoding_test.js
Normal file
243
op_crates/web/text_encoding_test.js
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
|
function assert(cond) {
|
||||||
|
if (!cond) {
|
||||||
|
throw Error("assert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertArrayEquals(a1, a2) {
|
||||||
|
if (a1.length !== a2.length) throw Error("assert");
|
||||||
|
|
||||||
|
for (const index in a1) {
|
||||||
|
if (a1[index] !== a2[index]) {
|
||||||
|
throw Error("assert");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function btoaSuccess() {
|
||||||
|
const text = "hello world";
|
||||||
|
const encoded = btoa(text);
|
||||||
|
assert(encoded === "aGVsbG8gd29ybGQ=");
|
||||||
|
}
|
||||||
|
|
||||||
|
function atobSuccess() {
|
||||||
|
const encoded = "aGVsbG8gd29ybGQ=";
|
||||||
|
const decoded = atob(encoded);
|
||||||
|
assert(decoded === "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
|
function atobWithAsciiWhitespace() {
|
||||||
|
const encodedList = [
|
||||||
|
" aGVsbG8gd29ybGQ=",
|
||||||
|
" aGVsbG8gd29ybGQ=",
|
||||||
|
"aGVsbG8gd29ybGQ= ",
|
||||||
|
"aGVsbG8gd29ybGQ=\n",
|
||||||
|
"aGVsbG\t8gd29ybGQ=",
|
||||||
|
`aGVsbG\t8g
|
||||||
|
d29ybGQ=`,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const encoded of encodedList) {
|
||||||
|
const decoded = atob(encoded);
|
||||||
|
assert(decoded === "hello world");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function atobThrows() {
|
||||||
|
let threw = false;
|
||||||
|
try {
|
||||||
|
atob("aGVsbG8gd29ybGQ==");
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function atobThrows2() {
|
||||||
|
let threw = false;
|
||||||
|
try {
|
||||||
|
atob("aGVsbG8gd29ybGQ===");
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function btoaFailed() {
|
||||||
|
let threw = false;
|
||||||
|
const text = "你好";
|
||||||
|
try {
|
||||||
|
btoa(text);
|
||||||
|
} catch (e) {
|
||||||
|
assert(e instanceof TypeError);
|
||||||
|
threw = true;
|
||||||
|
}
|
||||||
|
assert(threw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoder2() {
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const fixture = new Uint8Array([
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd,
|
||||||
|
0xf0, 0x9d, 0x93, 0xae,
|
||||||
|
0xf0, 0x9d, 0x94, 0x81,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd
|
||||||
|
]);
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
assert(decoder.decode(fixture) === "𝓽𝓮𝔁𝓽");
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderIgnoreBOM() {
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const fixture = new Uint8Array([
|
||||||
|
0xef, 0xbb, 0xbf,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd,
|
||||||
|
0xf0, 0x9d, 0x93, 0xae,
|
||||||
|
0xf0, 0x9d, 0x94, 0x81,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd
|
||||||
|
]);
|
||||||
|
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
||||||
|
assert(decoder.decode(fixture) === "𝓽𝓮𝔁𝓽");
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderNotBOM() {
|
||||||
|
// deno-fmt-ignore
|
||||||
|
const fixture = new Uint8Array([
|
||||||
|
0xef, 0xbb, 0x89,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd,
|
||||||
|
0xf0, 0x9d, 0x93, 0xae,
|
||||||
|
0xf0, 0x9d, 0x94, 0x81,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd
|
||||||
|
]);
|
||||||
|
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
||||||
|
assert(decoder.decode(fixture) === "ﻉ𝓽𝓮𝔁𝓽");
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderASCII() {
|
||||||
|
const fixture = new Uint8Array([0x89, 0x95, 0x9f, 0xbf]);
|
||||||
|
const decoder = new TextDecoder("ascii");
|
||||||
|
assert(decoder.decode(fixture) === "‰•Ÿ¿");
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderErrorEncoding() {
|
||||||
|
let didThrow = false;
|
||||||
|
try {
|
||||||
|
new TextDecoder("foo");
|
||||||
|
} catch (e) {
|
||||||
|
didThrow = true;
|
||||||
|
assert(e.message === "The encoding label provided ('foo') is invalid.");
|
||||||
|
}
|
||||||
|
assert(didThrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textEncoder() {
|
||||||
|
const fixture = "𝓽𝓮𝔁𝓽";
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
// deno-fmt-ignore
|
||||||
|
assertArrayEquals(Array.from(encoder.encode(fixture)), [
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd,
|
||||||
|
0xf0, 0x9d, 0x93, 0xae,
|
||||||
|
0xf0, 0x9d, 0x94, 0x81,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textEncodeInto() {
|
||||||
|
const fixture = "text";
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const bytes = new Uint8Array(5);
|
||||||
|
const result = encoder.encodeInto(fixture, bytes);
|
||||||
|
assert(result.read === 4);
|
||||||
|
assert(result.written === 4);
|
||||||
|
// deno-fmt-ignore
|
||||||
|
assertArrayEquals(Array.from(bytes), [
|
||||||
|
0x74, 0x65, 0x78, 0x74, 0x00,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textEncodeInto2() {
|
||||||
|
const fixture = "𝓽𝓮𝔁𝓽";
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const bytes = new Uint8Array(17);
|
||||||
|
const result = encoder.encodeInto(fixture, bytes);
|
||||||
|
assert(result.read === 8);
|
||||||
|
assert(result.written === 16);
|
||||||
|
// deno-fmt-ignore
|
||||||
|
assertArrayEquals(Array.from(bytes), [
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd,
|
||||||
|
0xf0, 0x9d, 0x93, 0xae,
|
||||||
|
0xf0, 0x9d, 0x94, 0x81,
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd, 0x00,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textEncodeInto3() {
|
||||||
|
const fixture = "𝓽𝓮𝔁𝓽";
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
const bytes = new Uint8Array(5);
|
||||||
|
const result = encoder.encodeInto(fixture, bytes);
|
||||||
|
assert(result.read === 2);
|
||||||
|
assert(result.written === 4);
|
||||||
|
// deno-fmt-ignore
|
||||||
|
assertArrayEquals(Array.from(bytes), [
|
||||||
|
0xf0, 0x9d, 0x93, 0xbd, 0x00,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderSharedUint8Array() {
|
||||||
|
const ab = new SharedArrayBuffer(6);
|
||||||
|
const dataView = new DataView(ab);
|
||||||
|
const charCodeA = "A".charCodeAt(0);
|
||||||
|
for (let i = 0; i < ab.byteLength; i++) {
|
||||||
|
dataView.setUint8(i, charCodeA + i);
|
||||||
|
}
|
||||||
|
const ui8 = new Uint8Array(ab);
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const actual = decoder.decode(ui8);
|
||||||
|
assert(actual === "ABCDEF");
|
||||||
|
}
|
||||||
|
|
||||||
|
function textDecoderSharedInt32Array() {
|
||||||
|
const ab = new SharedArrayBuffer(8);
|
||||||
|
const dataView = new DataView(ab);
|
||||||
|
const charCodeA = "A".charCodeAt(0);
|
||||||
|
for (let i = 0; i < ab.byteLength; i++) {
|
||||||
|
dataView.setUint8(i, charCodeA + i);
|
||||||
|
}
|
||||||
|
const i32 = new Int32Array(ab);
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const actual = decoder.decode(i32);
|
||||||
|
assert(actual === "ABCDEFGH");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toStringShouldBeWebCompatibility() {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
assert(encoder.toString() === "[object TextEncoder]");
|
||||||
|
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
assert(decoder.toString() === "[object TextDecoder]");
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
btoaSuccess();
|
||||||
|
atobSuccess();
|
||||||
|
atobWithAsciiWhitespace();
|
||||||
|
atobThrows();
|
||||||
|
atobThrows2();
|
||||||
|
btoaFailed();
|
||||||
|
textDecoder2();
|
||||||
|
textDecoderIgnoreBOM();
|
||||||
|
textDecoderNotBOM();
|
||||||
|
textDecoderASCII();
|
||||||
|
textDecoderErrorEncoding();
|
||||||
|
textEncoder();
|
||||||
|
textEncodeInto();
|
||||||
|
textEncodeInto2();
|
||||||
|
textEncodeInto3();
|
||||||
|
textDecoderSharedUint8Array();
|
||||||
|
textDecoderSharedInt32Array();
|
||||||
|
toStringShouldBeWebCompatibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
Loading…
Add table
Add a link
Reference in a new issue