summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com>2019-08-06 06:22:11 -0700
committerRyan Dahl <ry@tinyclouds.org>2019-08-06 09:22:11 -0400
commitccee2f01ba2f6304720ab17e99dee17bf6687bd8 (patch)
treedf04bd385c05e13b6a835fc4955cd5eac2c7c582
parent11c850af423f07769f054c494a76cbd9efb8806c (diff)
Implement Blob url support for worker (#2729)
-rw-r--r--cli/msg.fbs2
-rw-r--r--cli/ops.rs49
-rw-r--r--js/blob.ts7
-rw-r--r--js/url.ts40
-rw-r--r--js/workers.ts35
-rw-r--r--tests/040_worker_blob.test2
-rw-r--r--tests/040_worker_blob.ts6
-rw-r--r--tests/040_worker_blob.ts.out1
8 files changed, 118 insertions, 24 deletions
diff --git a/cli/msg.fbs b/cli/msg.fbs
index 8f6497afc..438fa0cb9 100644
--- a/cli/msg.fbs
+++ b/cli/msg.fbs
@@ -205,6 +205,8 @@ table FormatErrorRes {
table CreateWorker {
specifier: string;
include_deno_namespace: bool;
+ has_source_code: bool;
+ source_code: string;
}
table CreateWorkerRes {
diff --git a/cli/ops.rs b/cli/ops.rs
index 410f15a4c..6f98c82bc 100644
--- a/cli/ops.rs
+++ b/cli/ops.rs
@@ -2075,6 +2075,8 @@ fn op_create_worker(
// has included namespace (to avoid escalation).
let include_deno_namespace =
inner.include_deno_namespace() && state.include_deno_namespace;
+ let has_source_code = inner.has_source_code();
+ let source_code = inner.source_code().unwrap();
let parent_state = state.clone();
@@ -2094,29 +2096,34 @@ fn op_create_worker(
worker.execute(&deno_main_call).unwrap();
worker.execute("workerMain()").unwrap();
+ let exec_cb = move |worker: Worker| {
+ let mut workers_tl = parent_state.workers.lock().unwrap();
+ workers_tl.insert(rid, worker.shared());
+ let builder = &mut FlatBufferBuilder::new();
+ let msg_inner =
+ msg::CreateWorkerRes::create(builder, &msg::CreateWorkerResArgs { rid });
+ serialize_response(
+ cmd_id,
+ builder,
+ msg::BaseArgs {
+ inner: Some(msg_inner.as_union_value()),
+ inner_type: msg::Any::CreateWorkerRes,
+ ..Default::default()
+ },
+ )
+ };
+
+ // Has provided source code, execute immediately.
+ if has_source_code {
+ worker.execute(&source_code).unwrap();
+ return ok_buf(exec_cb(worker));
+ }
+
let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
- let op =
- worker
- .execute_mod_async(&module_specifier, false)
- .and_then(move |()| {
- let mut workers_tl = parent_state.workers.lock().unwrap();
- workers_tl.insert(rid, worker.shared());
- let builder = &mut FlatBufferBuilder::new();
- let msg_inner = msg::CreateWorkerRes::create(
- builder,
- &msg::CreateWorkerResArgs { rid },
- );
- Ok(serialize_response(
- cmd_id,
- builder,
- msg::BaseArgs {
- inner: Some(msg_inner.as_union_value()),
- inner_type: msg::Any::CreateWorkerRes,
- ..Default::default()
- },
- ))
- });
+ let op = worker
+ .execute_mod_async(&module_specifier, false)
+ .and_then(move |()| Ok(exec_cb(worker)));
let result = op.wait()?;
Ok(Op::Sync(result))
diff --git a/js/blob.ts b/js/blob.ts
index f2668b642..94d03c97f 100644
--- a/js/blob.ts
+++ b/js/blob.ts
@@ -118,6 +118,10 @@ function processBlobParts(
return bytes;
}
+// A WeakMap holding blob to byte array mapping.
+// Ensures it does not impact garbage collection.
+export const blobBytesWeakMap = new WeakMap<domTypes.Blob, Uint8Array>();
+
export class DenoBlob implements domTypes.Blob {
private readonly [bytesSymbol]: Uint8Array;
readonly size: number = 0;
@@ -161,6 +165,9 @@ export class DenoBlob implements domTypes.Blob {
this[bytesSymbol] = bytes;
this.size = bytes.byteLength;
this.type = type;
+
+ // Register bytes for internal private use.
+ blobBytesWeakMap.set(this, bytes);
}
slice(start?: number, end?: number, contentType?: string): DenoBlob {
diff --git a/js/url.ts b/js/url.ts
index e8b1ec8f4..6520af834 100644
--- a/js/url.ts
+++ b/js/url.ts
@@ -1,5 +1,8 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import * as urlSearchParams from "./url_search_params";
+import * as domTypes from "./dom_types";
+import { getRandomValues } from "./get_random_values";
+import { window } from "./window";
interface URLParts {
protocol: string;
@@ -63,6 +66,20 @@ function parse(url: string): URLParts | undefined {
return undefined;
}
+// Based on https://github.com/kelektiv/node-uuid
+// TODO(kevinkassimo): Use deno_std version once possible.
+function generateUUID(): string {
+ return "00000000-0000-4000-8000-000000000000".replace(
+ /[0]/g,
+ (): string =>
+ // random integer from 0 to 15 as a hex digit.
+ (getRandomValues(new Uint8Array(1))[0] % 16).toString(16)
+ );
+}
+
+// Keep it outside of URL to avoid any attempts of access.
+export const blobURLMap = new Map<string, domTypes.Blob>();
+
export class URL {
private _parts: URLParts;
private _searchParams!: urlSearchParams.URLSearchParams;
@@ -273,4 +290,27 @@ export class URL {
toJSON(): string {
return this.href;
}
+
+ // TODO(kevinkassimo): implement MediaSource version in the future.
+ static createObjectURL(b: domTypes.Blob): string {
+ const origin = window.location.origin || "http://deno-opaque-origin";
+ const key = `blob:${origin}/${generateUUID()}`;
+ blobURLMap.set(key, b);
+ return key;
+ }
+
+ static revokeObjectURL(url: string): void {
+ let urlObject;
+ try {
+ urlObject = new URL(url);
+ } catch {
+ throw new TypeError("Provided URL string is not valid");
+ }
+ if (urlObject.protocol !== "blob:") {
+ return;
+ }
+ // Origin match check seems irrelevant for now, unless we implement
+ // persisten storage for per window.location.origin at some point.
+ blobURLMap.delete(url);
+ }
}
diff --git a/js/workers.ts b/js/workers.ts
index f008ccecf..f061a0126 100644
--- a/js/workers.ts
+++ b/js/workers.ts
@@ -6,6 +6,8 @@ import * as flatbuffers from "./flatbuffers";
import { assert, log } from "./util";
import { TextDecoder, TextEncoder } from "./text_encoding";
import { window } from "./window";
+import { blobURLMap } from "./url";
+import { blobBytesWeakMap } from "./blob";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
@@ -22,14 +24,19 @@ export function decodeMessage(dataIntArray: Uint8Array): any {
function createWorker(
specifier: string,
- includeDenoNamespace: boolean
+ includeDenoNamespace: boolean,
+ hasSourceCode: boolean,
+ sourceCode: Uint8Array
): number {
const builder = flatbuffers.createBuilder();
const specifier_ = builder.createString(specifier);
+ const sourceCode_ = builder.createString(sourceCode);
const inner = msg.CreateWorker.createCreateWorker(
builder,
specifier_,
- includeDenoNamespace
+ includeDenoNamespace,
+ hasSourceCode,
+ sourceCode_
);
const baseRes = sendSync(builder, msg.Any.CreateWorker, inner);
assert(baseRes != null);
@@ -177,11 +184,33 @@ export class WorkerImpl implements Worker {
public onmessageerror?: () => void;
constructor(specifier: string, options?: DenoWorkerOptions) {
+ let hasSourceCode = false;
+ let sourceCode = new Uint8Array();
+
let includeDenoNamespace = true;
if (options && options.noDenoNamespace) {
includeDenoNamespace = false;
}
- this.rid = createWorker(specifier, includeDenoNamespace);
+ // Handle blob URL.
+ if (specifier.startsWith("blob:")) {
+ hasSourceCode = true;
+ const b = blobURLMap.get(specifier);
+ if (!b) {
+ throw new Error("No Blob associated with the given URL is found");
+ }
+ const blobBytes = blobBytesWeakMap.get(b!);
+ if (!blobBytes) {
+ throw new Error("Invalid Blob");
+ }
+ sourceCode = blobBytes!;
+ }
+
+ this.rid = createWorker(
+ specifier,
+ includeDenoNamespace,
+ hasSourceCode,
+ sourceCode
+ );
this.run();
this.isClosedPromise = hostGetWorkerClosed(this.rid);
this.isClosedPromise.then(
diff --git a/tests/040_worker_blob.test b/tests/040_worker_blob.test
new file mode 100644
index 000000000..81fd3182e
--- /dev/null
+++ b/tests/040_worker_blob.test
@@ -0,0 +1,2 @@
+args: run --reload tests/040_worker_blob.ts
+output: tests/040_worker_blob.ts.out
diff --git a/tests/040_worker_blob.ts b/tests/040_worker_blob.ts
new file mode 100644
index 000000000..1ba4528cf
--- /dev/null
+++ b/tests/040_worker_blob.ts
@@ -0,0 +1,6 @@
+const b = new Blob(["console.log('code from Blob'); postMessage('DONE')"]);
+const blobURL = URL.createObjectURL(b);
+const worker = new Worker(blobURL);
+worker.onmessage = (): void => {
+ Deno.exit(0);
+};
diff --git a/tests/040_worker_blob.ts.out b/tests/040_worker_blob.ts.out
new file mode 100644
index 000000000..f49b8f3d6
--- /dev/null
+++ b/tests/040_worker_blob.ts.out
@@ -0,0 +1 @@
+code from Blob