Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] implement Storage APIs. localStorage/sessionStorage #3525

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
491 changes: 288 additions & 203 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ url = "1.7.2"
utime = "0.2.1"
webpki = "0.21.0"
webpki-roots = "0.17.0"
rusty-leveldb = "0.3.0"

[target.'cfg(windows)'.dependencies]
winapi = "0.3.8"
Expand Down
8 changes: 8 additions & 0 deletions cli/deno_dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub struct DenoDir {
pub deps_cache: DiskCache,
/// Used by TsCompiler to cache compiler output.
pub gen_cache: DiskCache,
/// Used by localStorage and indexDB in the future.
pub storage: PathBuf,
/// Used by localStorage.
pub localstorage: PathBuf,
}

impl DenoDir {
Expand All @@ -31,11 +35,15 @@ impl DenoDir {
let root: PathBuf = custom_root.unwrap_or(default);
let deps_path = root.join("deps");
let gen_path = root.join("gen");
let storage_path = root.join("storage");
let localstorage_path = storage_path.join("localstorage");

let deno_dir = Self {
root,
deps_cache: DiskCache::new(&deps_path),
gen_cache: DiskCache::new(&gen_path),
storage: storage_path,
localstorage: localstorage_path,
};

Ok(deno_dir)
Expand Down
12 changes: 12 additions & 0 deletions cli/js/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ export let OP_FETCH_ASSET: number;
export let OP_DIAL_TLS: number;
export let OP_HOSTNAME: number;
export let OP_OPEN_PLUGIN: number;
export let OP_LOCALSTORAGE_GET_ITEM: number;
export let OP_LOCALSTORAGE_SET_ITEM: number;
export let OP_LOCALSTORAGE_REMOVE_ITEM: number;
export let OP_LOCALSTORAGE_GET_LEN: number;
export let OP_LOCALSTORAGE_CLEAN: number;
export let OP_LOCALSTORAGE_KEY: number;

const PLUGIN_ASYNC_HANDLER_MAP: Map<number, AsyncHandler> = new Map();

Expand Down Expand Up @@ -120,6 +126,12 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void {
case OP_MAKE_TEMP_DIR:
case OP_DIAL_TLS:
case OP_FETCH_SOURCE_FILES:
case OP_LOCALSTORAGE_GET_ITEM:
case OP_LOCALSTORAGE_SET_ITEM:
case OP_LOCALSTORAGE_REMOVE_ITEM:
case OP_LOCALSTORAGE_GET_LEN:
case OP_LOCALSTORAGE_CLEAN:
case OP_LOCALSTORAGE_KEY:
json.asyncMsgFromRust(opId, ui8);
break;
default:
Expand Down
11 changes: 11 additions & 0 deletions cli/js/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ import * as urlSearchParams from "./url_search_params.ts";
import * as workers from "./workers.ts";
import * as performanceUtil from "./performance.ts";
import * as request from "./request.ts";
import {
Storage as StorageClass,
StorageImpl,
LocalStorage,
SessionStorage
} from "./storage.ts";

// These imports are not exposed and therefore are fine to just import the
// symbols required.
Expand Down Expand Up @@ -118,6 +124,11 @@ export type URL = url.URL;
window.URLSearchParams = urlSearchParams.URLSearchParams;
export type URLSearchParams = domTypes.URLSearchParams;

window.Storage = StorageClass;
export type Storage = StorageImpl;
window.sessionStorage = new SessionStorage();
window.localStorage = new LocalStorage();

// Using the `as` keyword to use standard compliant interfaces as the Deno
// implementations contain some implementation details we wouldn't want to
// expose in the runtime type library.
Expand Down
26 changes: 26 additions & 0 deletions cli/js/lib.deno_runtime.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1505,6 +1505,9 @@ declare interface Window {
workerClose: typeof __workers.workerClose;
postMessage: typeof __workers.postMessage;
Worker: typeof __workers.WorkerImpl;
Storage: __storage.Storage;
localStorage: typeof __storage.localStorage;
sessionStorage: typeof __storage.sessionStorage;
addEventListener: (
type: string,
callback: (event: __domTypes.Event) => void | null,
Expand Down Expand Up @@ -1555,6 +1558,10 @@ declare const workerMain: typeof __workers.workerMain;
declare const workerClose: typeof __workers.workerClose;
declare const postMessage: typeof __workers.postMessage;
declare const Worker: typeof __workers.WorkerImpl;
declare const Storage: typeof __storage.Storage;
declare const localStorage: typeof __storage.localStorage;
declare const sessionStorage: typeof __storage.sessionStorage;

declare const addEventListener: (
type: string,
callback: (event: __domTypes.Event) => void | null,
Expand Down Expand Up @@ -3032,3 +3039,22 @@ declare namespace JSX {
[elemName: string]: any;
}
}

declare namespace __storage {
export interface Storage {
readonly length: number;
key(index: number): string | null;
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
clear(): void;
}

export const Storage: {
prototype: Storage;
new (): Storage;
};

export const localStorage: Storage;
export const sessionStorage: Storage;
}
142 changes: 142 additions & 0 deletions cli/js/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { customInspect } from "./deno.ts";
import * as dispatch from "./dispatch.ts";
import { sendSync } from "./dispatch_json.ts";

export interface StorageImpl {
readonly length: number;
key(index: number): string | null;
getItem(key: string): string | null;
setItem(key: string, value: string): void;
removeItem(key: string): void;
clear(): void;
// TODO: do not support yet
// [key: string]: any;
// [index: number]: string;
}

const kvMapSymbol = Symbol("kv_map_symbol");
const localStorageOriginSymbol = Symbol("localstorage_origin_symbol");

interface KVMap {
[k: string]: string;
}

export class Storage implements StorageImpl {
get length(): number {
return 0;
}
key(_index: number): string | null {
return null;
}
getItem(_key: string): string | null {
return null;
}
setItem(_key: string, _value: string): void {
return;
}
removeItem(_key: string): void {
return;
}
clear(): void {
return;
}
[customInspect](): string {
return `Storage {}`;
}
}

export class SessionStorage extends Storage {
private [kvMapSymbol]: KVMap = {};
get length(): number {
return Object.keys(this[kvMapSymbol]).length;
}
key(index: number): string | null {
const map = this[kvMapSymbol];

const keys = Object.keys(map);
const key = keys[index];

if (key === undefined) {
return null;
}

return map[key];
}
getItem(key: string): string | null {
const val = this[kvMapSymbol][key];
return val === undefined ? null : val;
}

setItem(key: string, value: string): void {
this[kvMapSymbol][key] = value.toString();
return;
}

removeItem(key: string): void {
delete this[kvMapSymbol][key];
return;
}

clear(): void {
for (const key in this[kvMapSymbol]) {
delete this[kvMapSymbol][key];
}
return;
}

[customInspect](): string {
const keys = Object.keys(this[kvMapSymbol]);
const str = keys
.map((key: string) => {
return `${key}: "${this.getItem(key)}"`;
})
.concat([`length: ${this.length}`])
.join(", ");
return `Storage {${str}}`;
}
}

export class LocalStorage extends Storage {
// TODO: dispatch data with origin.
private [localStorageOriginSymbol] = "";
constructor(origin = "") {
super();
this[localStorageOriginSymbol] = origin;
}
get length(): number {
return sendSync(dispatch.OP_LOCALSTORAGE_GET_LEN);
}
key(index: number): string | null {
const res = sendSync(dispatch.OP_LOCALSTORAGE_KEY, {
index: index
});

return res.value;
}
getItem(key: string): string | null {
const res = sendSync(dispatch.OP_LOCALSTORAGE_GET_ITEM, { key: key });

return res.value;
}

setItem(key: string, value: string): void {
sendSync(dispatch.OP_LOCALSTORAGE_SET_ITEM, {
key: key,
value: value
});
}

removeItem(key: string): void {
sendSync(dispatch.OP_LOCALSTORAGE_REMOVE_ITEM, { key: key });
}

clear(): void {
sendSync(dispatch.OP_LOCALSTORAGE_CLEAN);
}

[customInspect](): string {
// TODO: finish this.
return `Storage {}`;
}
}
61 changes: 61 additions & 0 deletions cli/js/storage_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { testPerm, assertEquals } from "./test_util.ts";

testPerm({}, async function storage(): Promise<void> {
assertEquals(sessionStorage instanceof Storage, true);
assertEquals(sessionStorage.__proto__ === Storage.prototype, true);

assertEquals(localStorage instanceof Storage, true);
assertEquals(localStorage.__proto__ === Storage.prototype, true);
});

testPerm({}, async function sessionStorageBasic(): Promise<void> {
assertEquals(sessionStorage.length, 0);
assertEquals(sessionStorage.key(0), null);
assertEquals(sessionStorage.key(1), null);
assertEquals(sessionStorage.setItem("foo", "bar"), undefined);
assertEquals(sessionStorage.length, 1);
assertEquals(sessionStorage.key(0), "foo");
assertEquals(sessionStorage.getItem("foo"), "bar");
assertEquals(sessionStorage.removeItem("foo"), undefined);
assertEquals(sessionStorage.length, 0);
assertEquals(sessionStorage.key(0), null);
assertEquals(sessionStorage.getItem("not_exist_key"), null);
assertEquals(sessionStorage.setItem("bar", "foo"), undefined);
assertEquals(sessionStorage.length, 1);
assertEquals(sessionStorage.key(0), "bar");
assertEquals(sessionStorage.clear(), undefined);
assertEquals(sessionStorage.length, 0);
assertEquals(sessionStorage.key(0), null);
});

testPerm({}, async function localStorageBasic(): Promise<void> {
assertEquals(localStorage.length, 0);
assertEquals(localStorage.key(0), null);
assertEquals(localStorage.key(1), null);
assertEquals(localStorage.setItem("foo", "bar"), undefined);
assertEquals(localStorage.length, 1);
assertEquals(localStorage.key(0), "foo");
assertEquals(localStorage.getItem("foo"), "bar");
assertEquals(localStorage.removeItem("foo"), undefined);
assertEquals(localStorage.length, 0);
assertEquals(localStorage.key(0), null);
assertEquals(localStorage.getItem("not_exist_key"), null);
assertEquals(localStorage.setItem("bar", "foo"), undefined);
assertEquals(localStorage.length, 1);
assertEquals(localStorage.key(0), "bar");
assertEquals(localStorage.clear(), undefined);
assertEquals(localStorage.length, 0);
assertEquals(localStorage.key(0), null);
});

testPerm({}, async function localStoragePersistence(): Promise<void> {
assertEquals(localStorage.length, 0);
assertEquals(localStorage.key(0), null);
assertEquals(localStorage.key(1), null);
assertEquals(localStorage.setItem("foo", "bar"), undefined);
assertEquals(localStorage.length, 1);
assertEquals(localStorage.key(0), "foo");

// TODO: start a child process and test if the data is persistent
});
1 change: 1 addition & 0 deletions cli/ops/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod process;
pub mod random;
pub mod repl;
pub mod resources;
pub mod storage;
pub mod timers;
pub mod tls;
pub mod workers;
Loading