1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

chore: split web op crate (#9635)

This commit starts splitting out the deno_web op crate into multiple
smaller crates. This commit splits out WebIDL and URL API, but in the
future I want to split out each spec into its own crate. That means we
will have (in rough order of loading): `webidl`, `dom`, `streams`,
`console`, `encoding`, `url`, `file`, `fetch`, `websocket`, and
`webgpu` crates.
This commit is contained in:
Luca Casonato 2021-03-12 16:17:18 +01:00 committed by GitHub
parent 2f9d7c02dc
commit e83ff62ccb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 785 additions and 1256 deletions

19
Cargo.lock generated
View file

@ -635,8 +635,10 @@ dependencies = [
"deno_core",
"deno_crypto",
"deno_fetch",
"deno_url",
"deno_web",
"deno_webgpu",
"deno_webidl",
"deno_websocket",
"dlopen",
"encoding_rs",
@ -668,13 +670,21 @@ dependencies = [
"winres",
]
[[package]]
name = "deno_url"
version = "0.1.0"
dependencies = [
"deno_core",
"idna",
"serde",
]
[[package]]
name = "deno_web"
version = "0.30.3"
dependencies = [
"deno_core",
"futures",
"serde",
]
[[package]]
@ -688,6 +698,13 @@ dependencies = [
"wgpu-types",
]
[[package]]
name = "deno_webidl"
version = "0.1.0"
dependencies = [
"deno_core",
]
[[package]]
name = "deno_websocket"
version = "0.5.3"

View file

@ -7,9 +7,13 @@ members = [
"runtime",
"test_plugin",
"test_util",
"op_crates/crypto",
"op_crates/fetch",
"op_crates/url",
"op_crates/web",
"op_crates/crypto"
"op_crates/webgpu",
"op_crates/webidl",
"op_crates/websocket",
]
exclude = [
"std/hash/_wasm"

View file

@ -10,6 +10,7 @@ use deno_core::JsRuntime;
use deno_core::RuntimeOptions;
use deno_runtime::deno_crypto;
use deno_runtime::deno_fetch;
use deno_runtime::deno_url;
use deno_runtime::deno_web;
use deno_runtime::deno_webgpu;
use deno_runtime::deno_websocket;
@ -61,6 +62,7 @@ fn create_compiler_snapshot(
) {
// libs that are being provided by op crates.
let mut op_crate_libs = HashMap::new();
op_crate_libs.insert("deno.url", deno_url::get_declaration());
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu::get_declaration());
@ -254,6 +256,10 @@ fn main() {
println!("cargo:rustc-env=TS_VERSION={}", ts_version());
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash());
println!(
"cargo:rustc-env=DENO_URL_LIB_PATH={}",
deno_url::get_declaration().display()
);
println!(
"cargo:rustc-env=DENO_WEB_LIB_PATH={}",
deno_web::get_declaration().display()

View file

@ -5,6 +5,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="deno.url" />
/// <reference lib="deno.web" />
/// <reference lib="deno.fetch" />
/// <reference lib="deno.websocket" />

View file

@ -278,8 +278,9 @@ fn print_cache_info(
pub fn get_types(unstable: bool) -> String {
let mut types = format!(
"{}\n{}\n{}\n{}\n{}\n{}\n{}",
"{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
crate::tsc::DENO_NS_LIB,
crate::tsc::DENO_URL_LIB,
crate::tsc::DENO_WEB_LIB,
crate::tsc::DENO_FETCH_LIB,
crate::tsc::DENO_WEBGPU_LIB,

View file

@ -29,6 +29,7 @@ use std::sync::Mutex;
// Declaration files
pub static DENO_NS_LIB: &str = include_str!("dts/lib.deno.ns.d.ts");
pub static DENO_URL_LIB: &str = include_str!(env!("DENO_URL_LIB_PATH"));
pub static DENO_WEB_LIB: &str = include_str!(env!("DENO_WEB_LIB_PATH"));
pub static DENO_FETCH_LIB: &str = include_str!(env!("DENO_FETCH_LIB_PATH"));
pub static DENO_WEBGPU_LIB: &str = include_str!(env!("DENO_WEBGPU_LIB_PATH"));

View file

@ -4,7 +4,7 @@
name = "deno_crypto"
version = "0.14.1"
edition = "2018"
description = "Collection of WebCrypto APIs"
description = "Web Cryptography API implementation for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"

View file

@ -1,3 +1,5 @@
# deno crypto
# deno_crypto
Op crate that implements crypto functions.
This crate implements the Web Cryptography API.
Spec: https://www.w3.org/TR/WebCryptoAPI/

View file

@ -4,7 +4,7 @@
name = "deno_fetch"
version = "0.22.3"
edition = "2018"
description = "provides fetch Web API to deno_core"
description = "Fetch API implementation for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"

View file

@ -1 +1,5 @@
This crate provides the web standard fetch API to `deno_core`.
# deno_fetch
This crate implements the Fetch API.
Spec: https://fetch.spec.whatwg.org/

View file

@ -28,7 +28,7 @@
init = init.slice(1);
}
this.#params = core.jsonOpSync("op_parse_url_search_params", init);
this.#params = core.jsonOpSync("op_url_parse_search_params", init);
} else if (
Array.isArray(init) ||
typeof init?.[Symbol.iterator] == "function"
@ -64,7 +64,7 @@
return;
}
const parseArgs = { href: url.href, setSearch: this.toString() };
parts.set(url, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(url, core.jsonOpSync("op_url_parse", parseArgs));
};
append(name, value) {
@ -189,7 +189,7 @@
}
toString() {
return core.jsonOpSync("op_stringify_url_search_params", this.#params);
return core.jsonOpSync("op_url_stringify_search_params", this.#params);
}
}
@ -206,7 +206,7 @@
} else {
base = base !== undefined ? String(base) : base;
const parseArgs = { href: String(url), baseHref: base };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
}
}
@ -231,7 +231,7 @@
if (this.#searchParams != null) {
const params = paramLists.get(this.#searchParams);
const newParams = core.jsonOpSync(
"op_parse_url_search_params",
"op_url_parse_search_params",
this.search.slice(1),
);
params.splice(0, params.length, ...newParams);
@ -245,7 +245,7 @@
set hash(value) {
try {
const parseArgs = { href: this.href, setHash: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -258,7 +258,7 @@
set host(value) {
try {
const parseArgs = { href: this.href, setHost: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -271,7 +271,7 @@
set hostname(value) {
try {
const parseArgs = { href: this.href, setHostname: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -284,7 +284,7 @@
set href(value) {
try {
const parseArgs = { href: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
throw new TypeError("Invalid URL");
}
@ -302,7 +302,7 @@
set password(value) {
try {
const parseArgs = { href: this.href, setPassword: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -315,7 +315,7 @@
set pathname(value) {
try {
const parseArgs = { href: this.href, setPathname: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -328,7 +328,7 @@
set port(value) {
try {
const parseArgs = { href: this.href, setPort: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -341,7 +341,7 @@
set protocol(value) {
try {
const parseArgs = { href: this.href, setProtocol: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}
@ -354,7 +354,7 @@
set search(value) {
try {
const parseArgs = { href: this.href, setSearch: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
this.#updateSearchParams();
} catch {
/* pass */
@ -368,7 +368,7 @@
set username(value) {
try {
const parseArgs = { href: this.href, setUsername: String(value) };
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
parts.set(this, core.jsonOpSync("op_url_parse", parseArgs));
} catch {
/* pass */
}

19
op_crates/url/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
[package]
name = "deno_url"
version = "0.1.0"
edition = "2018"
description = "URL API implementation for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/denoland/deno"
[lib]
path = "lib.rs"
[dependencies]
deno_core = { version = "0.80.2", path = "../../core" }
idna = "0.2.1"
serde = { version = "1.0.123", features = ["derive"] }

5
op_crates/url/README.md Normal file
View file

@ -0,0 +1,5 @@
# deno_url
This crate implements the URL API for Deno.
Spec: https://url.spec.whatwg.org/

13
op_crates/url/internal.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
declare namespace globalThis {
declare namespace __bootstrap {
declare var url: {
URL: typeof URL;
URLSearchParams: typeof URLSearchParams;
};
}
}

175
op_crates/url/lib.deno_url.d.ts vendored Normal file
View file

@ -0,0 +1,175 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-explicit-any
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
declare class URLSearchParams {
constructor(
init?: string[][] | Record<string, string> | string | URLSearchParams,
);
static toString(): string;
/** Appends a specified key/value pair as a new search parameter.
*
* ```ts
* let searchParams = new URLSearchParams();
* searchParams.append('name', 'first');
* searchParams.append('name', 'second');
* ```
*/
append(name: string, value: string): void;
/** Deletes the given search parameter and its associated value,
* from the list of all search parameters.
*
* ```ts
* let searchParams = new URLSearchParams([['name', 'value']]);
* searchParams.delete('name');
* ```
*/
delete(name: string): void;
/** Returns all the values associated with a given search parameter
* as an array.
*
* ```ts
* searchParams.getAll('name');
* ```
*/
getAll(name: string): string[];
/** Returns the first value associated to the given search parameter.
*
* ```ts
* searchParams.get('name');
* ```
*/
get(name: string): string | null;
/** Returns a Boolean that indicates whether a parameter with the
* specified name exists.
*
* ```ts
* searchParams.has('name');
* ```
*/
has(name: string): boolean;
/** Sets the value associated with a given search parameter to the
* given value. If there were several matching values, this method
* deletes the others. If the search parameter doesn't exist, this
* method creates it.
*
* ```ts
* searchParams.set('name', 'value');
* ```
*/
set(name: string, value: string): void;
/** Sort all key/value pairs contained in this object in place and
* return undefined. The sort order is according to Unicode code
* points of the keys.
*
* ```ts
* searchParams.sort();
* ```
*/
sort(): void;
/** Calls a function for each element contained in this object in
* place and return undefined. Optionally accepts an object to use
* as this when executing callback as second argument.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* params.forEach((value, key, parent) => {
* console.log(value, key, parent);
* });
* ```
*
*/
forEach(
callbackfn: (value: string, key: string, parent: this) => void,
thisArg?: any,
): void;
/** Returns an iterator allowing to go through all keys contained
* in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const key of params.keys()) {
* console.log(key);
* }
* ```
*/
keys(): IterableIterator<string>;
/** Returns an iterator allowing to go through all values contained
* in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const value of params.values()) {
* console.log(value);
* }
* ```
*/
values(): IterableIterator<string>;
/** Returns an iterator allowing to go through all key/value
* pairs contained in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const [key, value] of params.entries()) {
* console.log(key, value);
* }
* ```
*/
entries(): IterableIterator<[string, string]>;
/** Returns an iterator allowing to go through all key/value
* pairs contained in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const [key, value] of params) {
* console.log(key, value);
* }
* ```
*/
[Symbol.iterator](): IterableIterator<[string, string]>;
/** Returns a query string suitable for use in a URL.
*
* ```ts
* searchParams.toString();
* ```
*/
toString(): string;
}
/** The URL interface represents an object providing static methods used for creating object URLs. */
declare class URL {
constructor(url: string, base?: string | URL);
createObjectURL(object: any): string;
revokeObjectURL(url: string): void;
hash: string;
host: string;
hostname: string;
href: string;
toString(): string;
readonly origin: string;
password: string;
pathname: string;
port: string;
protocol: string;
search: string;
readonly searchParams: URLSearchParams;
username: string;
toJSON(): string;
}

155
op_crates/url/lib.rs Normal file
View file

@ -0,0 +1,155 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_core::error::generic_error;
use deno_core::error::type_error;
use deno_core::error::uri_error;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::url::form_urlencoded;
use deno_core::url::quirks;
use deno_core::url::Url;
use deno_core::JsRuntime;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use serde::Serialize;
use std::panic::catch_unwind;
use std::path::PathBuf;
/// Parse `UrlParseArgs::href` with an optional `UrlParseArgs::base_href`, or an
/// optional part to "set" after parsing. Return `UrlParts`.
pub fn op_url_parse(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct UrlParseArgs {
href: String,
base_href: Option<String>,
// If one of the following are present, this is a setter call. Apply the
// proper `Url::set_*()` method after (re)parsing `href`.
set_hash: Option<String>,
set_host: Option<String>,
set_hostname: Option<String>,
set_password: Option<String>,
set_pathname: Option<String>,
set_port: Option<String>,
set_protocol: Option<String>,
set_search: Option<String>,
set_username: Option<String>,
}
let args: UrlParseArgs = serde_json::from_value(args)?;
let base_url = args
.base_href
.as_ref()
.map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL")))
.transpose()?;
let mut url = Url::options()
.base_url(base_url.as_ref())
.parse(&args.href)
.map_err(|_| type_error("Invalid URL"))?;
if let Some(hash) = args.set_hash.as_ref() {
quirks::set_hash(&mut url, hash);
} else if let Some(host) = args.set_host.as_ref() {
quirks::set_host(&mut url, host).map_err(|_| uri_error("Invalid host"))?;
} else if let Some(hostname) = args.set_hostname.as_ref() {
quirks::set_hostname(&mut url, hostname)
.map_err(|_| uri_error("Invalid hostname"))?;
} else if let Some(password) = args.set_password.as_ref() {
quirks::set_password(&mut url, password)
.map_err(|_| uri_error("Invalid password"))?;
} else if let Some(pathname) = args.set_pathname.as_ref() {
quirks::set_pathname(&mut url, pathname);
} else if let Some(port) = args.set_port.as_ref() {
quirks::set_port(&mut url, port).map_err(|_| uri_error("Invalid port"))?;
} else if let Some(protocol) = args.set_protocol.as_ref() {
quirks::set_protocol(&mut url, protocol)
.map_err(|_| uri_error("Invalid protocol"))?;
} else if let Some(search) = args.set_search.as_ref() {
quirks::set_search(&mut url, search);
} else if let Some(username) = args.set_username.as_ref() {
quirks::set_username(&mut url, username)
.map_err(|_| uri_error("Invalid username"))?;
}
#[derive(Serialize)]
struct UrlParts<'a> {
href: &'a str,
hash: &'a str,
host: &'a str,
hostname: &'a str,
origin: &'a str,
password: &'a str,
pathname: &'a str,
port: &'a str,
protocol: &'a str,
search: &'a str,
username: &'a str,
}
// TODO(nayeemrmn): Panic that occurs in rust-url for the `non-spec:`
// url-constructor wpt tests: https://github.com/servo/rust-url/issues/670.
let username = catch_unwind(|| quirks::username(&url)).map_err(|_| {
generic_error(format!(
"Internal error while parsing \"{}\"{}, \
see https://github.com/servo/rust-url/issues/670",
args.href,
args
.base_href
.map(|b| format!(" against \"{}\"", b))
.unwrap_or_default()
))
})?;
Ok(json!(UrlParts {
href: quirks::href(&url),
hash: quirks::hash(&url),
host: quirks::host(&url),
hostname: quirks::hostname(&url),
origin: &quirks::origin(&url),
password: quirks::password(&url),
pathname: quirks::pathname(&url),
port: quirks::port(&url),
protocol: quirks::protocol(&url),
search: quirks::search(&url),
username,
}))
}
pub fn op_url_parse_search_params(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let search: String = serde_json::from_value(args)?;
let search_params: Vec<_> = form_urlencoded::parse(search.as_bytes())
.into_iter()
.collect();
Ok(json!(search_params))
}
pub fn op_url_stringify_search_params(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let search_params: Vec<(String, String)> = serde_json::from_value(args)?;
let search = form_urlencoded::Serializer::new(String::new())
.extend_pairs(search_params)
.finish();
Ok(json!(search))
}
/// Load and execute the javascript code.
pub fn init(isolate: &mut JsRuntime) {
let files = vec![("deno:op_crates/url/00_url.js", include_str!("00_url.js"))];
for (url, source_code) in files {
isolate.execute(url, source_code).unwrap();
}
}
pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_url.d.ts")
}

View file

@ -2,6 +2,7 @@
// @ts-check
/// <reference path="../../core/lib.deno_core.d.ts" />
/// <reference path="../webidl/internal.d.ts" />
/// <reference path="../web/internal.d.ts" />
/// <reference path="../web/lib.deno_web.d.ts" />

View file

@ -15,7 +15,6 @@ path = "lib.rs"
[dependencies]
deno_core = { version = "0.80.2", path = "../../core" }
serde = { version = "1.0.123", features = ["derive"] }
[dev-dependencies]
futures = "0.3.12"

View file

@ -1,140 +0,0 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";
function assert(cond) {
if (!cond) {
throw Error("assert");
}
}
function assertEquals(left, right) {
assert(left === right);
}
function assertThrows(fn) {
let error = null;
try {
fn();
} catch (error_) {
error = error_;
}
if (error == null) {
throw new Error("Didn't throw.");
}
return error;
}
function basicAbortController() {
const controller = new AbortController();
assert(controller);
const { signal } = controller;
assert(signal);
assertEquals(signal.aborted, false);
controller.abort();
assertEquals(signal.aborted, true);
}
function signalCallsOnabort() {
const controller = new AbortController();
const { signal } = controller;
let called = false;
signal.onabort = (evt) => {
assert(evt);
assertEquals(evt.type, "abort");
called = true;
};
controller.abort();
assert(called);
}
function signalEventListener() {
const controller = new AbortController();
const { signal } = controller;
let called = false;
signal.addEventListener("abort", function (ev) {
assert(this === signal);
assertEquals(ev.type, "abort");
called = true;
});
controller.abort();
assert(called);
}
function onlyAbortsOnce() {
const controller = new AbortController();
const { signal } = controller;
let called = 0;
signal.addEventListener("abort", () => called++);
signal.onabort = () => {
called++;
};
controller.abort();
assertEquals(called, 2);
controller.abort();
assertEquals(called, 2);
}
function controllerHasProperToString() {
const actual = Object.prototype.toString.call(new AbortController());
assertEquals(actual, "[object AbortController]");
}
function abortSignalIllegalConstructor() {
const error = assertThrows(() => new AbortSignal());
assert(error instanceof TypeError);
assertEquals(error.message, "Illegal constructor.");
}
function abortSignalEventOrder() {
const arr = [];
const controller = new AbortController();
const { signal } = controller;
signal.addEventListener("abort", () => arr.push(1));
signal.onabort = () => arr.push(2);
signal.addEventListener("abort", () => arr.push(3));
controller.abort();
assertEquals(arr[0], 1);
assertEquals(arr[1], 2);
assertEquals(arr[2], 3);
}
function abortSignalEventOrderComplex() {
const arr = [];
const controller = new AbortController();
const { signal } = controller;
signal.addEventListener("abort", () => arr.push(1));
signal.onabort = () => {
throw new Error();
};
signal.addEventListener("abort", () => arr.push(3));
signal.onabort = () => arr.push(2);
controller.abort();
assertEquals(arr[0], 1);
assertEquals(arr[1], 2);
assertEquals(arr[2], 3);
}
function abortSignalHandlerLocation() {
const controller = new AbortController();
const { signal } = controller;
const abortHandler = Object.getOwnPropertyDescriptor(signal, "onabort");
assertEquals(abortHandler, undefined);
}
function abortSignalLength() {
const controller = new AbortController();
const { signal } = controller;
assertEquals(signal.constructor.length, 0);
}
function main() {
basicAbortController();
signalCallsOnabort();
signalEventListener();
onlyAbortsOnce();
controllerHasProperToString();
abortSignalIllegalConstructor();
abortSignalEventOrder();
abortSignalEventOrderComplex();
abortSignalHandlerLocation();
abortSignalLength();
}
main();

View file

@ -1,245 +0,0 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";
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();

View file

@ -1,142 +0,0 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
"use strict";
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() {
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 eventIsTrustedGetterName() {
const { get } = Object.getOwnPropertyDescriptor(new Event("x"), "isTrusted");
assert(get.name === "get isTrusted");
try {
Reflect.construct(get);
throw new Error("Should not have reached here");
} catch (e) {
assert(e.message.includes("not a constructor"));
}
}
function eventAbortSignal() {
let count = 0;
function handler() {
count++;
}
const et = new EventTarget();
const controller = new AbortController();
et.addEventListener("test", handler, { signal: controller.signal });
et.dispatchEvent(new Event("test"));
assert(count === 1);
et.dispatchEvent(new Event("test"));
assert(count === 2);
controller.abort();
et.dispatchEvent(new Event("test"));
assert(count === 2);
et.addEventListener("test", handler, { signal: controller.signal });
et.dispatchEvent(new Event("test"));
assert(count === 2);
}
function main() {
eventInitializedWithType();
eventInitializedWithTypeAndDict();
eventComposedPathSuccess();
eventStopPropagationSuccess();
eventStopImmediatePropagationSuccess();
eventPreventDefaultSuccess();
eventInitializedWithNonStringType();
eventIsTrusted();
eventIsTrustedGetterName();
eventAbortSignal();
}
main();

View file

@ -1,301 +1,14 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-explicit-any ban-types
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
declare namespace globalThis {
declare namespace __bootstrap {
declare namespace webidl {
declare interface ConverterOpts {
/**
* The prefix for error messages created by this converter.
* Examples:
* - `Failed to construct 'Event'`
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
*/
prefix: string;
}
declare interface ValueConverterOpts extends ConverterOpts {
/**
* The context of this value error messages created by this converter.
* Examples:
* - `Argument 1`
* - `Argument 3`
*/
context: string;
}
declare function makeException(
ErrorType: any,
message: string,
opts: ValueConverterOpts,
): any;
declare interface IntConverterOpts extends ValueConverterOpts {
/**
* Wether to throw if the number is outside of the acceptable values for
* this type.
*/
enforceRange?: boolean;
/**
* Wether to clamp this number to the acceptable values for this type.
*/
clamp?: boolean;
}
declare interface StringConverterOpts extends ValueConverterOpts {
/**
* Wether to treat `null` value as an empty string.
*/
treatNullAsEmptyString?: boolean;
}
declare interface BufferConverterOpts extends ValueConverterOpts {
/**
* Wether to allow `SharedArrayBuffer` (not just `ArrayBuffer`).
*/
allowShared?: boolean;
}
declare const converters: {
any(v: any): any;
/**
* Convert a value into a `boolean` (bool).
*/
boolean(v: any, opts?: IntConverterOpts): boolean;
/**
* Convert a value into a `byte` (int8).
*/
byte(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `octet` (uint8).
*/
octet(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `short` (int16).
*/
short(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned short` (uint16).
*/
["unsigned short"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `long` (int32).
*/
long(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned long` (uint32).
*/
["unsigned long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `long long` (int64).
* **Note this is truncated to a JS number (53 bit precision).**
*/
["long long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned long long` (uint64).
* **Note this is truncated to a JS number (53 bit precision).**
*/
["unsigned long long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `float` (f32).
*/
float(v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `unrestricted float` (f32, infinity, or NaN).
*/
["unrestricted float"](v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `double` (f64).
*/
double(v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `unrestricted double` (f64, infinity, or NaN).
*/
["unrestricted double"](v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `DOMString` (string).
*/
DOMString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into a `ByteString` (string with only u8 codepoints).
*/
ByteString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into a `USVString` (string with only valid non
* surrogate Unicode code points).
*/
USVString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into an `object` (object).
*/
object(v: any, opts?: ValueConverterOpts): object;
/**
* Convert a value into an `ArrayBuffer` (ArrayBuffer).
*/
ArrayBuffer(v: any, opts?: BufferConverterOpts): ArrayBuffer;
/**
* Convert a value into a `DataView` (ArrayBuffer).
*/
DataView(v: any, opts?: BufferConverterOpts): DataView;
/**
* Convert a value into a `Int8Array` (Int8Array).
*/
Int8Array(v: any, opts?: BufferConverterOpts): Int8Array;
/**
* Convert a value into a `Int16Array` (Int16Array).
*/
Int16Array(v: any, opts?: BufferConverterOpts): Int16Array;
/**
* Convert a value into a `Int32Array` (Int32Array).
*/
Int32Array(v: any, opts?: BufferConverterOpts): Int32Array;
/**
* Convert a value into a `Uint8Array` (Uint8Array).
*/
Uint8Array(v: any, opts?: BufferConverterOpts): Uint8Array;
/**
* Convert a value into a `Uint16Array` (Uint16Array).
*/
Uint16Array(v: any, opts?: BufferConverterOpts): Uint16Array;
/**
* Convert a value into a `Uint32Array` (Uint32Array).
*/
Uint32Array(v: any, opts?: BufferConverterOpts): Uint32Array;
/**
* Convert a value into a `Uint8ClampedArray` (Uint8ClampedArray).
*/
Uint8ClampedArray(
v: any,
opts?: BufferConverterOpts,
): Uint8ClampedArray;
/**
* Convert a value into a `Float32Array` (Float32Array).
*/
Float32Array(v: any, opts?: BufferConverterOpts): Float32Array;
/**
* Convert a value into a `Float64Array` (Float64Array).
*/
Float64Array(v: any, opts?: BufferConverterOpts): Float64Array;
/**
* Convert a value into an `ArrayBufferView` (ArrayBufferView).
*/
ArrayBufferView(v: any, opts?: BufferConverterOpts): ArrayBufferView;
/**
* Convert a value into a `BufferSource` (ArrayBuffer or ArrayBufferView).
*/
BufferSource(
v: any,
opts?: BufferConverterOpts,
): ArrayBuffer | ArrayBufferView;
/**
* Convert a value into a `DOMTimeStamp` (u64). Alias for unsigned long long
*/
DOMTimeStamp(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `Function` ((...args: any[]) => any).
*/
Function(v: any, opts?: ValueConverterOpts): (...args: any) => any;
/**
* Convert a value into a `VoidFunction` (() => void).
*/
VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
["UVString?"](v: any, opts?: ValueConverterOpts): string | null;
["sequence<double>"](v: any, opts?: ValueConverterOpts): number[];
[type: string]: (v: any, opts: ValueConverterOpts) => any;
};
/**
* Assert that the a function has at least a required amount of arguments.
*/
declare function requiredArguments(
length: number,
required: number,
opts: ConverterOpts,
): void;
declare type Dictionary = DictionaryMember[];
declare interface DictionaryMember {
key: string;
converter: (v: any, opts: ValueConverterOpts) => any;
defaultValue?: any;
required?: boolean;
}
/**
* Create a converter for dictionaries.
*/
declare function createDictionaryConverter<T>(
name: string,
...dictionaries: Dictionary[]
): (v: any, opts: ValueConverterOpts) => T;
/**
* Create a converter for enums.
*/
declare function createEnumConverter(
name: string,
values: string[],
): (v: any, opts: ValueConverterOpts) => string;
/**
* Create a converter that makes the contained type nullable.
*/
declare function createNullableConverter<T>(
converter: (v: any, opts: ValueConverterOpts) => T,
): (v: any, opts: ValueConverterOpts) => T | null;
/**
* Create a converter that converts a sequence of the inner type.
*/
declare function createSequenceConverter<T>(
converter: (v: any, opts: ValueConverterOpts) => T,
): (v: any, opts: ValueConverterOpts) => T[];
/**
* Throw an illegal constructor error.
*/
declare function illegalConstructor(): never;
/**
* The branding symbol.
*/
declare const brand: unique symbol;
/**
* Create a branded instance of an interface.
*/
declare function createBranded(self: any): any;
/**
* Assert that self is branded.
*/
declare function assertBranded(self: any, type: any): void;
/**
* Create a converter for interfaces.
*/
declare function createInterfaceConverter(
name: string,
prototype: any,
): (v: any, opts: ValueConverterOpts) => any;
declare function createRecordConverter<
K extends string | number | symbol,
V,
>(
keyConverter: (v: any, opts: ValueConverterOpts) => K,
valueConverter: (v: any, opts: ValueConverterOpts) => V,
): (
v: Record<K, V>,
opts: ValueConverterOpts,
) => any;
}
declare var eventTarget: {
EventTarget: typeof EventTarget;
};
declare var url: {
URLSearchParams: typeof URLSearchParams;
};
declare var location: {
getLocationHref(): string | undefined;
};

View file

@ -313,172 +313,3 @@ declare var FileReader: {
readonly EMPTY: number;
readonly LOADING: number;
};
declare class URLSearchParams {
constructor(
init?: string[][] | Record<string, string> | string | URLSearchParams,
);
static toString(): string;
/** Appends a specified key/value pair as a new search parameter.
*
* ```ts
* let searchParams = new URLSearchParams();
* searchParams.append('name', 'first');
* searchParams.append('name', 'second');
* ```
*/
append(name: string, value: string): void;
/** Deletes the given search parameter and its associated value,
* from the list of all search parameters.
*
* ```ts
* let searchParams = new URLSearchParams([['name', 'value']]);
* searchParams.delete('name');
* ```
*/
delete(name: string): void;
/** Returns all the values associated with a given search parameter
* as an array.
*
* ```ts
* searchParams.getAll('name');
* ```
*/
getAll(name: string): string[];
/** Returns the first value associated to the given search parameter.
*
* ```ts
* searchParams.get('name');
* ```
*/
get(name: string): string | null;
/** Returns a Boolean that indicates whether a parameter with the
* specified name exists.
*
* ```ts
* searchParams.has('name');
* ```
*/
has(name: string): boolean;
/** Sets the value associated with a given search parameter to the
* given value. If there were several matching values, this method
* deletes the others. If the search parameter doesn't exist, this
* method creates it.
*
* ```ts
* searchParams.set('name', 'value');
* ```
*/
set(name: string, value: string): void;
/** Sort all key/value pairs contained in this object in place and
* return undefined. The sort order is according to Unicode code
* points of the keys.
*
* ```ts
* searchParams.sort();
* ```
*/
sort(): void;
/** Calls a function for each element contained in this object in
* place and return undefined. Optionally accepts an object to use
* as this when executing callback as second argument.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* params.forEach((value, key, parent) => {
* console.log(value, key, parent);
* });
* ```
*
*/
forEach(
callbackfn: (value: string, key: string, parent: this) => void,
thisArg?: any,
): void;
/** Returns an iterator allowing to go through all keys contained
* in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const key of params.keys()) {
* console.log(key);
* }
* ```
*/
keys(): IterableIterator<string>;
/** Returns an iterator allowing to go through all values contained
* in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const value of params.values()) {
* console.log(value);
* }
* ```
*/
values(): IterableIterator<string>;
/** Returns an iterator allowing to go through all key/value
* pairs contained in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const [key, value] of params.entries()) {
* console.log(key, value);
* }
* ```
*/
entries(): IterableIterator<[string, string]>;
/** Returns an iterator allowing to go through all key/value
* pairs contained in this object.
*
* ```ts
* const params = new URLSearchParams([["a", "b"], ["c", "d"]]);
* for (const [key, value] of params) {
* console.log(key, value);
* }
* ```
*/
[Symbol.iterator](): IterableIterator<[string, string]>;
/** Returns a query string suitable for use in a URL.
*
* ```ts
* searchParams.toString();
* ```
*/
toString(): string;
}
/** The URL interface represents an object providing static methods used for creating object URLs. */
declare class URL {
constructor(url: string, base?: string | URL);
createObjectURL(object: any): string;
revokeObjectURL(url: string): void;
hash: string;
host: string;
hostname: string;
href: string;
toString(): string;
readonly origin: string;
password: string;
pathname: string;
port: string;
protocol: string;
search: string;
readonly searchParams: URLSearchParams;
username: string;
toJSON(): string;
}

View file

@ -1,29 +1,11 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_core::error::generic_error;
use deno_core::error::type_error;
use deno_core::error::uri_error;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::url::form_urlencoded;
use deno_core::url::quirks;
use deno_core::url::Url;
use deno_core::JsRuntime;
use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use serde::Serialize;
use std::panic::catch_unwind;
use std::path::PathBuf;
/// Load and execute the javascript code.
pub fn init(isolate: &mut JsRuntime) {
let files = vec![
(
"deno:op_crates/web/00_webidl.js",
include_str!("00_webidl.js"),
),
(
"deno:op_crates/web/01_dom_exception.js",
include_str!("01_dom_exception.js"),
@ -44,7 +26,6 @@ pub fn init(isolate: &mut JsRuntime) {
"deno:op_crates/web/08_text_encoding.js",
include_str!("08_text_encoding.js"),
),
("deno:op_crates/web/11_url.js", include_str!("11_url.js")),
(
"deno:op_crates/web/12_location.js",
include_str!("12_location.js"),
@ -59,213 +40,6 @@ pub fn init(isolate: &mut JsRuntime) {
}
}
/// Parse `UrlParseArgs::href` with an optional `UrlParseArgs::base_href`, or an
/// optional part to "set" after parsing. Return `UrlParts`.
pub fn op_parse_url(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct UrlParseArgs {
href: String,
base_href: Option<String>,
// If one of the following are present, this is a setter call. Apply the
// proper `Url::set_*()` method after (re)parsing `href`.
set_hash: Option<String>,
set_host: Option<String>,
set_hostname: Option<String>,
set_password: Option<String>,
set_pathname: Option<String>,
set_port: Option<String>,
set_protocol: Option<String>,
set_search: Option<String>,
set_username: Option<String>,
}
let args: UrlParseArgs = serde_json::from_value(args)?;
let base_url = args
.base_href
.as_ref()
.map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL")))
.transpose()?;
let mut url = Url::options()
.base_url(base_url.as_ref())
.parse(&args.href)
.map_err(|_| type_error("Invalid URL"))?;
if let Some(hash) = args.set_hash.as_ref() {
quirks::set_hash(&mut url, hash);
} else if let Some(host) = args.set_host.as_ref() {
quirks::set_host(&mut url, host).map_err(|_| uri_error("Invalid host"))?;
} else if let Some(hostname) = args.set_hostname.as_ref() {
quirks::set_hostname(&mut url, hostname)
.map_err(|_| uri_error("Invalid hostname"))?;
} else if let Some(password) = args.set_password.as_ref() {
quirks::set_password(&mut url, password)
.map_err(|_| uri_error("Invalid password"))?;
} else if let Some(pathname) = args.set_pathname.as_ref() {
quirks::set_pathname(&mut url, pathname);
} else if let Some(port) = args.set_port.as_ref() {
quirks::set_port(&mut url, port).map_err(|_| uri_error("Invalid port"))?;
} else if let Some(protocol) = args.set_protocol.as_ref() {
quirks::set_protocol(&mut url, protocol)
.map_err(|_| uri_error("Invalid protocol"))?;
} else if let Some(search) = args.set_search.as_ref() {
quirks::set_search(&mut url, search);
} else if let Some(username) = args.set_username.as_ref() {
quirks::set_username(&mut url, username)
.map_err(|_| uri_error("Invalid username"))?;
}
#[derive(Serialize)]
struct UrlParts<'a> {
href: &'a str,
hash: &'a str,
host: &'a str,
hostname: &'a str,
origin: &'a str,
password: &'a str,
pathname: &'a str,
port: &'a str,
protocol: &'a str,
search: &'a str,
username: &'a str,
}
// TODO(nayeemrmn): Panic that occurs in rust-url for the `non-spec:`
// url-constructor wpt tests: https://github.com/servo/rust-url/issues/670.
let username = catch_unwind(|| quirks::username(&url)).map_err(|_| {
generic_error(format!(
"Internal error while parsing \"{}\"{}, \
see https://github.com/servo/rust-url/issues/670",
args.href,
args
.base_href
.map(|b| format!(" against \"{}\"", b))
.unwrap_or_default()
))
})?;
Ok(json!(UrlParts {
href: quirks::href(&url),
hash: quirks::hash(&url),
host: quirks::host(&url),
hostname: quirks::hostname(&url),
origin: &quirks::origin(&url),
password: quirks::password(&url),
pathname: quirks::pathname(&url),
port: quirks::port(&url),
protocol: quirks::protocol(&url),
search: quirks::search(&url),
username,
}))
}
pub fn op_parse_url_search_params(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let search: String = serde_json::from_value(args)?;
let search_params: Vec<_> = form_urlencoded::parse(search.as_bytes())
.into_iter()
.collect();
Ok(json!(search_params))
}
pub fn op_stringify_url_search_params(
_state: &mut deno_core::OpState,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<Value, AnyError> {
let search_params: Vec<(String, String)> = serde_json::from_value(args)?;
let search = form_urlencoded::Serializer::new(String::new())
.extend_pairs(search_params)
.finish();
Ok(json!(search))
}
pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
}
#[cfg(test)]
mod tests {
use deno_core::JsRuntime;
use futures::future::lazy;
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() -> JsRuntime {
let mut isolate = JsRuntime::new(Default::default());
crate::init(&mut isolate);
isolate
}
#[test]
fn test_abort_controller() {
run_in_task(|mut cx| {
let mut isolate = setup();
isolate
.execute(
"abort_controller_test.js",
include_str!("abort_controller_test.js"),
)
.unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!();
}
});
}
#[test]
fn test_event() {
run_in_task(|mut cx| {
let mut isolate = setup();
isolate
.execute("event_test.js", include_str!("event_test.js"))
.unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!();
}
});
}
#[test]
fn test_event_error() {
run_in_task(|mut cx| {
let mut isolate = setup();
let result = isolate.execute("foo.js", "new Event()");
if let Err(error) = result {
let error_string = error.to_string();
// Test that the script specifier is a URL: `deno:<repo-relative path>`.
assert!(error_string.contains("deno:op_crates/web/02_event.js"));
assert!(error_string.contains("TypeError"));
} else {
unreachable!();
}
if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!();
}
});
}
#[test]
fn test_event_target() {
run_in_task(|mut cx| {
let mut isolate = setup();
isolate
.execute("event_target_test.js", include_str!("event_target_test.js"))
.unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!();
}
});
}
}

View file

@ -4,7 +4,7 @@
name = "deno_webgpu"
version = "0.1.1"
edition = "2018"
description = "provides webgpu Web API to deno_core"
description = "WebGPU implementation for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"

View file

@ -0,0 +1,17 @@
# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
[package]
name = "deno_webidl"
version = "0.1.0"
edition = "2018"
description = "WebIDL implementation for Deno"
authors = ["the Deno authors"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/denoland/deno"
[lib]
path = "lib.rs"
[dependencies]
deno_core = { version = "0.80.2", path = "../../core" }

View file

@ -0,0 +1,6 @@
# deno_webidl
This crate implements WebIDL for Deno. It consists of infrastructure to do ECMA
-> WebIDL conversions.
Spec: https://heycam.github.io/webidl/

291
op_crates/webidl/internal.d.ts vendored Normal file
View file

@ -0,0 +1,291 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-explicit-any ban-types
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
declare namespace globalThis {
declare namespace __bootstrap {
declare namespace webidl {
declare interface ConverterOpts {
/**
* The prefix for error messages created by this converter.
* Examples:
* - `Failed to construct 'Event'`
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
*/
prefix: string;
}
declare interface ValueConverterOpts extends ConverterOpts {
/**
* The context of this value error messages created by this converter.
* Examples:
* - `Argument 1`
* - `Argument 3`
*/
context: string;
}
declare function makeException(
ErrorType: any,
message: string,
opts: ValueConverterOpts,
): any;
declare interface IntConverterOpts extends ValueConverterOpts {
/**
* Wether to throw if the number is outside of the acceptable values for
* this type.
*/
enforceRange?: boolean;
/**
* Wether to clamp this number to the acceptable values for this type.
*/
clamp?: boolean;
}
declare interface StringConverterOpts extends ValueConverterOpts {
/**
* Wether to treat `null` value as an empty string.
*/
treatNullAsEmptyString?: boolean;
}
declare interface BufferConverterOpts extends ValueConverterOpts {
/**
* Wether to allow `SharedArrayBuffer` (not just `ArrayBuffer`).
*/
allowShared?: boolean;
}
declare const converters: {
any(v: any): any;
/**
* Convert a value into a `boolean` (bool).
*/
boolean(v: any, opts?: IntConverterOpts): boolean;
/**
* Convert a value into a `byte` (int8).
*/
byte(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `octet` (uint8).
*/
octet(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `short` (int16).
*/
short(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned short` (uint16).
*/
["unsigned short"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `long` (int32).
*/
long(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned long` (uint32).
*/
["unsigned long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `long long` (int64).
* **Note this is truncated to a JS number (53 bit precision).**
*/
["long long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `unsigned long long` (uint64).
* **Note this is truncated to a JS number (53 bit precision).**
*/
["unsigned long long"](v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `float` (f32).
*/
float(v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `unrestricted float` (f32, infinity, or NaN).
*/
["unrestricted float"](v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `double` (f64).
*/
double(v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `unrestricted double` (f64, infinity, or NaN).
*/
["unrestricted double"](v: any, opts?: ValueConverterOpts): number;
/**
* Convert a value into a `DOMString` (string).
*/
DOMString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into a `ByteString` (string with only u8 codepoints).
*/
ByteString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into a `USVString` (string with only valid non
* surrogate Unicode code points).
*/
USVString(v: any, opts?: StringConverterOpts): string;
/**
* Convert a value into an `object` (object).
*/
object(v: any, opts?: ValueConverterOpts): object;
/**
* Convert a value into an `ArrayBuffer` (ArrayBuffer).
*/
ArrayBuffer(v: any, opts?: BufferConverterOpts): ArrayBuffer;
/**
* Convert a value into a `DataView` (ArrayBuffer).
*/
DataView(v: any, opts?: BufferConverterOpts): DataView;
/**
* Convert a value into a `Int8Array` (Int8Array).
*/
Int8Array(v: any, opts?: BufferConverterOpts): Int8Array;
/**
* Convert a value into a `Int16Array` (Int16Array).
*/
Int16Array(v: any, opts?: BufferConverterOpts): Int16Array;
/**
* Convert a value into a `Int32Array` (Int32Array).
*/
Int32Array(v: any, opts?: BufferConverterOpts): Int32Array;
/**
* Convert a value into a `Uint8Array` (Uint8Array).
*/
Uint8Array(v: any, opts?: BufferConverterOpts): Uint8Array;
/**
* Convert a value into a `Uint16Array` (Uint16Array).
*/
Uint16Array(v: any, opts?: BufferConverterOpts): Uint16Array;
/**
* Convert a value into a `Uint32Array` (Uint32Array).
*/
Uint32Array(v: any, opts?: BufferConverterOpts): Uint32Array;
/**
* Convert a value into a `Uint8ClampedArray` (Uint8ClampedArray).
*/
Uint8ClampedArray(
v: any,
opts?: BufferConverterOpts,
): Uint8ClampedArray;
/**
* Convert a value into a `Float32Array` (Float32Array).
*/
Float32Array(v: any, opts?: BufferConverterOpts): Float32Array;
/**
* Convert a value into a `Float64Array` (Float64Array).
*/
Float64Array(v: any, opts?: BufferConverterOpts): Float64Array;
/**
* Convert a value into an `ArrayBufferView` (ArrayBufferView).
*/
ArrayBufferView(v: any, opts?: BufferConverterOpts): ArrayBufferView;
/**
* Convert a value into a `BufferSource` (ArrayBuffer or ArrayBufferView).
*/
BufferSource(
v: any,
opts?: BufferConverterOpts,
): ArrayBuffer | ArrayBufferView;
/**
* Convert a value into a `DOMTimeStamp` (u64). Alias for unsigned long long
*/
DOMTimeStamp(v: any, opts?: IntConverterOpts): number;
/**
* Convert a value into a `Function` ((...args: any[]) => any).
*/
Function(v: any, opts?: ValueConverterOpts): (...args: any) => any;
/**
* Convert a value into a `VoidFunction` (() => void).
*/
VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
["UVString?"](v: any, opts?: ValueConverterOpts): string | null;
["sequence<double>"](v: any, opts?: ValueConverterOpts): number[];
[type: string]: (v: any, opts: ValueConverterOpts) => any;
};
/**
* Assert that the a function has at least a required amount of arguments.
*/
declare function requiredArguments(
length: number,
required: number,
opts: ConverterOpts,
): void;
declare type Dictionary = DictionaryMember[];
declare interface DictionaryMember {
key: string;
converter: (v: any, opts: ValueConverterOpts) => any;
defaultValue?: any;
required?: boolean;
}
/**
* Create a converter for dictionaries.
*/
declare function createDictionaryConverter<T>(
name: string,
...dictionaries: Dictionary[]
): (v: any, opts: ValueConverterOpts) => T;
/**
* Create a converter for enums.
*/
declare function createEnumConverter(
name: string,
values: string[],
): (v: any, opts: ValueConverterOpts) => string;
/**
* Create a converter that makes the contained type nullable.
*/
declare function createNullableConverter<T>(
converter: (v: any, opts: ValueConverterOpts) => T,
): (v: any, opts: ValueConverterOpts) => T | null;
/**
* Create a converter that converts a sequence of the inner type.
*/
declare function createSequenceConverter<T>(
converter: (v: any, opts: ValueConverterOpts) => T,
): (v: any, opts: ValueConverterOpts) => T[];
/**
* Throw an illegal constructor error.
*/
declare function illegalConstructor(): never;
/**
* The branding symbol.
*/
declare const brand: unique symbol;
/**
* Create a branded instance of an interface.
*/
declare function createBranded(self: any): any;
/**
* Assert that self is branded.
*/
declare function assertBranded(self: any, type: any): void;
/**
* Create a converter for interfaces.
*/
declare function createInterfaceConverter(
name: string,
prototype: any,
): (v: any, opts: ValueConverterOpts) => any;
declare function createRecordConverter<
K extends string | number | symbol,
V,
>(
keyConverter: (v: any, opts: ValueConverterOpts) => K,
valueConverter: (v: any, opts: ValueConverterOpts) => V,
): (
v: Record<K, V>,
opts: ValueConverterOpts,
) => any;
}
}
}

14
op_crates/webidl/lib.rs Normal file
View file

@ -0,0 +1,14 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_core::JsRuntime;
/// Load and execute the javascript code.
pub fn init(isolate: &mut JsRuntime) {
let files = vec![(
"deno:op_crates/webidl/00_webidl.js",
include_str!("00_webidl.js"),
)];
for (url, source_code) in files {
isolate.execute(url, source_code).unwrap();
}
}

View file

@ -22,6 +22,8 @@ deno_core = { path = "../core", version = "0.80.2" }
deno_crypto = { path = "../op_crates/crypto", version = "0.14.1" }
deno_fetch = { path = "../op_crates/fetch", version = "0.22.3" }
deno_web = { path = "../op_crates/web", version = "0.30.3" }
deno_url = { path = "../op_crates/url", version = "0.1.0" }
deno_webidl = { path = "../op_crates/webidl", version = "0.1.0" }
deno_websocket = { path = "../op_crates/websocket", version = "0.5.3" }
deno_webgpu = { path = "../op_crates/webgpu", version = "0.1.1" }
@ -34,6 +36,8 @@ deno_core = { path = "../core", version = "0.80.2" }
deno_crypto = { path = "../op_crates/crypto", version = "0.14.1" }
deno_fetch = { path = "../op_crates/fetch", version = "0.22.3" }
deno_web = { path = "../op_crates/web", version = "0.30.3" }
deno_url = { path = "../op_crates/url", version = "0.1.0" }
deno_webidl = { path = "../op_crates/webidl", version = "0.1.0" }
deno_websocket = { path = "../op_crates/websocket", version = "0.5.3" }
deno_webgpu = { path = "../op_crates/webgpu", version = "0.1.1" }

View file

@ -13,6 +13,8 @@ fn create_snapshot(
snapshot_path: &Path,
files: Vec<PathBuf>,
) {
deno_webidl::init(&mut js_runtime);
deno_url::init(&mut js_runtime);
deno_web::init(&mut js_runtime);
deno_fetch::init(&mut js_runtime);
deno_websocket::init(&mut js_runtime);

View file

@ -9,8 +9,10 @@ extern crate log;
pub use deno_crypto;
pub use deno_fetch;
pub use deno_url;
pub use deno_web;
pub use deno_webgpu;
pub use deno_webidl;
pub use deno_websocket;
pub mod colors;

View file

@ -20,6 +20,7 @@ pub mod signal;
pub mod timers;
pub mod tls;
pub mod tty;
pub mod url;
pub mod web_worker;
pub mod webgpu;
pub mod websocket;

18
runtime/ops/url.rs Normal file
View file

@ -0,0 +1,18 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use deno_url::op_url_parse;
use deno_url::op_url_parse_search_params;
use deno_url::op_url_stringify_search_params;
pub fn init(rt: &mut deno_core::JsRuntime) {
super::reg_json_sync(rt, "op_url_parse", op_url_parse);
super::reg_json_sync(
rt,
"op_url_parse_search_params",
op_url_parse_search_params,
);
super::reg_json_sync(
rt,
"op_url_stringify_search_params",
op_url_stringify_search_params,
);
}

View file

@ -231,17 +231,7 @@ impl WebWorker {
);
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
ops::reg_json_sync(js_runtime, "op_parse_url", deno_web::op_parse_url);
ops::reg_json_sync(
js_runtime,
"op_parse_url_search_params",
deno_web::op_parse_url_search_params,
);
ops::reg_json_sync(
js_runtime,
"op_stringify_url_search_params",
deno_web::op_stringify_url_search_params,
);
ops::url::init(js_runtime);
ops::io::init(js_runtime);
ops::webgpu::init(js_runtime);
ops::websocket::init(

View file

@ -127,17 +127,7 @@ impl MainWorker {
ops::crypto::init(js_runtime, options.seed);
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
ops::reg_json_sync(js_runtime, "op_parse_url", deno_web::op_parse_url);
ops::reg_json_sync(
js_runtime,
"op_parse_url_search_params",
deno_web::op_parse_url_search_params,
);
ops::reg_json_sync(
js_runtime,
"op_stringify_url_search_params",
deno_web::op_stringify_url_search_params,
);
ops::url::init(js_runtime);
ops::fs_events::init(js_runtime);
ops::fs::init(js_runtime);
ops::io::init(js_runtime);