summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrowlKats <13135287+crowlKats@users.noreply.github.com>2021-05-10 12:02:47 +0200
committerGitHub <noreply@github.com>2021-05-10 12:02:47 +0200
commitdfe528198d363ebc883da84dc816bce112ecd24b (patch)
treedfa0e67521a5b0a968b57514aa02fc8f114d4132
parent32ad8f77d68df04e26ecaed71bdd8d37f048218a (diff)
feat: add WebStorage API (#7819)
This commit introduces localStorage and sessionStorage.
-rw-r--r--Cargo.lock147
-rw-r--r--cli/build.rs6
-rw-r--r--cli/dts/lib.deno.window.d.ts5
-rw-r--r--cli/main.rs11
-rw-r--r--cli/standalone.rs1
-rw-r--r--cli/tsc.rs2
-rw-r--r--extensions/webstorage/01_webstorage.js190
-rw-r--r--extensions/webstorage/Cargo.toml19
-rw-r--r--extensions/webstorage/README.md5
-rw-r--r--extensions/webstorage/lib.deno_webstorage.d.ts42
-rw-r--r--extensions/webstorage/lib.rs316
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/errors.rs2
-rw-r--r--runtime/examples/hello_runtime.rs1
-rw-r--r--runtime/js/99_main.js24
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/worker.rs3
-rwxr-xr-xtools/wpt.ts26
-rw-r--r--tools/wpt/expectation.json60
20 files changed, 819 insertions, 45 deletions
diff --git a/Cargo.lock b/Cargo.lock
index afedc80aa..5627224ba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -37,9 +37,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "0.7.15"
+version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
@@ -114,9 +114,9 @@ dependencies = [
[[package]]
name = "async-compression"
-version = "0.3.7"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b72c1f1154e234325b50864a349b9c8e56939e266a4c307c0f159812df2f9537"
+checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6"
dependencies = [
"brotli",
"flate2",
@@ -442,9 +442,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.3"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
+checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
@@ -697,6 +697,7 @@ dependencies = [
"deno_webgpu",
"deno_webidl",
"deno_websocket",
+ "deno_webstorage",
"dlopen",
"encoding_rs",
"filetime",
@@ -788,6 +789,15 @@ dependencies = [
]
[[package]]
+name = "deno_webstorage"
+version = "0.1.0"
+dependencies = [
+ "deno_core",
+ "rusqlite",
+ "serde",
+]
+
+[[package]]
name = "derive_more"
version = "0.99.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1014,6 +1024,18 @@ dependencies = [
]
[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
name = "fancy-regex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1483,9 +1505,9 @@ dependencies = [
[[package]]
name = "h2"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
+checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726"
dependencies = [
"bytes",
"fnv",
@@ -1510,6 +1532,15 @@ dependencies = [
]
[[package]]
+name = "hashlink"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8"
+dependencies = [
+ "hashbrown",
+]
+
+[[package]]
name = "heck"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1787,9 +1818,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.93"
+version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
+checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "libloading"
@@ -1802,6 +1833,17 @@ dependencies = [
]
[[package]]
+name = "libsqlite3-sys"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19cb1effde5f834799ac5e5ef0e40d45027cd74f271b1de786ba8abb30e2164d"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1809,9 +1851,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
-version = "0.4.3"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
+checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
@@ -1908,9 +1950,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
-version = "2.3.4"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
+checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "metal"
@@ -2254,6 +2296,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "pkg-config"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
+[[package]]
name = "pmutil"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2327,7 +2375,7 @@ version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
dependencies = [
- "unicode-xid 0.2.1",
+ "unicode-xid 0.2.2",
]
[[package]]
@@ -2505,9 +2553,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
+checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2"
dependencies = [
"bitflags",
]
@@ -2526,9 +2574,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.6.23"
+version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "relative-path"
@@ -2625,6 +2673,21 @@ dependencies = [
]
[[package]]
+name = "rusqlite"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc783b7ddae608338003bac1fa00b6786a75a9675fbd8e87243ecfdea3f6ed2"
+dependencies = [
+ "bitflags",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "memchr",
+ "smallvec",
+]
+
+[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3010,9 +3073,9 @@ dependencies = [
[[package]]
name = "swc_bundler"
-version = "0.32.5"
+version = "0.32.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a3fd9db5796f48c7db74c045838c77981aed155c47e8ecf7dca2f7c47408e70"
+checksum = "2c724be1aea9b099b11b2d57ab59e25ed8428c03d9daa8fdded8fa81d17b6d56"
dependencies = [
"ahash 0.7.2",
"anyhow",
@@ -3076,9 +3139,9 @@ dependencies = [
[[package]]
name = "swc_ecma_codegen"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bffd587bf624fc3ad732433704cc4ff415967ed9feed26accf2db9130237e09"
+checksum = "4f62df17607eee6c488fdd24d192911db862b2af68a6ea72e166bb699f8a417a"
dependencies = [
"bitflags",
"num-bigint",
@@ -3132,7 +3195,7 @@ dependencies = [
"swc_common",
"swc_ecma_ast",
"swc_ecma_visit",
- "unicode-xid 0.2.1",
+ "unicode-xid 0.2.2",
]
[[package]]
@@ -3152,7 +3215,7 @@ dependencies = [
"swc_ecma_transforms_typescript",
"swc_ecma_utils",
"swc_ecma_visit",
- "unicode-xid 0.2.1",
+ "unicode-xid 0.2.2",
]
[[package]]
@@ -3176,9 +3239,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_optimization"
-version = "0.15.5"
+version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f187623203e84095337ea16b499ef6768819c3f2f7e3cdae24022135282dd3d"
+checksum = "70ae2bd352c1365d1c8f02c5016f729de6e1fb2fe03afd5c37a896468d1f20a4"
dependencies = [
"dashmap",
"fxhash",
@@ -3217,9 +3280,9 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_react"
-version = "0.14.3"
+version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da3cfd8e900a1f1e26ea705f4614717b3deceec40d4b2fd4b1dc65a2b90de066"
+checksum = "b13d80190e922c2f83edec85c40372cbc0dd14d15973447471c0885ca408ce78"
dependencies = [
"base64 0.13.0",
"dashmap",
@@ -3267,7 +3330,7 @@ dependencies = [
"swc_common",
"swc_ecma_ast",
"swc_ecma_visit",
- "unicode-xid 0.2.1",
+ "unicode-xid 0.2.2",
]
[[package]]
@@ -3285,9 +3348,9 @@ dependencies = [
[[package]]
name = "swc_ecmascript"
-version = "0.31.3"
+version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45286c7e1199a0c9ef13951535f5c6f777228d26df2fcf2d2d81ef11d0f22e40"
+checksum = "9148743bf5d6dcc482fd4db219968b216bff61a475c7838e0f60e00886cfa2b4"
dependencies = [
"swc_ecma_ast",
"swc_ecma_codegen",
@@ -3365,7 +3428,7 @@ checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
dependencies = [
"proc-macro2 1.0.26",
"quote 1.0.9",
- "unicode-xid 0.2.1",
+ "unicode-xid 0.2.2",
]
[[package]]
@@ -3640,9 +3703,9 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.25"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
+checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
@@ -3651,9 +3714,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
+checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
dependencies = [
"lazy_static",
]
@@ -3852,9 +3915,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "untrusted"
@@ -3898,6 +3961,12 @@ dependencies = [
]
[[package]]
+name = "vcpkg"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
+
+[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/build.rs b/cli/build.rs
index eb8a71c8c..116ce8167 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -16,6 +16,7 @@ use deno_runtime::deno_url;
use deno_runtime::deno_web;
use deno_runtime::deno_webgpu;
use deno_runtime::deno_websocket;
+use deno_runtime::deno_webstorage;
use regex::Regex;
use std::collections::HashMap;
use std::env;
@@ -71,6 +72,7 @@ fn create_compiler_snapshot(
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
op_crate_libs.insert("deno.webgpu", deno_webgpu::get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
+ op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
// ensure we invalidate the build properly.
@@ -291,6 +293,10 @@ fn main() {
deno_websocket::get_declaration().display()
);
println!(
+ "cargo:rustc-env=DENO_WEBSTORAGE_LIB_PATH={}",
+ deno_webstorage::get_declaration().display()
+ );
+ println!(
"cargo:rustc-env=DENO_CRYPTO_LIB_PATH={}",
deno_crypto::get_declaration().display()
);
diff --git a/cli/dts/lib.deno.window.d.ts b/cli/dts/lib.deno.window.d.ts
index b7eca1e24..3c985e641 100644
--- a/cli/dts/lib.deno.window.d.ts
+++ b/cli/dts/lib.deno.window.d.ts
@@ -4,6 +4,7 @@
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
/// <reference lib="deno.webgpu" />
+/// <reference lib="deno.webstorage" />
/// <reference lib="esnext" />
declare class Window extends EventTarget {
@@ -22,12 +23,16 @@ declare class Window extends EventTarget {
navigator: Navigator;
Location: typeof Location;
location: Location;
+ localStorage: Storage;
+ sessionStorage: Storage;
}
declare var window: Window & typeof globalThis;
declare var self: Window & typeof globalThis;
declare var onload: ((this: Window, ev: Event) => any) | null;
declare var onunload: ((this: Window, ev: Event) => any) | null;
+declare var localStorage: Storage;
+declare var sessionStorage: Storage;
declare class Navigator {
constructor();
diff --git a/cli/main.rs b/cli/main.rs
index d20462c96..43c1cd0b2 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -204,6 +204,14 @@ pub fn create_main_worker(
no_color: !colors::use_color(),
get_error_class_fn: Some(&crate::errors::get_error_class_name),
location: program_state.flags.location.clone(),
+ location_data_dir: program_state.flags.location.clone().map(|loc| {
+ program_state
+ .dir
+ .root
+ .clone()
+ .join("location_data")
+ .join(checksum::gen(&[loc.to_string().as_bytes()]))
+ }),
blob_url_store: program_state.blob_url_store.clone(),
};
@@ -295,7 +303,7 @@ 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{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}",
crate::tsc::DENO_NS_LIB,
crate::tsc::DENO_CONSOLE_LIB,
crate::tsc::DENO_URL_LIB,
@@ -304,6 +312,7 @@ pub fn get_types(unstable: bool) -> String {
crate::tsc::DENO_FETCH_LIB,
crate::tsc::DENO_WEBGPU_LIB,
crate::tsc::DENO_WEBSOCKET_LIB,
+ crate::tsc::DENO_WEBSTORAGE_LIB,
crate::tsc::DENO_CRYPTO_LIB,
crate::tsc::SHARED_GLOBALS_LIB,
crate::tsc::WINDOW_LIB,
diff --git a/cli/standalone.rs b/cli/standalone.rs
index 83879a163..e0b131eb8 100644
--- a/cli/standalone.rs
+++ b/cli/standalone.rs
@@ -191,6 +191,7 @@ pub async fn run(
no_color: !colors::use_color(),
get_error_class_fn: Some(&get_error_class_name),
location: metadata.location,
+ location_data_dir: None,
blob_url_store,
};
let mut worker =
diff --git a/cli/tsc.rs b/cli/tsc.rs
index e2fc80676..7abae5ca1 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -37,6 +37,8 @@ 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"));
pub static DENO_WEBSOCKET_LIB: &str =
include_str!(env!("DENO_WEBSOCKET_LIB_PATH"));
+pub static DENO_WEBSTORAGE_LIB: &str =
+ include_str!(env!("DENO_WEBSTORAGE_LIB_PATH"));
pub static DENO_CRYPTO_LIB: &str = include_str!(env!("DENO_CRYPTO_LIB_PATH"));
pub static SHARED_GLOBALS_LIB: &str =
include_str!("dts/lib.deno.shared_globals.d.ts");
diff --git a/extensions/webstorage/01_webstorage.js b/extensions/webstorage/01_webstorage.js
new file mode 100644
index 000000000..a11d44068
--- /dev/null
+++ b/extensions/webstorage/01_webstorage.js
@@ -0,0 +1,190 @@
+((window) => {
+ const core = window.Deno.core;
+ const webidl = window.__bootstrap.webidl;
+
+ const _rid = Symbol("[[rid]]");
+
+ class Storage {
+ [_rid];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get length() {
+ webidl.assertBranded(this, Storage);
+ return core.opSync("op_webstorage_length", this[_rid]);
+ }
+
+ key(index) {
+ webidl.assertBranded(this, Storage);
+ const prefix = "Failed to execute 'key' on 'Storage'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ index = webidl.converters["unsigned long"](index, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ return core.opSync("op_webstorage_key", {
+ rid: this[_rid],
+ index,
+ });
+ }
+
+ setItem(key, value) {
+ webidl.assertBranded(this, Storage);
+ const prefix = "Failed to execute 'setItem' on 'Storage'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ key = webidl.converters.DOMString(key, {
+ prefix,
+ context: "Argument 1",
+ });
+ value = webidl.converters.DOMString(value, {
+ prefix,
+ context: "Argument 2",
+ });
+
+ core.opSync("op_webstorage_set", {
+ rid: this[_rid],
+ keyName: key,
+ keyValue: value,
+ });
+ }
+
+ getItem(key) {
+ webidl.assertBranded(this, Storage);
+ const prefix = "Failed to execute 'getItem' on 'Storage'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ key = webidl.converters.DOMString(key, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ return core.opSync("op_webstorage_get", {
+ rid: this[_rid],
+ keyName: key,
+ });
+ }
+
+ removeItem(key) {
+ webidl.assertBranded(this, Storage);
+ const prefix = "Failed to execute 'removeItem' on 'Storage'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ key = webidl.converters.DOMString(key, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ core.opSync("op_webstorage_remove", {
+ rid: this[_rid],
+ keyName: key,
+ });
+ }
+
+ clear() {
+ webidl.assertBranded(this, Storage);
+ core.opSync("op_webstorage_clear", this[_rid]);
+ }
+ }
+
+ function createStorage(persistent) {
+ if (persistent) window.location;
+
+ const rid = core.opSync("op_webstorage_open", persistent);
+
+ const storage = webidl.createBranded(Storage);
+ storage[_rid] = rid;
+
+ const proxy = new Proxy(storage, {
+ deleteProperty(target, key) {
+ if (typeof key == "symbol") {
+ delete target[key];
+ } else {
+ target.removeItem(key);
+ }
+ return true;
+ },
+ defineProperty(target, key, descriptor) {
+ if (typeof key == "symbol") {
+ Object.defineProperty(target, key, descriptor);
+ } else {
+ target.setItem(key, descriptor.value);
+ }
+ return true;
+ },
+ get(target, key) {
+ if (typeof key == "symbol") return target[key];
+ if (key in target) {
+ return Reflect.get(...arguments);
+ } else {
+ return target.getItem(key) ?? undefined;
+ }
+ },
+ set(target, key, value) {
+ if (typeof key == "symbol") {
+ Object.defineProperty(target, key, {
+ value,
+ configurable: true,
+ });
+ } else {
+ target.setItem(key, value);
+ }
+ return true;
+ },
+ has(target, p) {
+ return (typeof target.getItem(p)) === "string";
+ },
+ ownKeys() {
+ return core.opSync("op_webstorage_iterate_keys", rid);
+ },
+ getOwnPropertyDescriptor(target, key) {
+ if (arguments.length === 1) {
+ return undefined;
+ }
+ if (key in target) {
+ return undefined;
+ }
+ const value = target.getItem(key);
+ if (value === null) {
+ return undefined;
+ }
+ return {
+ value,
+ enumerable: true,
+ configurable: true,
+ writable: true,
+ };
+ },
+ });
+
+ proxy[Symbol.for("Deno.customInspect")] = function (inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ length: this.length,
+ ...Object.fromEntries(Object.entries(proxy)),
+ })
+ }`;
+ };
+
+ return proxy;
+ }
+
+ let localStorage;
+ let sessionStorage;
+
+ window.__bootstrap.webStorage = {
+ localStorage() {
+ if (!localStorage) {
+ localStorage = createStorage(true);
+ }
+ return localStorage;
+ },
+ sessionStorage() {
+ if (!sessionStorage) {
+ sessionStorage = createStorage(false);
+ }
+ return sessionStorage;
+ },
+ Storage,
+ };
+})(this);
diff --git a/extensions/webstorage/Cargo.toml b/extensions/webstorage/Cargo.toml
new file mode 100644
index 000000000..acfaf6a16
--- /dev/null
+++ b/extensions/webstorage/Cargo.toml
@@ -0,0 +1,19 @@
+# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_webstorage"
+version = "0.1.0"
+edition = "2018"
+description = "Implementation of WebStorage API 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.86.0", path = "../../core" }
+rusqlite = { version = "0.25.0", features = ["unlock_notify", "bundled"] }
+serde = { version = "1.0.125", features = ["derive"] }
diff --git a/extensions/webstorage/README.md b/extensions/webstorage/README.md
new file mode 100644
index 000000000..a0f8a0613
--- /dev/null
+++ b/extensions/webstorage/README.md
@@ -0,0 +1,5 @@
+# deno_webstorage
+
+This op crate implements the WebStorage spec in Deno.
+
+Spec: https://html.spec.whatwg.org/multipage/webstorage.html
diff --git a/extensions/webstorage/lib.deno_webstorage.d.ts b/extensions/webstorage/lib.deno_webstorage.d.ts
new file mode 100644
index 000000000..bf438e005
--- /dev/null
+++ b/extensions/webstorage/lib.deno_webstorage.d.ts
@@ -0,0 +1,42 @@
+// 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" />
+
+/** This Web Storage API interface provides access to a particular domain's session or local storage. It allows, for example, the addition, modification, or deletion of stored data items. */
+interface Storage {
+ /**
+ * Returns the number of key/value pairs currently present in the list associated with the object.
+ */
+ readonly length: number;
+ /**
+ * Empties the list associated with the object of all key/value pairs, if there are any.
+ */
+ clear(): void;
+ /**
+ * Returns the current value associated with the given key, or null if the given key does not exist in the list associated with the object.
+ */
+ getItem(key: string): string | null;
+ /**
+ * Returns the name of the nth key in the list, or null if n is greater than or equal to the number of key/value pairs in the object.
+ */
+ key(index: number): string | null;
+ /**
+ * Removes the key/value pair with the given key from the list associated with the object, if a key/value pair with the given key exists.
+ */
+ removeItem(key: string): void;
+ /**
+ * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.
+ *
+ * Throws a "QuotaExceededError" DOMException exception if the new value couldn't be set. (Setting could fail if, e.g., the user has disabled storage for the site, or if the quota has been exceeded.)
+ */
+ setItem(key: string, value: string): void;
+ [name: string]: any;
+}
+
+declare var Storage: {
+ prototype: Storage;
+ new (): Storage;
+};
diff --git a/extensions/webstorage/lib.rs b/extensions/webstorage/lib.rs
new file mode 100644
index 000000000..90ae0598a
--- /dev/null
+++ b/extensions/webstorage/lib.rs
@@ -0,0 +1,316 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::include_js_files;
+use deno_core::op_sync;
+use deno_core::Extension;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ZeroCopyBuf;
+use rusqlite::params;
+use rusqlite::Connection;
+use rusqlite::OptionalExtension;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::fmt;
+use std::path::PathBuf;
+
+#[derive(Clone)]
+struct LocationDataDir(PathBuf);
+
+pub fn init(location_data_dir: Option<PathBuf>) -> Extension {
+ Extension::builder()
+ .js(include_js_files!(
+ prefix "deno:extensions/webstorage",
+ "01_webstorage.js",
+ ))
+ .ops(vec![
+ ("op_webstorage_open", op_sync(op_webstorage_open)),
+ ("op_webstorage_length", op_sync(op_webstorage_length)),
+ ("op_webstorage_key", op_sync(op_webstorage_key)),
+ ("op_webstorage_set", op_sync(op_webstorage_set)),
+ ("op_webstorage_get", op_sync(op_webstorage_get)),
+ ("op_webstorage_remove", op_sync(op_webstorage_remove)),
+ ("op_webstorage_clear", op_sync(op_webstorage_clear)),
+ (
+ "op_webstorage_iterate_keys",
+ op_sync(op_webstorage_iterate_keys),
+ ),
+ ])
+ .state(move |state| {
+ if let Some(location_data_dir) = location_data_dir.clone() {
+ state.put(LocationDataDir(location_data_dir));
+ }
+ Ok(())
+ })
+ .build()
+}
+
+pub fn get_declaration() -> PathBuf {
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webstorage.d.ts")
+}
+
+struct WebStorageConnectionResource(Connection);
+
+impl Resource for WebStorageConnectionResource {
+ fn name(&self) -> Cow<str> {
+ "webStorage".into()
+ }
+}
+
+pub fn op_webstorage_open(
+ state: &mut OpState,
+ persistent: bool,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<u32, AnyError> {
+ let connection = if persistent {
+ let path = state.try_borrow::<LocationDataDir>().ok_or_else(|| {
+ DomExceptionNotSupportedError::new(
+ "LocalStorage is not supported in this context.",
+ )
+ })?;
+ std::fs::create_dir_all(&path.0)?;
+ Connection::open(path.0.join("local_storage"))?
+ } else {
+ Connection::open_in_memory()?
+ };
+
+ connection.execute(
+ "CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)",
+ params![],
+ )?;
+
+ let rid = state
+ .resource_table
+ .add(WebStorageConnectionResource(connection));
+ Ok(rid)
+}
+
+pub fn op_webstorage_length(
+ state: &mut OpState,
+ rid: u32,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<u32, AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut stmt = resource.0.prepare("SELECT COUNT(*) FROM data")?;
+
+ let length: u32 = stmt.query_row(params![], |row| row.get(0))?;
+
+ Ok(length)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct KeyArgs {
+ rid: u32,
+ index: u32,
+}
+
+pub fn op_webstorage_key(
+ state: &mut OpState,
+ args: KeyArgs,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<Option<String>, AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(args.rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut stmt = resource
+ .0
+ .prepare("SELECT key FROM data LIMIT 1 OFFSET ?")?;
+
+ let key: Option<String> = stmt
+ .query_row(params![args.index], |row| row.get(0))
+ .optional()?;
+
+ Ok(key)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SetArgs {
+ rid: u32,
+ key_name: String,
+ key_value: String,
+}
+
+pub fn op_webstorage_set(
+ state: &mut OpState,
+ args: SetArgs,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<(), AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(args.rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut stmt = resource
+ .0
+ .prepare("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?;
+ let size: u32 = stmt.query_row(params![], |row| row.get(0))?;
+
+ if size >= 5000000 {
+ return Err(
+ DomExceptionQuotaExceededError::new("Exceeded maximum storage size")
+ .into(),
+ );
+ }
+
+ resource.0.execute(
+ "INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)",
+ params![args.key_name, args.key_value],
+ )?;
+
+ Ok(())
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GetArgs {
+ rid: u32,
+ key_name: String,
+}
+
+pub fn op_webstorage_get(
+ state: &mut OpState,
+ args: GetArgs,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<Option<String>, AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(args.rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut stmt = resource.0.prepare("SELECT value FROM data WHERE key = ?")?;
+
+ let val = stmt
+ .query_row(params![args.key_name], |row| row.get(0))
+ .optional()?;
+
+ Ok(val)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoveArgs {
+ rid: u32,
+ key_name: String,
+}
+
+pub fn op_webstorage_remove(
+ state: &mut OpState,
+ args: RemoveArgs,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<(), AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(args.rid)
+ .ok_or_else(bad_resource_id)?;
+
+ resource
+ .0
+ .execute("DELETE FROM data WHERE key = ?", params![args.key_name])?;
+
+ Ok(())
+}
+
+pub fn op_webstorage_clear(
+ state: &mut OpState,
+ rid: u32,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<(), AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(rid)
+ .ok_or_else(bad_resource_id)?;
+
+ resource.0.execute("DROP TABLE data", params![])?;
+ resource.0.execute(
+ "CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)",
+ params![],
+ )?;
+
+ Ok(())
+}
+
+pub fn op_webstorage_iterate_keys(
+ state: &mut OpState,
+ rid: u32,
+ _zero_copy: Option<ZeroCopyBuf>,
+) -> Result<Vec<String>, AnyError> {
+ let resource = state
+ .resource_table
+ .get::<WebStorageConnectionResource>(rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut stmt = resource.0.prepare("SELECT key FROM data")?;
+
+ let keys = stmt
+ .query_map(params![], |row| row.get::<_, String>(0))?
+ .map(|r| r.unwrap())
+ .collect();
+
+ Ok(keys)
+}
+
+#[derive(Debug)]
+pub struct DomExceptionQuotaExceededError {
+ pub msg: String,
+}
+
+impl DomExceptionQuotaExceededError {
+ pub fn new(msg: &str) -> Self {
+ DomExceptionQuotaExceededError {
+ msg: msg.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for DomExceptionQuotaExceededError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad(&self.msg)
+ }
+}
+
+impl std::error::Error for DomExceptionQuotaExceededError {}
+
+pub fn get_quota_exceeded_error_class_name(
+ e: &AnyError,
+) -> Option<&'static str> {
+ e.downcast_ref::<DomExceptionQuotaExceededError>()
+ .map(|_| "DOMExceptionQuotaExceededError")
+}
+
+#[derive(Debug)]
+pub struct DomExceptionNotSupportedError {
+ pub msg: String,
+}
+
+impl DomExceptionNotSupportedError {
+ pub fn new(msg: &str) -> Self {
+ DomExceptionNotSupportedError {
+ msg: msg.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for DomExceptionNotSupportedError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad(&self.msg)
+ }
+}
+
+impl std::error::Error for DomExceptionNotSupportedError {}
+
+pub fn get_not_supported_error_class_name(
+ e: &AnyError,
+) -> Option<&'static str> {
+ e.downcast_ref::<DomExceptionNotSupportedError>()
+ .map(|_| "DOMExceptionNotSupportedError")
+}
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 8acd61f7e..4cefa23ee 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -29,6 +29,7 @@ deno_web = { path = "../extensions/web", version = "0.35.0" }
deno_webgpu = { path = "../extensions/webgpu", version = "0.6.0" }
deno_webidl = { path = "../extensions/webidl", version = "0.5.0" }
deno_websocket = { path = "../extensions/websocket", version = "0.10.0" }
+deno_webstorage = { path = "../extensions/webstorage", version = "0.1.0" }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1.11"
@@ -46,6 +47,7 @@ deno_web = { path = "../extensions/web", version = "0.35.0" }
deno_webgpu = { path = "../extensions/webgpu", version = "0.6.0" }
deno_webidl = { path = "../extensions/webidl", version = "0.5.0" }
deno_websocket = { path = "../extensions/websocket", version = "0.10.0" }
+deno_webstorage = { path = "../extensions/webstorage", version = "0.1.0" }
atty = "0.2.14"
bytes = "1"
diff --git a/runtime/build.rs b/runtime/build.rs
index b6a9da582..e1cae7195 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -48,6 +48,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
"".to_owned(),
None,
),
+ deno_webstorage::init(None),
deno_crypto::init(None),
deno_webgpu::init(false),
deno_timers::init::<deno_timers::NoTimersPermission>(),
diff --git a/runtime/errors.rs b/runtime/errors.rs
index f9ef947a3..c5e93d65b 100644
--- a/runtime/errors.rs
+++ b/runtime/errors.rs
@@ -157,6 +157,8 @@ fn get_nix_error_class(error: &nix::Error) -> &'static str {
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
.or_else(|| deno_webgpu::error::get_error_class_name(e))
+ .or_else(|| deno_webstorage::get_quota_exceeded_error_class_name(e))
+ .or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| {
e.downcast_ref::<dlopen::Error>()
.map(get_dlopen_error_class)
diff --git a/runtime/examples/hello_runtime.rs b/runtime/examples/hello_runtime.rs
index 9c3cbd67e..80a258c17 100644
--- a/runtime/examples/hello_runtime.rs
+++ b/runtime/examples/hello_runtime.rs
@@ -40,6 +40,7 @@ async fn main() -> Result<(), AnyError> {
no_color: false,
get_error_class_fn: Some(&get_error_class_name),
location: None,
+ location_data_dir: None,
blob_url_store: BlobUrlStore::default(),
};
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index fee7cd2d7..d2926bb1f 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -28,6 +28,7 @@ delete Object.prototype.__proto__;
const fileReader = window.__bootstrap.fileReader;
const webgpu = window.__bootstrap.webgpu;
const webSocket = window.__bootstrap.webSocket;
+ const webStorage = window.__bootstrap.webStorage;
const file = window.__bootstrap.file;
const formData = window.__bootstrap.formData;
const fetch = window.__bootstrap.fetch;
@@ -190,6 +191,18 @@ delete Object.prototype.__proto__;
return new DOMException(msg, "OperationError");
},
);
+ core.registerErrorBuilder(
+ "DOMExceptionQuotaExceededError",
+ function DOMExceptionQuotaExceededError(msg) {
+ return new DOMException(msg, "QuotaExceededError");
+ },
+ );
+ core.registerErrorBuilder(
+ "DOMExceptionNotSupportedError",
+ function DOMExceptionNotSupportedError(msg) {
+ return new DOMException(msg, "NotSupported");
+ },
+ );
}
class Navigator {
@@ -351,6 +364,17 @@ delete Object.prototype.__proto__;
alert: util.writable(prompt.alert),
confirm: util.writable(prompt.confirm),
prompt: util.writable(prompt.prompt),
+ localStorage: {
+ configurable: true,
+ enumerable: true,
+ get: webStorage.localStorage,
+ },
+ sessionStorage: {
+ configurable: true,
+ enumerable: true,
+ get: webStorage.sessionStorage,
+ },
+ Storage: util.nonEnumerable(webStorage.Storage),
};
const workerRuntimeGlobalProperties = {
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 61b8fc77e..d45a72727 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -10,6 +10,7 @@ pub use deno_web;
pub use deno_webgpu;
pub use deno_webidl;
pub use deno_websocket;
+pub use deno_webstorage;
pub mod colors;
pub mod errors;
diff --git a/runtime/worker.rs b/runtime/worker.rs
index b4c27b4f4..ab54e2153 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -69,6 +69,7 @@ pub struct WorkerOptions {
pub no_color: bool,
pub get_error_class_fn: Option<GetErrorClassFn>,
pub location: Option<Url>,
+ pub location_data_dir: Option<std::path::PathBuf>,
pub blob_url_store: BlobUrlStore,
}
@@ -104,6 +105,7 @@ impl MainWorker {
options.user_agent.clone(),
options.ca_data.clone(),
),
+ deno_webstorage::init(options.location_data_dir.clone()),
deno_crypto::init(options.seed),
deno_webgpu::init(options.unstable),
deno_timers::init::<Permissions>(),
@@ -291,6 +293,7 @@ mod tests {
no_color: true,
get_error_class_fn: None,
location: None,
+ location_data_dir: None,
blob_url_store: BlobUrlStore::default(),
};
diff --git a/tools/wpt.ts b/tools/wpt.ts
index e7da43e64..13f8b6467 100755
--- a/tools/wpt.ts
+++ b/tools/wpt.ts
@@ -34,6 +34,10 @@ import {
red,
yellow,
} from "https://deno.land/std@0.84.0/fmt/colors.ts";
+import {
+ writeAll,
+ writeAllSync,
+} from "https://deno.land/std@0.95.0/io/util.ts";
import { saveExpectation } from "./wpt/utils.ts";
const command = Deno.args[0];
@@ -104,7 +108,7 @@ async function setup() {
throw err;
}
});
- await Deno.writeAll(
+ await writeAll(
file,
new TextEncoder().encode(
"\n\n# Configured for Web Platform Tests (Deno)\n" + entries,
@@ -150,7 +154,7 @@ async function run() {
rest.length == 0 ? undefined : rest,
expectation,
);
- assertAllExpectationsHaveTests(expectation, tests);
+ assertAllExpectationsHaveTests(expectation, tests, rest);
console.log(`Going to run ${tests.length} test files.`);
const results = await runWithTestUtil(false, async () => {
@@ -182,6 +186,7 @@ async function run() {
function assertAllExpectationsHaveTests(
expectation: Expectation,
testsToRun: TestToRun[],
+ filter?: string[],
): void {
const tests = new Set(testsToRun.map((t) => t.path));
const missingTests: string[] = [];
@@ -189,6 +194,12 @@ function assertAllExpectationsHaveTests(
function walk(parentExpectation: Expectation, parent: string) {
for (const key in parentExpectation) {
const path = `${parent}/${key}`;
+ if (
+ filter &&
+ !filter.find((filter) => path.substring(1).startsWith(filter))
+ ) {
+ continue;
+ }
const expectation = parentExpectation[key];
if (typeof expectation == "boolean" || Array.isArray(expectation)) {
if (!tests.has(path)) {
@@ -422,7 +433,7 @@ function analyzeTestResult(
function reportVariation(result: TestResult, expectation: boolean | string[]) {
if (result.status !== 0) {
console.log(`test stderr:`);
- Deno.writeAllSync(Deno.stdout, new TextEncoder().encode(result.stderr));
+ writeAllSync(Deno.stdout, new TextEncoder().encode(result.stderr));
const expectFail = expectation === false;
console.log(
@@ -508,7 +519,7 @@ function createReportTestCase(expectation: boolean | string[]) {
break;
}
- console.log(simpleMessage);
+ writeAllSync(Deno.stdout, new TextEncoder().encode(simpleMessage + "\n"));
};
}
@@ -536,7 +547,12 @@ function discoverTestsToRun(
) {
if (!path) continue;
const url = new URL(path, "http://web-platform.test:8000");
- if (!url.pathname.endsWith(".any.html")) continue;
+ if (
+ !url.pathname.endsWith(".any.html") &&
+ !url.pathname.endsWith(".window.html")
+ ) {
+ continue;
+ }
const finalPath = url.pathname + url.search;
const split = finalPath.split("/");
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index 983f73e53..afdc0cb1f 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -1064,6 +1064,66 @@
}
}
},
+ "webstorage": {
+ "defineProperty.window.html": true,
+ "set.window.html": true,
+ "storage_enumerate.window.html": true,
+ "storage_in.window.html": true,
+ "event_constructor.window.html": false,
+ "event_initstorageevent.window.html": false,
+ "missing_arguments.window.html": true,
+ "storage_builtins.window.html": true,
+ "storage_clear.window.html": true,
+ "storage_functions_not_overwritten.window.html": true,
+ "storage_getitem.window.html": true,
+ "storage_indexing.window.html": true,
+ "storage_key.window.html": true,
+ "storage_key_empty_string.window.html": true,
+ "storage_length.window.html": true,
+ "storage_local_setitem_quotaexceedederr.window.html": true,
+ "storage_local_window_open.window.html": false,
+ "storage_removeitem.window.html": true,
+ "storage_session_setitem_quotaexceedederr.window.html": true,
+ "storage_session_window_noopener.window.html": false,
+ "storage_session_window_open.window.html": false,
+ "storage_set_value_enumerate.window.html": true,
+ "storage_setitem.window.html": [
+ "localStorage[\"\ud800\"]",
+ "localStorage[] = \"\ud800\"",
+ "localStorage[\"\udbff\"]",
+ "localStorage[] = \"\udbff\"",
+ "localStorage[\"\udc00\"]",
+ "localStorage[] = \"\udc00\"",
+ "localStorage[\"\udfff\"]",
+ "localStorage[] = \"\udfff\"",
+ "localStorage[\"\\ufffd\"]",
+ "localStorage[] = \"\\ufffd\"",
+ "localStorage[\"\ud83ca\"]",
+ "localStorage[] = \"\ud83ca\"",
+ "localStorage[\"a\udf4d\"]",
+ "localStorage[] = \"a\udf4d\"",
+ "sessionStorage[\"\ud800\"]",
+ "sessionStorage[] = \"\ud800\"",
+ "sessionStorage[\"\udbff\"]",
+ "sessionStorage[] = \"\udbff\"",
+ "sessionStorage[\"\udc00\"]",
+ "sessionStorage[] = \"\udc00\"",
+ "sessionStorage[\"\udfff\"]",
+ "sessionStorage[] = \"\udfff\"",
+ "sessionStorage[\"\\ufffd\"]",
+ "sessionStorage[] = \"\\ufffd\"",
+ "sessionStorage[\"\ud83ca\"]",
+ "sessionStorage[] = \"\ud83ca\"",
+ "sessionStorage[\"a\udf4d\"]",
+ "sessionStorage[] = \"a\udf4d\""
+ ],
+ "storage_string_conversion.window.html": true,
+ "storage_supported_property_names.window.html": true,
+ "symbol-props.window.html": [
+ "localStorage: defineProperty not configurable",
+ "sessionStorage: defineProperty not configurable"
+ ]
+ },
"xhr": {
"formdata": {
"append.any.html": true,