diff options
author | Kevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com> | 2019-08-06 06:22:11 -0700 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2019-08-06 09:22:11 -0400 |
commit | ccee2f01ba2f6304720ab17e99dee17bf6687bd8 (patch) | |
tree | df04bd385c05e13b6a835fc4955cd5eac2c7c582 /js | |
parent | 11c850af423f07769f054c494a76cbd9efb8806c (diff) |
Implement Blob url support for worker (#2729)
Diffstat (limited to 'js')
-rw-r--r-- | js/blob.ts | 7 | ||||
-rw-r--r-- | js/url.ts | 40 | ||||
-rw-r--r-- | js/workers.ts | 35 |
3 files changed, 79 insertions, 3 deletions
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 { @@ -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( |