1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 16:19:12 -05:00

feat: add WebStorage API (#7819)

This commit introduces localStorage and sessionStorage.
This commit is contained in:
crowlKats 2021-05-10 12:02:47 +02:00 committed by GitHub
parent 32ad8f77d6
commit dfe528198d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 819 additions and 45 deletions

147
Cargo.lock generated
View file

@ -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",
@ -787,6 +788,15 @@ dependencies = [
"webpki-roots",
]
[[package]]
name = "deno_webstorage"
version = "0.1.0"
dependencies = [
"deno_core",
"rusqlite",
"serde",
]
[[package]]
name = "derive_more"
version = "0.99.13"
@ -1013,6 +1023,18 @@ dependencies = [
"libc",
]
[[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"
@ -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",
@ -1509,6 +1531,15 @@ dependencies = [
"ahash 0.4.7",
]
[[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"
@ -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"
@ -1801,6 +1832,17 @@ dependencies = [
"winapi 0.3.9",
]
[[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"
@ -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"
@ -2253,6 +2295,12 @@ version = "0.1.0"
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"
@ -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"
@ -2624,6 +2672,21 @@ dependencies = [
"serde",
]
[[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"
@ -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"
@ -3897,6 +3960,12 @@ dependencies = [
"serde",
]
[[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"

View file

@ -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.
@ -290,6 +292,10 @@ fn main() {
"cargo:rustc-env=DENO_WEBSOCKET_LIB_PATH={}",
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()

View file

@ -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();

View file

@ -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,

View file

@ -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 =

View file

@ -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");

View file

@ -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);

View file

@ -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"] }

View file

@ -0,0 +1,5 @@
# deno_webstorage
This op crate implements the WebStorage spec in Deno.
Spec: https://html.spec.whatwg.org/multipage/webstorage.html

View file

@ -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;
};

View file

@ -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")
}

View file

@ -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"

View file

@ -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>(),

View file

@ -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)

View file

@ -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(),
};

View file

@ -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 = {

View file

@ -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;

View file

@ -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(),
};

View file

@ -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("/");

View file

@ -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,