mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05: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",
|
||||
"deno_core",
|
||||
"deno_lint",
|
||||
"deno_web",
|
||||
"dissimilar",
|
||||
"dlopen",
|
||||
"dprint-plugin-typescript",
|
||||
|
@ -459,6 +460,14 @@ dependencies = [
|
|||
"swc_ecmascript",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_web"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_deref"
|
||||
version = "1.1.1"
|
||||
|
|
|
@ -4,6 +4,7 @@ members = [
|
|||
"core",
|
||||
"test_plugin",
|
||||
"test_util",
|
||||
"op_crates/web",
|
||||
]
|
||||
exclude = [
|
||||
"std/hash/_wasm"
|
||||
|
|
|
@ -16,6 +16,7 @@ path = "main.rs"
|
|||
|
||||
[build-dependencies]
|
||||
deno_core = { path = "../core", version = "0.51.0" }
|
||||
deno_web = { path = "../op_crates/web", version = "0.1.0" }
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
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 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(
|
||||
"lib.deno.window.d.ts".to_string(),
|
||||
cwd.join("dts/lib.deno.window.d.ts"),
|
||||
|
@ -80,6 +85,10 @@ fn main() {
|
|||
// op_fetch_asset::trace_serializer();
|
||||
|
||||
println!("cargo:rustc-env=TS_VERSION={}", ts_version());
|
||||
println!(
|
||||
"cargo:rustc-env=DENO_WEB_LIB_PATH={}",
|
||||
deno_web::get_scripts().declaration
|
||||
);
|
||||
|
||||
println!(
|
||||
"cargo:rustc-env=TARGET={}",
|
||||
|
@ -93,7 +102,7 @@ fn main() {
|
|||
let runtime_snapshot_path = o.join("CLI_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);
|
||||
|
||||
let js_files = get_js_files("tsc");
|
||||
|
@ -123,3 +132,67 @@ fn get_js_files(d: &str) -> Vec<String> {
|
|||
js_files.sort();
|
||||
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 lib="esnext" />
|
||||
/// <reference lib="deno.web" />
|
||||
|
||||
// This follows the WebIDL at: https://webassembly.github.io/spec/js-api/
|
||||
// and: https://webassembly.github.io/spec/web-api/
|
||||
|
@ -529,12 +530,6 @@ interface DOMStringList {
|
|||
[index: number]: string;
|
||||
}
|
||||
|
||||
declare class DOMException extends Error {
|
||||
constructor(message?: string, name?: string);
|
||||
readonly name: string;
|
||||
readonly message: string;
|
||||
}
|
||||
|
||||
type BufferSource = ArrayBufferView | ArrayBuffer;
|
||||
type BlobPart = BufferSource | Blob | string;
|
||||
|
||||
|
@ -1030,46 +1025,6 @@ declare function fetch(
|
|||
init?: RequestInit,
|
||||
): 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 {
|
||||
/** Appends a specified key/value pair as a new search parameter.
|
||||
*
|
||||
|
@ -1444,143 +1399,6 @@ declare class PerformanceMeasure extends PerformanceEntry {
|
|||
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
|
||||
* (for an XMLHttpRequest, or the loading of the underlying resource of an
|
||||
* <img>, <audio>, <video>, <style> or <link>). */
|
||||
|
|
|
@ -5,6 +5,7 @@ pub static CLI_SNAPSHOT: &[u8] =
|
|||
pub static COMPILER_SNAPSHOT: &[u8] =
|
||||
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_WEB_LIB: &str = include_str!(env!("DENO_WEB_LIB_PATH"));
|
||||
pub static SHARED_GLOBALS_LIB: &str =
|
||||
include_str!("dts/lib.deno.shared_globals.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 {
|
||||
let mut types = format!(
|
||||
"{}\n{}\n{}",
|
||||
"{}\n{}\n{}\n{}",
|
||||
crate::js::DENO_NS_LIB,
|
||||
crate::js::DENO_WEB_LIB,
|
||||
crate::js::SHARED_GLOBALS_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]
|
||||
Files: 43
|
||||
Files: 44
|
||||
Nodes: [WILDCARD]
|
||||
Identifiers: [WILDCARD]
|
||||
Symbols: [WILDCARD]
|
||||
|
|
|
@ -776,6 +776,7 @@ delete Object.prototype.__proto__;
|
|||
// as these are internal APIs of TypeScript which maintain valid libs
|
||||
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.web", "lib.deno.web.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.shared_globals", "lib.deno.shared_globals.d.ts");
|
||||
|
@ -787,6 +788,10 @@ delete Object.prototype.__proto__;
|
|||
`${ASSETS}/lib.deno.ns.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
SNAPSHOT_HOST.getSourceFile(
|
||||
`${ASSETS}/lib.deno.web.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
SNAPSHOT_HOST.getSourceFile(
|
||||
`${ASSETS}/lib.deno.window.d.ts`,
|
||||
ts.ScriptTarget.ESNext,
|
||||
|
|
|
@ -1038,6 +1038,10 @@
|
|||
window.EventTarget = EventTarget;
|
||||
window.ErrorEvent = ErrorEvent;
|
||||
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 = {
|
||||
setEventTargetData,
|
||||
};
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
((window) => {
|
||||
const core = Deno.core;
|
||||
const base64 = window.__bootstrap.base64;
|
||||
|
||||
const CONTINUE = null;
|
||||
const END_OF_STREAM = -1;
|
||||
|
@ -679,6 +678,159 @@
|
|||
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.TextDecoder = TextDecoder;
|
||||
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…
Reference in a new issue