mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Implement Blob url support for worker (#2729)
This commit is contained in:
parent
11c850af42
commit
ccee2f01ba
8 changed files with 118 additions and 24 deletions
|
@ -205,6 +205,8 @@ table FormatErrorRes {
|
||||||
table CreateWorker {
|
table CreateWorker {
|
||||||
specifier: string;
|
specifier: string;
|
||||||
include_deno_namespace: bool;
|
include_deno_namespace: bool;
|
||||||
|
has_source_code: bool;
|
||||||
|
source_code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
table CreateWorkerRes {
|
table CreateWorkerRes {
|
||||||
|
|
33
cli/ops.rs
33
cli/ops.rs
|
@ -2075,6 +2075,8 @@ fn op_create_worker(
|
||||||
// has included namespace (to avoid escalation).
|
// has included namespace (to avoid escalation).
|
||||||
let include_deno_namespace =
|
let include_deno_namespace =
|
||||||
inner.include_deno_namespace() && state.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();
|
let parent_state = state.clone();
|
||||||
|
|
||||||
|
@ -2094,20 +2096,13 @@ fn op_create_worker(
|
||||||
worker.execute(&deno_main_call).unwrap();
|
worker.execute(&deno_main_call).unwrap();
|
||||||
worker.execute("workerMain()").unwrap();
|
worker.execute("workerMain()").unwrap();
|
||||||
|
|
||||||
let module_specifier = ModuleSpecifier::resolve_url_or_path(specifier)?;
|
let exec_cb = move |worker: Worker| {
|
||||||
|
|
||||||
let op =
|
|
||||||
worker
|
|
||||||
.execute_mod_async(&module_specifier, false)
|
|
||||||
.and_then(move |()| {
|
|
||||||
let mut workers_tl = parent_state.workers.lock().unwrap();
|
let mut workers_tl = parent_state.workers.lock().unwrap();
|
||||||
workers_tl.insert(rid, worker.shared());
|
workers_tl.insert(rid, worker.shared());
|
||||||
let builder = &mut FlatBufferBuilder::new();
|
let builder = &mut FlatBufferBuilder::new();
|
||||||
let msg_inner = msg::CreateWorkerRes::create(
|
let msg_inner =
|
||||||
builder,
|
msg::CreateWorkerRes::create(builder, &msg::CreateWorkerResArgs { rid });
|
||||||
&msg::CreateWorkerResArgs { rid },
|
serialize_response(
|
||||||
);
|
|
||||||
Ok(serialize_response(
|
|
||||||
cmd_id,
|
cmd_id,
|
||||||
builder,
|
builder,
|
||||||
msg::BaseArgs {
|
msg::BaseArgs {
|
||||||
|
@ -2115,8 +2110,20 @@ fn op_create_worker(
|
||||||
inner_type: msg::Any::CreateWorkerRes,
|
inner_type: msg::Any::CreateWorkerRes,
|
||||||
..Default::default()
|
..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 |()| Ok(exec_cb(worker)));
|
||||||
|
|
||||||
let result = op.wait()?;
|
let result = op.wait()?;
|
||||||
Ok(Op::Sync(result))
|
Ok(Op::Sync(result))
|
||||||
|
|
|
@ -118,6 +118,10 @@ function processBlobParts(
|
||||||
return bytes;
|
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 {
|
export class DenoBlob implements domTypes.Blob {
|
||||||
private readonly [bytesSymbol]: Uint8Array;
|
private readonly [bytesSymbol]: Uint8Array;
|
||||||
readonly size: number = 0;
|
readonly size: number = 0;
|
||||||
|
@ -161,6 +165,9 @@ export class DenoBlob implements domTypes.Blob {
|
||||||
this[bytesSymbol] = bytes;
|
this[bytesSymbol] = bytes;
|
||||||
this.size = bytes.byteLength;
|
this.size = bytes.byteLength;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
|
// Register bytes for internal private use.
|
||||||
|
blobBytesWeakMap.set(this, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
slice(start?: number, end?: number, contentType?: string): DenoBlob {
|
slice(start?: number, end?: number, contentType?: string): DenoBlob {
|
||||||
|
|
40
js/url.ts
40
js/url.ts
|
@ -1,5 +1,8 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import * as urlSearchParams from "./url_search_params";
|
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 {
|
interface URLParts {
|
||||||
protocol: string;
|
protocol: string;
|
||||||
|
@ -63,6 +66,20 @@ function parse(url: string): URLParts | undefined {
|
||||||
return 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 {
|
export class URL {
|
||||||
private _parts: URLParts;
|
private _parts: URLParts;
|
||||||
private _searchParams!: urlSearchParams.URLSearchParams;
|
private _searchParams!: urlSearchParams.URLSearchParams;
|
||||||
|
@ -273,4 +290,27 @@ export class URL {
|
||||||
toJSON(): string {
|
toJSON(): string {
|
||||||
return this.href;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import * as flatbuffers from "./flatbuffers";
|
||||||
import { assert, log } from "./util";
|
import { assert, log } from "./util";
|
||||||
import { TextDecoder, TextEncoder } from "./text_encoding";
|
import { TextDecoder, TextEncoder } from "./text_encoding";
|
||||||
import { window } from "./window";
|
import { window } from "./window";
|
||||||
|
import { blobURLMap } from "./url";
|
||||||
|
import { blobBytesWeakMap } from "./blob";
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
@ -22,14 +24,19 @@ export function decodeMessage(dataIntArray: Uint8Array): any {
|
||||||
|
|
||||||
function createWorker(
|
function createWorker(
|
||||||
specifier: string,
|
specifier: string,
|
||||||
includeDenoNamespace: boolean
|
includeDenoNamespace: boolean,
|
||||||
|
hasSourceCode: boolean,
|
||||||
|
sourceCode: Uint8Array
|
||||||
): number {
|
): number {
|
||||||
const builder = flatbuffers.createBuilder();
|
const builder = flatbuffers.createBuilder();
|
||||||
const specifier_ = builder.createString(specifier);
|
const specifier_ = builder.createString(specifier);
|
||||||
|
const sourceCode_ = builder.createString(sourceCode);
|
||||||
const inner = msg.CreateWorker.createCreateWorker(
|
const inner = msg.CreateWorker.createCreateWorker(
|
||||||
builder,
|
builder,
|
||||||
specifier_,
|
specifier_,
|
||||||
includeDenoNamespace
|
includeDenoNamespace,
|
||||||
|
hasSourceCode,
|
||||||
|
sourceCode_
|
||||||
);
|
);
|
||||||
const baseRes = sendSync(builder, msg.Any.CreateWorker, inner);
|
const baseRes = sendSync(builder, msg.Any.CreateWorker, inner);
|
||||||
assert(baseRes != null);
|
assert(baseRes != null);
|
||||||
|
@ -177,11 +184,33 @@ export class WorkerImpl implements Worker {
|
||||||
public onmessageerror?: () => void;
|
public onmessageerror?: () => void;
|
||||||
|
|
||||||
constructor(specifier: string, options?: DenoWorkerOptions) {
|
constructor(specifier: string, options?: DenoWorkerOptions) {
|
||||||
|
let hasSourceCode = false;
|
||||||
|
let sourceCode = new Uint8Array();
|
||||||
|
|
||||||
let includeDenoNamespace = true;
|
let includeDenoNamespace = true;
|
||||||
if (options && options.noDenoNamespace) {
|
if (options && options.noDenoNamespace) {
|
||||||
includeDenoNamespace = false;
|
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.run();
|
||||||
this.isClosedPromise = hostGetWorkerClosed(this.rid);
|
this.isClosedPromise = hostGetWorkerClosed(this.rid);
|
||||||
this.isClosedPromise.then(
|
this.isClosedPromise.then(
|
||||||
|
|
2
tests/040_worker_blob.test
Normal file
2
tests/040_worker_blob.test
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
args: run --reload tests/040_worker_blob.ts
|
||||||
|
output: tests/040_worker_blob.ts.out
|
6
tests/040_worker_blob.ts
Normal file
6
tests/040_worker_blob.ts
Normal file
|
@ -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);
|
||||||
|
};
|
1
tests/040_worker_blob.ts.out
Normal file
1
tests/040_worker_blob.ts.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
code from Blob
|
Loading…
Add table
Reference in a new issue