summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock8
-rw-r--r--cli/build.rs6
-rw-r--r--cli/dts/lib.deno.shared_globals.d.ts1
-rw-r--r--cli/main.rs3
-rw-r--r--cli/tsc.rs1
-rw-r--r--op_crates/fetch/internal.d.ts10
-rw-r--r--op_crates/fetch/lib.deno_fetch.d.ts36
-rw-r--r--op_crates/fetch/lib.rs4
-rw-r--r--op_crates/file/01_file.js (renamed from op_crates/fetch/21_file.js)2
-rw-r--r--op_crates/file/02_filereader.js334
-rw-r--r--op_crates/file/Cargo.toml17
-rw-r--r--op_crates/file/README.md5
-rw-r--r--op_crates/file/internal.d.ts18
-rw-r--r--op_crates/file/lib.deno_file.d.ts40
-rw-r--r--op_crates/file/lib.rs22
-rw-r--r--op_crates/web/21_filereader.js261
-rw-r--r--op_crates/web/internal.d.ts6
-rw-r--r--op_crates/web/lib.rs4
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/lib.rs1
m---------test_util/wpt0
-rw-r--r--tools/wpt/expectation.json7
23 files changed, 469 insertions, 320 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ef2d7a272..f5af15f8c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -617,6 +617,13 @@ dependencies = [
]
[[package]]
+name = "deno_file"
+version = "0.1.0"
+dependencies = [
+ "deno_core",
+]
+
+[[package]]
name = "deno_lint"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -643,6 +650,7 @@ dependencies = [
"deno_core",
"deno_crypto",
"deno_fetch",
+ "deno_file",
"deno_url",
"deno_web",
"deno_webgpu",
diff --git a/cli/build.rs b/cli/build.rs
index 6606242e2..1b1e913d2 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -11,6 +11,7 @@ use deno_core::RuntimeOptions;
use deno_runtime::deno_console;
use deno_runtime::deno_crypto;
use deno_runtime::deno_fetch;
+use deno_runtime::deno_file;
use deno_runtime::deno_url;
use deno_runtime::deno_web;
use deno_runtime::deno_webgpu;
@@ -66,6 +67,7 @@ fn create_compiler_snapshot(
op_crate_libs.insert("deno.console", deno_console::get_declaration());
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.file", deno_file::get_declaration());
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());
@@ -271,6 +273,10 @@ fn main() {
deno_web::get_declaration().display()
);
println!(
+ "cargo:rustc-env=DENO_FILE_LIB_PATH={}",
+ deno_file::get_declaration().display()
+ );
+ println!(
"cargo:rustc-env=DENO_FETCH_LIB_PATH={}",
deno_fetch::get_declaration().display()
);
diff --git a/cli/dts/lib.deno.shared_globals.d.ts b/cli/dts/lib.deno.shared_globals.d.ts
index 84272caeb..d96d06830 100644
--- a/cli/dts/lib.deno.shared_globals.d.ts
+++ b/cli/dts/lib.deno.shared_globals.d.ts
@@ -6,6 +6,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="deno.console" />
+/// <reference lib="deno.file" />
/// <reference lib="deno.url" />
/// <reference lib="deno.web" />
/// <reference lib="deno.fetch" />
diff --git a/cli/main.rs b/cli/main.rs
index 6833665c1..e9e29c9db 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -275,11 +275,12 @@ 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{}",
crate::tsc::DENO_NS_LIB,
crate::tsc::DENO_CONSOLE_LIB,
crate::tsc::DENO_URL_LIB,
crate::tsc::DENO_WEB_LIB,
+ crate::tsc::DENO_FILE_LIB,
crate::tsc::DENO_FETCH_LIB,
crate::tsc::DENO_WEBGPU_LIB,
crate::tsc::DENO_WEBSOCKET_LIB,
diff --git a/cli/tsc.rs b/cli/tsc.rs
index dd97cb6b7..7c54905c5 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -32,6 +32,7 @@ pub static DENO_NS_LIB: &str = include_str!("dts/lib.deno.ns.d.ts");
pub static DENO_CONSOLE_LIB: &str = include_str!(env!("DENO_CONSOLE_LIB_PATH"));
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_FILE_LIB: &str = include_str!(env!("DENO_FILE_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"));
pub static DENO_WEBSOCKET_LIB: &str =
diff --git a/op_crates/fetch/internal.d.ts b/op_crates/fetch/internal.d.ts
index a474d499c..e02bc6ed2 100644
--- a/op_crates/fetch/internal.d.ts
+++ b/op_crates/fetch/internal.d.ts
@@ -19,16 +19,6 @@ declare namespace globalThis {
Headers: typeof Headers;
};
- declare var file: {
- Blob: typeof Blob & {
- [globalThis.__bootstrap.file._byteSequence]: Uint8Array;
- };
- _byteSequence: unique symbol;
- File: typeof File & {
- [globalThis.__bootstrap.file._byteSequence]: Uint8Array;
- };
- };
-
declare var streams: {
ReadableStream: typeof ReadableStream;
isReadableStreamDisturbed(stream: ReadableStream): boolean;
diff --git a/op_crates/fetch/lib.deno_fetch.d.ts b/op_crates/fetch/lib.deno_fetch.d.ts
index 7d06fe691..af21d8c44 100644
--- a/op_crates/fetch/lib.deno_fetch.d.ts
+++ b/op_crates/fetch/lib.deno_fetch.d.ts
@@ -287,42 +287,6 @@ interface TransformStreamDefaultControllerTransformCallback<I, O> {
): void | PromiseLike<void>;
}
-type BlobPart = BufferSource | Blob | string;
-
-interface BlobPropertyBag {
- type?: string;
- endings?: "transparent" | "native";
-}
-
-/** A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. */
-declare class Blob {
- constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);
-
- readonly size: number;
- readonly type: string;
- arrayBuffer(): Promise<ArrayBuffer>;
- slice(start?: number, end?: number, contentType?: string): Blob;
- stream(): ReadableStream;
- text(): Promise<string>;
-}
-
-interface FilePropertyBag extends BlobPropertyBag {
- lastModified?: number;
-}
-
-/** Provides information about files and allows JavaScript in a web page to
- * access their content. */
-declare class File extends Blob {
- constructor(
- fileBits: BlobPart[],
- fileName: string,
- options?: FilePropertyBag,
- );
-
- readonly lastModified: number;
- readonly name: string;
-}
-
type FormDataEntryValue = File | string;
/** Provides a way to easily construct a set of key/value pairs representing
diff --git a/op_crates/fetch/lib.rs b/op_crates/fetch/lib.rs
index 1d36bfc09..32a5a0140 100644
--- a/op_crates/fetch/lib.rs
+++ b/op_crates/fetch/lib.rs
@@ -69,10 +69,6 @@ pub fn init(isolate: &mut JsRuntime) {
include_str!("20_headers.js"),
),
(
- "deno:op_crates/fetch/21_file.js",
- include_str!("21_file.js"),
- ),
- (
"deno:op_crates/fetch/26_fetch.js",
include_str!("26_fetch.js"),
),
diff --git a/op_crates/fetch/21_file.js b/op_crates/file/01_file.js
index 4a052576c..23886fbd5 100644
--- a/op_crates/fetch/21_file.js
+++ b/op_crates/file/01_file.js
@@ -6,7 +6,7 @@
/// <reference path="../web/internal.d.ts" />
/// <reference path="../web/lib.deno_web.d.ts" />
/// <reference path="./internal.d.ts" />
-/// <reference path="./lib.deno_fetch.d.ts" />
+/// <reference path="./lib.deno_file.d.ts" />
/// <reference lib="esnext" />
"use strict";
diff --git a/op_crates/file/02_filereader.js b/op_crates/file/02_filereader.js
new file mode 100644
index 000000000..e398b23df
--- /dev/null
+++ b/op_crates/file/02_filereader.js
@@ -0,0 +1,334 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+/// <reference no-default-lib="true" />
+/// <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" />
+/// <reference path="./internal.d.ts" />
+/// <reference path="./lib.deno_file.d.ts" />
+/// <reference lib="esnext" />
+
+"use strict";
+
+((window) => {
+ const webidl = window.__bootstrap.webidl;
+ const base64 = window.__bootstrap.base64;
+
+ const state = Symbol("[[state]]");
+ const result = Symbol("[[result]]");
+ const error = Symbol("[[error]]");
+ const aborted = Symbol("[[aborted]]");
+
+ class FileReader extends EventTarget {
+ /** @type {"empty" | "loading" | "done"} */
+ [state] = "empty";
+ /** @type {null | string | ArrayBuffer} */
+ [result] = null;
+ /** @type {null | DOMException} */
+ [error] = null;
+
+ [aborted] = false;
+
+ /**
+ * @param {Blob} blob
+ * @param {{kind: "ArrayBuffer" | "Text" | "DataUrl", encoding?: string}} readtype
+ */
+ #readOperation = async (blob, readtype) => {
+ // 1. If fr’s state is "loading", throw an InvalidStateError DOMException.
+ if (this[state] === "loading") {
+ throw new DOMException(
+ "Invalid FileReader state.",
+ "InvalidStateError",
+ );
+ }
+ // 2. Set fr’s state to "loading".
+ this[state] = "loading";
+ // 3. Set fr’s result to null.
+ this[result] = null;
+ // 4. Set fr’s error to null.
+ this[error] = null;
+
+ // 5. Let stream be the result of calling get stream on blob.
+ const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
+
+ // 6. Let reader be the result of getting a reader from stream.
+ const reader = stream.getReader();
+
+ // 7. Let bytes be an empty byte sequence.
+ /** @type {Uint8Array[]} */
+ const chunks = [];
+
+ // 8. Let chunkPromise be the result of reading a chunk from stream with reader.
+ let chunkPromise = reader.read();
+
+ // 9. Let isFirstChunk be true.
+ let isFirstChunk = true;
+
+ // 10 in parallel while true
+ while (!this[aborted]) {
+ // 1. Wait for chunkPromise to be fulfilled or rejected.
+ try {
+ const chunk = await chunkPromise;
+ if (this[aborted]) return;
+
+ // 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
+ if (isFirstChunk) {
+ queueMicrotask(() => {
+ // fire a progress event for loadstart
+ const ev = new ProgressEvent("loadstart", {});
+ this.dispatchEvent(ev);
+ });
+ }
+ // 3. Set isFirstChunk to false.
+ isFirstChunk = false;
+
+ // 4. If chunkPromise is fulfilled with an object whose done property is false
+ // and whose value property is a Uint8Array object, run these steps:
+ if (!chunk.done && chunk.value instanceof Uint8Array) {
+ chunks.push(chunk.value);
+
+ // TODO(bartlomieju): (only) If roughly 50ms have passed since last progress
+ {
+ const size = chunks.reduce((p, i) => p + i.byteLength, 0);
+ const ev = new ProgressEvent("progress", {
+ loaded: size,
+ });
+ this.dispatchEvent(ev);
+ }
+
+ chunkPromise = reader.read();
+ } // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm:
+ else if (chunk.done === true) {
+ queueMicrotask(() => {
+ // 1. Set fr’s state to "done".
+ this[state] = "done";
+ // 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName.
+ const size = chunks.reduce((p, i) => p + i.byteLength, 0);
+ const bytes = new Uint8Array(size);
+ let offs = 0;
+ for (const chunk of chunks) {
+ bytes.set(chunk, offs);
+ offs += chunk.byteLength;
+ }
+ switch (readtype.kind) {
+ case "ArrayBuffer": {
+ this[result] = bytes.buffer;
+ break;
+ }
+ case "Text": {
+ const decoder = new TextDecoder(readtype.encoding);
+ this[result] = decoder.decode(bytes.buffer);
+ break;
+ }
+ case "DataUrl": {
+ this[result] = "data:application/octet-stream;base64," +
+ base64.fromByteArray(bytes);
+ break;
+ }
+ }
+ // 4.2 Fire a progress event called load at the fr.
+ {
+ const ev = new ProgressEvent("load", {
+ lengthComputable: true,
+ loaded: size,
+ total: size,
+ });
+ this.dispatchEvent(ev);
+ }
+
+ // 5. If fr’s state is not "loading", fire a progress event called loadend at the fr.
+ //Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired.
+ if (this[state] !== "loading") {
+ const ev = new ProgressEvent("loadend", {
+ lengthComputable: true,
+ loaded: size,
+ total: size,
+ });
+ this.dispatchEvent(ev);
+ }
+ });
+
+ break;
+ }
+ } catch (err) {
+ if (this[aborted]) return;
+
+ // chunkPromise rejected
+ this[state] = "done";
+ this[error] = err;
+
+ {
+ const ev = new ProgressEvent("error", {});
+ this.dispatchEvent(ev);
+ }
+
+ //If fr’s state is not "loading", fire a progress event called loadend at fr.
+ //Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired.
+ if (this[state] !== "loading") {
+ const ev = new ProgressEvent("loadend", {});
+ this.dispatchEvent(ev);
+ }
+
+ break;
+ }
+ }
+ };
+
+ static EMPTY = 0;
+ static LOADING = 1;
+ static DONE = 2;
+
+ constructor() {
+ super();
+ this[webidl.brand] = webidl.brand;
+ }
+
+ /** @returns {number} */
+ get readyState() {
+ webidl.assertBranded(this, FileReader);
+ switch (this[state]) {
+ case "empty":
+ return FileReader.EMPTY;
+ case "loading":
+ return FileReader.LOADING;
+ case "done":
+ return FileReader.DONE;
+ default:
+ throw new TypeError("Invalid state");
+ }
+ }
+
+ get result() {
+ webidl.assertBranded(this, FileReader);
+ return this[result];
+ }
+
+ get error() {
+ webidl.assertBranded(this, FileReader);
+ return this[error];
+ }
+
+ abort() {
+ webidl.assertBranded(this, FileReader);
+ // If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm.
+ if (
+ this[state] === "empty" ||
+ this[state] === "done"
+ ) {
+ this[result] = null;
+ return;
+ }
+ // If context object's state is "loading" set context object's state to "done" and set context object's result to null.
+ if (this[state] === "loading") {
+ this[state] = "done";
+ this[result] = null;
+ }
+ // If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue.
+ // Terminate the algorithm for the read method being processed.
+ this[aborted] = true;
+
+ // Fire a progress event called abort at the context object.
+ const ev = new ProgressEvent("abort", {});
+ this.dispatchEvent(ev);
+
+ // If context object's state is not "loading", fire a progress event called loadend at the context object.
+ if (this[state] !== "loading") {
+ const ev = new ProgressEvent("loadend", {});
+ this.dispatchEvent(ev);
+ }
+ }
+
+ /** @param {Blob} blob */
+ readAsArrayBuffer(blob) {
+ webidl.assertBranded(this, FileReader);
+ const prefix = "Failed to execute 'readAsArrayBuffer' on 'FileReader'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ this.#readOperation(blob, { kind: "ArrayBuffer" });
+ }
+
+ /** @param {Blob} blob */
+ readAsBinaryString(blob) {
+ webidl.assertBranded(this, FileReader);
+ const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ // alias for readAsArrayBuffer
+ this.#readOperation(blob, { kind: "ArrayBuffer" });
+ }
+
+ /** @param {Blob} blob */
+ readAsDataURL(blob) {
+ webidl.assertBranded(this, FileReader);
+ const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ // alias for readAsArrayBuffer
+ this.#readOperation(blob, { kind: "DataUrl" });
+ }
+
+ /**
+ * @param {Blob} blob
+ * @param {string} [encoding]
+ */
+ readAsText(blob, encoding) {
+ webidl.assertBranded(this, FileReader);
+ const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ if (encoding !== undefined) {
+ encoding = webidl.converters["DOMString"](encoding, {
+ prefix,
+ context: "Argument 2",
+ });
+ }
+ // alias for readAsArrayBuffer
+ this.#readOperation(blob, { kind: "Text", encoding });
+ }
+ }
+
+ const handlerSymbol = Symbol("eventHandlers");
+
+ function makeWrappedHandler(handler) {
+ function wrappedHandler(...args) {
+ if (typeof wrappedHandler.handler !== "function") {
+ return;
+ }
+ return wrappedHandler.handler.call(this, ...args);
+ }
+ wrappedHandler.handler = handler;
+ return wrappedHandler;
+ }
+ // TODO(benjamingr) reuse when we can reuse code between web crates
+ function defineEventHandler(emitter, name) {
+ // HTML specification section 8.1.5.1
+ Object.defineProperty(emitter, `on${name}`, {
+ get() {
+ return this[handlerSymbol]?.get(name)?.handler;
+ },
+ set(value) {
+ if (!this[handlerSymbol]) {
+ this[handlerSymbol] = new Map();
+ }
+ let handlerWrapper = this[handlerSymbol]?.get(name);
+ if (handlerWrapper) {
+ handlerWrapper.handler = value;
+ } else {
+ handlerWrapper = makeWrappedHandler(value);
+ this.addEventListener(name, handlerWrapper);
+ }
+ this[handlerSymbol].set(name, handlerWrapper);
+ },
+ configurable: true,
+ enumerable: true,
+ });
+ }
+ defineEventHandler(FileReader.prototype, "error");
+ defineEventHandler(FileReader.prototype, "loadstart");
+ defineEventHandler(FileReader.prototype, "load");
+ defineEventHandler(FileReader.prototype, "loadend");
+ defineEventHandler(FileReader.prototype, "progress");
+ defineEventHandler(FileReader.prototype, "abort");
+
+ window.__bootstrap.fileReader = {
+ FileReader,
+ };
+})(this);
diff --git a/op_crates/file/Cargo.toml b/op_crates/file/Cargo.toml
new file mode 100644
index 000000000..54476c783
--- /dev/null
+++ b/op_crates/file/Cargo.toml
@@ -0,0 +1,17 @@
+# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_file"
+version = "0.1.0"
+edition = "2018"
+description = "File 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.83.0", path = "../../core" }
diff --git a/op_crates/file/README.md b/op_crates/file/README.md
new file mode 100644
index 000000000..c421bf004
--- /dev/null
+++ b/op_crates/file/README.md
@@ -0,0 +1,5 @@
+# deno_file
+
+This crate implements the File API.
+
+Spec: https://w3c.github.io/FileAPI
diff --git a/op_crates/file/internal.d.ts b/op_crates/file/internal.d.ts
new file mode 100644
index 000000000..91a61d811
--- /dev/null
+++ b/op_crates/file/internal.d.ts
@@ -0,0 +1,18 @@
+// 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 file: {
+ Blob: typeof Blob & {
+ [globalThis.__bootstrap.file._byteSequence]: Uint8Array;
+ };
+ _byteSequence: unique symbol;
+ File: typeof File & {
+ [globalThis.__bootstrap.file._byteSequence]: Uint8Array;
+ };
+ };
+ }
+}
diff --git a/op_crates/file/lib.deno_file.d.ts b/op_crates/file/lib.deno_file.d.ts
new file mode 100644
index 000000000..a907c3f50
--- /dev/null
+++ b/op_crates/file/lib.deno_file.d.ts
@@ -0,0 +1,40 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+/// <reference no-default-lib="true" />
+/// <reference lib="esnext" />
+
+type BlobPart = BufferSource | Blob | string;
+
+interface BlobPropertyBag {
+ type?: string;
+ endings?: "transparent" | "native";
+}
+
+/** A file-like object of immutable, raw data. Blobs represent data that isn't necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user's system. */
+declare class Blob {
+ constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);
+
+ readonly size: number;
+ readonly type: string;
+ arrayBuffer(): Promise<ArrayBuffer>;
+ slice(start?: number, end?: number, contentType?: string): Blob;
+ stream(): ReadableStream<Uint8Array>;
+ text(): Promise<string>;
+}
+
+interface FilePropertyBag extends BlobPropertyBag {
+ lastModified?: number;
+}
+
+/** Provides information about files and allows JavaScript in a web page to
+ * access their content. */
+declare class File extends Blob {
+ constructor(
+ fileBits: BlobPart[],
+ fileName: string,
+ options?: FilePropertyBag,
+ );
+
+ readonly lastModified: number;
+ readonly name: string;
+}
diff --git a/op_crates/file/lib.rs b/op_crates/file/lib.rs
new file mode 100644
index 000000000..c7e690433
--- /dev/null
+++ b/op_crates/file/lib.rs
@@ -0,0 +1,22 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::JsRuntime;
+use std::path::PathBuf;
+
+/// Load and execute the javascript code.
+pub fn init(isolate: &mut JsRuntime) {
+ let files = vec![
+ ("deno:op_crates/file/01_file.js", include_str!("01_file.js")),
+ (
+ "deno:op_crates/file/02_filereader.js",
+ include_str!("02_filereader.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_file.d.ts")
+}
diff --git a/op_crates/web/21_filereader.js b/op_crates/web/21_filereader.js
deleted file mode 100644
index 30fff33d8..000000000
--- a/op_crates/web/21_filereader.js
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-"use strict";
-
-((window) => {
- const base64 = window.__bootstrap.base64;
-
- async function readOperation(fr, blob, readtype) {
- // Implementation from https://w3c.github.io/FileAPI/ notes
- // And body of deno blob.ts readBytes
-
- fr.aborting = false;
-
- // 1. If fr’s state is "loading", throw an InvalidStateError DOMException.
- if (fr.readyState === FileReader.LOADING) {
- throw new DOMException(
- "Invalid FileReader state.",
- "InvalidStateError",
- );
- }
- // 2. Set fr’s state to "loading".
- fr.readyState = FileReader.LOADING;
- // 3. Set fr’s result to null.
- fr.result = null;
- // 4. Set fr’s error to null.
- fr.error = null;
-
- // 5. Let stream be the result of calling get stream on blob.
- const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
-
- // 6. Let reader be the result of getting a reader from stream.
- const reader = stream.getReader();
-
- // 7. Let bytes be an empty byte sequence.
- //let bytes = new Uint8Array();
- const chunks /*: Uint8Array[]*/ = [];
-
- // 8. Let chunkPromise be the result of reading a chunk from stream with reader.
- let chunkPromise = reader.read();
-
- // 9. Let isFirstChunk be true.
- let isFirstChunk = true;
-
- // 10 in parallel while true
- while (!fr.aborting) {
- // 1. Wait for chunkPromise to be fulfilled or rejected.
- try {
- const chunk = await chunkPromise;
-
- // 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
- if (isFirstChunk) {
- queueMicrotask(() => {
- // fire a progress event for loadstart
- const ev = new ProgressEvent("loadstart", {});
- fr.dispatchEvent(ev);
- });
- }
- // 3. Set isFirstChunk to false.
- isFirstChunk = false;
-
- // 4. If chunkPromise is fulfilled with an object whose done property is false
- // and whose value property is a Uint8Array object, run these steps:
- if (!chunk.done && chunk.value instanceof Uint8Array) {
- chunks.push(chunk.value);
-
- // TODO(bartlomieju): (only) If roughly 50ms have passed since last progress
- {
- const size = chunks.reduce((p, i) => p + i.byteLength, 0);
- const ev = new ProgressEvent("progress", {
- loaded: size,
- });
- fr.dispatchEvent(ev);
- }
-
- chunkPromise = reader.read();
- } // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm:
- else if (chunk.done === true) {
- queueMicrotask(() => {
- if (fr.aborting) {
- return;
- }
-
- // 1. Set fr’s state to "done".
- fr.readyState = FileReader.DONE;
- // 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName.
- const size = chunks.reduce((p, i) => p + i.byteLength, 0);
- const bytes = new Uint8Array(size);
- let offs = 0;
- for (const chunk of chunks) {
- bytes.set(chunk, offs);
- offs += chunk.byteLength;
- }
- switch (readtype.kind) {
- case "ArrayBuffer": {
- fr.result = bytes.buffer;
- break;
- }
- case "Text": {
- const decoder = new TextDecoder(readtype.encoding);
- fr.result = decoder.decode(bytes.buffer);
- break;
- }
- case "DataUrl": {
- fr.result = "data:application/octet-stream;base64," +
- base64.fromByteArray(bytes);
- break;
- }
- }
- // 4.2 Fire a progress event called load at the fr.
- {
- const ev = new ProgressEvent("load", {
- lengthComputable: true,
- loaded: size,
- total: size,
- });
- fr.dispatchEvent(ev);
- }
-
- // 5. If fr’s state is not "loading", fire a progress event called loadend at the fr.
- //Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired.
- if (fr.readyState !== FileReader.LOADING) {
- const ev = new ProgressEvent("loadend", {
- lengthComputable: true,
- loaded: size,
- total: size,
- });
- fr.dispatchEvent(ev);
- }
- });
-
- break;
- }
- } catch (err) {
- if (fr.aborting) {
- break;
- }
-
- // chunkPromise rejected
- fr.readyState = FileReader.DONE;
- fr.error = err;
-
- {
- const ev = new ProgressEvent("error", {});
- fr.dispatchEvent(ev);
- }
-
- //If fr’s state is not "loading", fire a progress event called loadend at fr.
- //Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired.
- if (fr.readyState !== FileReader.LOADING) {
- const ev = new ProgressEvent("loadend", {});
- fr.dispatchEvent(ev);
- }
-
- break;
- }
- }
- }
-
- class FileReader extends EventTarget {
- error = null;
- readyState = FileReader.EMPTY;
- result = null;
- aborting = false;
-
- constructor() {
- super();
- }
-
- abort() {
- // If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm.
- if (
- this.readyState === FileReader.EMPTY ||
- this.readyState === FileReader.DONE
- ) {
- this.result = null;
- return;
- }
- // If context object's state is "loading" set context object's state to "done" and set context object's result to null.
- if (this.readyState === FileReader.LOADING) {
- this.readyState = FileReader.DONE;
- this.result = null;
- }
- // If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue.
- // Terminate the algorithm for the read method being processed.
- this.aborting = true;
-
- // Fire a progress event called abort at the context object.
- const ev = new ProgressEvent("abort", {});
- this.dispatchEvent(ev);
-
- // If context object's state is not "loading", fire a progress event called loadend at the context object.
- if (this.readyState !== FileReader.LOADING) {
- const ev = new ProgressEvent("loadend", {});
- this.dispatchEvent(ev);
- }
- }
- readAsArrayBuffer(blob) {
- readOperation(this, blob, { kind: "ArrayBuffer" });
- }
- readAsBinaryString(blob) {
- // alias for readAsArrayBuffer
- readOperation(this, blob, { kind: "ArrayBuffer" });
- }
- readAsDataURL(blob) {
- readOperation(this, blob, { kind: "DataUrl" });
- }
- readAsText(blob, encoding) {
- readOperation(this, blob, { kind: "Text", encoding });
- }
- }
-
- FileReader.EMPTY = 0;
- FileReader.LOADING = 1;
- FileReader.DONE = 2;
-
- const handlerSymbol = Symbol("eventHandlers");
-
- function makeWrappedHandler(handler) {
- function wrappedHandler(...args) {
- if (typeof wrappedHandler.handler !== "function") {
- return;
- }
- return wrappedHandler.handler.call(this, ...args);
- }
- wrappedHandler.handler = handler;
- return wrappedHandler;
- }
- // TODO(benjamingr) reuse when we can reuse code between web crates
- function defineEventHandler(emitter, name) {
- // HTML specification section 8.1.5.1
- Object.defineProperty(emitter, `on${name}`, {
- get() {
- return this[handlerSymbol]?.get(name)?.handler;
- },
- set(value) {
- if (!this[handlerSymbol]) {
- this[handlerSymbol] = new Map();
- }
- let handlerWrapper = this[handlerSymbol]?.get(name);
- if (handlerWrapper) {
- handlerWrapper.handler = value;
- } else {
- handlerWrapper = makeWrappedHandler(value);
- this.addEventListener(name, handlerWrapper);
- }
- this[handlerSymbol].set(name, handlerWrapper);
- },
- configurable: true,
- enumerable: true,
- });
- }
- defineEventHandler(FileReader.prototype, "error");
- defineEventHandler(FileReader.prototype, "loadstart");
- defineEventHandler(FileReader.prototype, "load");
- defineEventHandler(FileReader.prototype, "loadend");
- defineEventHandler(FileReader.prototype, "progress");
- defineEventHandler(FileReader.prototype, "abort");
-
- window.__bootstrap.fileReader = {
- FileReader,
- };
-})(this);
diff --git a/op_crates/web/internal.d.ts b/op_crates/web/internal.d.ts
index 458f4a173..521563810 100644
--- a/op_crates/web/internal.d.ts
+++ b/op_crates/web/internal.d.ts
@@ -12,5 +12,11 @@ declare namespace globalThis {
declare var location: {
getLocationHref(): string | undefined;
};
+
+ declare var base64: {
+ byteLength(b64: string): number;
+ toByteArray(b64: string): Uint8Array;
+ fromByteArray(uint8: Uint8Array): string;
+ };
}
}
diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs
index af7a7cebc..8ee944d74 100644
--- a/op_crates/web/lib.rs
+++ b/op_crates/web/lib.rs
@@ -30,10 +30,6 @@ pub fn init(isolate: &mut JsRuntime) {
"deno:op_crates/web/12_location.js",
include_str!("12_location.js"),
),
- (
- "deno:op_crates/web/21_filereader.js",
- include_str!("21_filereader.js"),
- ),
];
for (url, source_code) in files {
isolate.execute(url, source_code).unwrap();
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index a6f9a80ff..22293f0a4 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -22,6 +22,7 @@ deno_core = { path = "../core", version = "0.83.0" }
deno_console = { path = "../op_crates/console", version = "0.2.1" }
deno_crypto = { path = "../op_crates/crypto", version = "0.16.1" }
deno_fetch = { path = "../op_crates/fetch", version = "0.24.1" }
+deno_file = { path = "../op_crates/file", version = "0.1.0" }
deno_web = { path = "../op_crates/web", version = "0.32.1" }
deno_url = { path = "../op_crates/url", version = "0.2.1" }
deno_webidl = { path = "../op_crates/webidl", version = "0.2.1" }
@@ -37,6 +38,7 @@ deno_core = { path = "../core", version = "0.83.0" }
deno_console = { path = "../op_crates/console", version = "0.2.1" }
deno_crypto = { path = "../op_crates/crypto", version = "0.16.1" }
deno_fetch = { path = "../op_crates/fetch", version = "0.24.1" }
+deno_file = { path = "../op_crates/file", version = "0.1.0" }
deno_web = { path = "../op_crates/web", version = "0.32.1" }
deno_url = { path = "../op_crates/url", version = "0.2.1" }
deno_webidl = { path = "../op_crates/webidl", version = "0.2.1" }
diff --git a/runtime/build.rs b/runtime/build.rs
index 9172816d8..d7d8cb78f 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -17,6 +17,7 @@ fn create_snapshot(
deno_console::init(&mut js_runtime);
deno_url::init(&mut js_runtime);
deno_web::init(&mut js_runtime);
+ deno_file::init(&mut js_runtime);
deno_fetch::init(&mut js_runtime);
deno_websocket::init(&mut js_runtime);
deno_crypto::init(&mut js_runtime);
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 1606e9dfe..b45010c4d 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -5,6 +5,7 @@
pub use deno_console;
pub use deno_crypto;
pub use deno_fetch;
+pub use deno_file;
pub use deno_url;
pub use deno_web;
pub use deno_webgpu;
diff --git a/test_util/wpt b/test_util/wpt
-Subproject 681d273a49e7b5228394285b0c017f1b4c0d33b
+Subproject f897da00871cf39366bc2f0ceec051c65bc7570
diff --git a/tools/wpt/expectation.json b/tools/wpt/expectation.json
index abbe8cdfc..2ebdc1011 100644
--- a/tools/wpt/expectation.json
+++ b/tools/wpt/expectation.json
@@ -358,7 +358,7 @@
],
"patched-global.any.js": true,
"reentrant-strategies.any.js": true,
- "tee.any.js": true,
+ "tee.any.js": false,
"templated.any.js": [
"ReadableStream (empty) reader: canceling via the stream should fail"
]
@@ -784,7 +784,8 @@
},
"file": {
"File-constructor.any.js": true
- }
+ },
+ "fileReader.any.js": true
},
"html": {
"webappapis": {
@@ -805,4 +806,4 @@
}
}
}
-}
+} \ No newline at end of file