mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
fix: enable FileReader wpt and align to spec (#10063)
This adds some algorithms from the whatwg mimesniff, whatwg infra, and whatwg encoding specs that FileReader needs to use internally.
This commit is contained in:
parent
d2e500e1cf
commit
c867c1aa47
8 changed files with 526 additions and 99 deletions
|
@ -14,6 +14,8 @@
|
|||
|
||||
((window) => {
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { decode } = window.__bootstrap.encoding;
|
||||
const { parseMimeType } = window.__bootstrap.mimesniff;
|
||||
const base64 = window.__bootstrap.base64;
|
||||
|
||||
const state = Symbol("[[state]]");
|
||||
|
@ -33,9 +35,9 @@
|
|||
|
||||
/**
|
||||
* @param {Blob} blob
|
||||
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl", encoding?: string}} readtype
|
||||
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
|
||||
*/
|
||||
#readOperation = async (blob, readtype) => {
|
||||
#readOperation = (blob, readtype) => {
|
||||
// 1. If fr’s state is "loading", throw an InvalidStateError DOMException.
|
||||
if (this[state] === "loading") {
|
||||
throw new DOMException(
|
||||
|
@ -67,119 +69,156 @@
|
|||
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;
|
||||
(async () => {
|
||||
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,
|
||||
// 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
|
||||
if (isFirstChunk) {
|
||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||
queueMicrotask(() => {
|
||||
if (this[aborted]) return;
|
||||
// fire a progress event for loadstart
|
||||
const ev = new ProgressEvent("loadstart", {});
|
||||
this.dispatchEvent(ev);
|
||||
});
|
||||
this.dispatchEvent(ev);
|
||||
}
|
||||
// 3. Set isFirstChunk to false.
|
||||
isFirstChunk = false;
|
||||
|
||||
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.
|
||||
// 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 ev = new ProgressEvent("load", {
|
||||
lengthComputable: true,
|
||||
const size = chunks.reduce((p, i) => p + i.byteLength, 0);
|
||||
const ev = new ProgressEvent("progress", {
|
||||
loaded: size,
|
||||
total: size,
|
||||
});
|
||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||
queueMicrotask(() => {
|
||||
if (this[aborted]) return;
|
||||
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) {
|
||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||
queueMicrotask(() => {
|
||||
if (this[aborted]) return;
|
||||
// 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 "BinaryString":
|
||||
this[result] = [...new Uint8Array(bytes.buffer)].map((v) =>
|
||||
String.fromCodePoint(v)
|
||||
).join("");
|
||||
break;
|
||||
case "Text": {
|
||||
let decoder = undefined;
|
||||
if (readtype.encoding) {
|
||||
try {
|
||||
decoder = new TextDecoder(readtype.encoding);
|
||||
} catch {
|
||||
// don't care about the error
|
||||
}
|
||||
}
|
||||
if (decoder === undefined) {
|
||||
const mimeType = parseMimeType(blob.type);
|
||||
if (mimeType) {
|
||||
const charset = mimeType.parameters.get("charset");
|
||||
if (charset) {
|
||||
try {
|
||||
decoder = new TextDecoder(charset);
|
||||
} catch {
|
||||
// don't care about the error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (decoder === undefined) {
|
||||
decoder = new TextDecoder();
|
||||
}
|
||||
this[result] = decode(bytes, decoder.encoding);
|
||||
break;
|
||||
}
|
||||
case "DataUrl": {
|
||||
const mediaType = blob.type || "application/octet-stream";
|
||||
this[result] = `data:${mediaType};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) {
|
||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||
queueMicrotask(() => {
|
||||
if (this[aborted]) return;
|
||||
|
||||
// chunkPromise rejected
|
||||
this[state] = "done";
|
||||
this[error] = err;
|
||||
|
||||
{
|
||||
const ev = new ProgressEvent("error", {});
|
||||
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 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", {
|
||||
lengthComputable: true,
|
||||
loaded: size,
|
||||
total: size,
|
||||
});
|
||||
const ev = new ProgressEvent("loadend", {});
|
||||
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;
|
||||
|
@ -254,7 +293,7 @@
|
|||
const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
// alias for readAsArrayBuffer
|
||||
this.#readOperation(blob, { kind: "ArrayBuffer" });
|
||||
this.#readOperation(blob, { kind: "BinaryString" });
|
||||
}
|
||||
|
||||
/** @param {Blob} blob */
|
||||
|
@ -285,6 +324,43 @@
|
|||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(FileReader, "EMPTY", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 0,
|
||||
});
|
||||
Object.defineProperty(FileReader, "LOADING", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 1,
|
||||
});
|
||||
Object.defineProperty(FileReader, "DONE", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 2,
|
||||
});
|
||||
Object.defineProperty(FileReader.prototype, "EMPTY", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 0,
|
||||
});
|
||||
Object.defineProperty(FileReader.prototype, "LOADING", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 1,
|
||||
});
|
||||
Object.defineProperty(FileReader.prototype, "DONE", {
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
value: 2,
|
||||
});
|
||||
|
||||
const handlerSymbol = Symbol("eventHandlers");
|
||||
|
||||
function makeWrappedHandler(handler) {
|
||||
|
@ -302,7 +378,7 @@
|
|||
// HTML specification section 8.1.5.1
|
||||
Object.defineProperty(emitter, `on${name}`, {
|
||||
get() {
|
||||
return this[handlerSymbol]?.get(name)?.handler;
|
||||
return this[handlerSymbol]?.get(name)?.handler ?? null;
|
||||
},
|
||||
set(value) {
|
||||
if (!this[handlerSymbol]) {
|
||||
|
|
31
op_crates/web/00_infra.js
Normal file
31
op_crates/web/00_infra.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// @ts-check
|
||||
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||
/// <reference path="../web/internal.d.ts" />
|
||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
/**
|
||||
* https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
|
||||
* @param {string} input
|
||||
* @param {number} position
|
||||
* @param {(char: string) => boolean} condition
|
||||
* @returns {{result: string, position: number}}
|
||||
*/
|
||||
function collectSequenceOfCodepoints(input, position, condition) {
|
||||
const start = position;
|
||||
for (
|
||||
let c = input.charAt(position);
|
||||
position < input.length && condition(c);
|
||||
c = input.charAt(++position)
|
||||
);
|
||||
return { result: input.slice(start, position), position };
|
||||
}
|
||||
|
||||
window.__bootstrap.infra = {
|
||||
collectSequenceOfCodepoints,
|
||||
};
|
||||
})(globalThis);
|
242
op_crates/web/01_mimesniff.js
Normal file
242
op_crates/web/01_mimesniff.js
Normal file
|
@ -0,0 +1,242 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// @ts-check
|
||||
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||
/// <reference path="../web/internal.d.ts" />
|
||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { collectSequenceOfCodepoints } = window.__bootstrap.infra;
|
||||
|
||||
const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"];
|
||||
const HTTP_WHITESPACE = ["\u000A", "\u000D", ...HTTP_TAB_OR_SPACE];
|
||||
|
||||
const ASCII_DIGIT = ["\u0030-\u0039"];
|
||||
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
|
||||
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
|
||||
const ASCII_ALPHA = [...ASCII_UPPER_ALPHA, ...ASCII_LOWER_ALPHA];
|
||||
const ASCII_ALPHANUMERIC = [...ASCII_DIGIT, ...ASCII_ALPHA];
|
||||
const HTTP_TOKEN_CODE_POINT = [
|
||||
"\u0021",
|
||||
"\u0023",
|
||||
"\u0025",
|
||||
"\u0026",
|
||||
"\u0027",
|
||||
"\u002A",
|
||||
"\u002B",
|
||||
"\u002D",
|
||||
"\u002E",
|
||||
"\u005E",
|
||||
"\u005F",
|
||||
"\u0060",
|
||||
"\u007C",
|
||||
"\u007E",
|
||||
...ASCII_ALPHANUMERIC,
|
||||
];
|
||||
const HTTP_TOKEN_CODE_POINT_RE = new RegExp(`^[${HTTP_TOKEN_CODE_POINT}]+$`);
|
||||
const HTTP_QUOTED_STRING_TOKEN_POINT = [
|
||||
"\u0009",
|
||||
"\u0020-\u007E",
|
||||
"\u0080-\u00FF",
|
||||
];
|
||||
const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp(
|
||||
`^[${HTTP_QUOTED_STRING_TOKEN_POINT}]+$`,
|
||||
);
|
||||
|
||||
/**
|
||||
* https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
|
||||
* @param {string} input
|
||||
* @param {number} position
|
||||
* @param {boolean} extractValue
|
||||
* @returns {{result: string, position: number}}
|
||||
*/
|
||||
function collectHttpQuotedString(input, position, extractValue) {
|
||||
// 1.
|
||||
const positionStart = position;
|
||||
// 2.
|
||||
let value = "";
|
||||
// 3.
|
||||
if (input[position] !== "\u0022") throw new Error('must be "');
|
||||
// 4.
|
||||
position++;
|
||||
// 5.
|
||||
while (true) {
|
||||
// 5.1.
|
||||
const res = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c !== "\u0022" && c !== "\u005C",
|
||||
);
|
||||
value += res.result;
|
||||
position = res.position;
|
||||
// 5.2.
|
||||
if (position >= input.length) break;
|
||||
// 5.3.
|
||||
const quoteOrBackslash = input[position];
|
||||
// 5.4.
|
||||
position++;
|
||||
// 5.5.
|
||||
if (quoteOrBackslash === "\u005C") {
|
||||
// 5.5.1.
|
||||
if (position >= input.length) {
|
||||
value += "\u005C";
|
||||
break;
|
||||
}
|
||||
// 5.5.2.
|
||||
value += input[position];
|
||||
// 5.5.3.
|
||||
position++;
|
||||
} else { // 5.6.
|
||||
// 5.6.1
|
||||
if (input[position] !== "\u0022") throw new Error('must be "');
|
||||
// 5.6.2
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 6.
|
||||
if (extractValue) return { result: value, position };
|
||||
// 7.
|
||||
return { result: input.substring(positionStart, position + 1), position };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
*/
|
||||
function parseMimeType(input) {
|
||||
// 1.
|
||||
input = input.replaceAll(new RegExp(`^[${HTTP_WHITESPACE}]+`, "g"), "");
|
||||
input = input.replaceAll(new RegExp(`[${HTTP_WHITESPACE}]+$`, "g"), "");
|
||||
|
||||
// 2.
|
||||
let position = 0;
|
||||
const endOfInput = input.length;
|
||||
|
||||
// 3.
|
||||
const res1 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c != "\u002F",
|
||||
);
|
||||
const type = res1.result;
|
||||
position = res1.position;
|
||||
|
||||
// 4.
|
||||
if (type === "" || !HTTP_TOKEN_CODE_POINT_RE.test(type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 5.
|
||||
if (position >= endOfInput) return null;
|
||||
|
||||
// 6.
|
||||
position++;
|
||||
|
||||
// 7.
|
||||
const res2 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c != "\u003B",
|
||||
);
|
||||
let subtype = res2.result;
|
||||
position = res2.position;
|
||||
|
||||
// 8.
|
||||
subtype = subtype.replaceAll(new RegExp(`[${HTTP_WHITESPACE}]+$`, "g"), "");
|
||||
|
||||
// 9.
|
||||
if (subtype === "" || !HTTP_TOKEN_CODE_POINT_RE.test(subtype)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 10.
|
||||
const mimeType = {
|
||||
type: type.toLowerCase(),
|
||||
subtype: subtype.toLowerCase(),
|
||||
/** @type {Map<string, string>} */
|
||||
parameters: new Map(),
|
||||
};
|
||||
|
||||
// 11.
|
||||
while (position < endOfInput) {
|
||||
// 11.1.
|
||||
position++;
|
||||
|
||||
// 11.2.
|
||||
const res1 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => HTTP_WHITESPACE.includes(c),
|
||||
);
|
||||
position = res1.position;
|
||||
|
||||
// 11.3.
|
||||
const res2 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c !== "\u003B" && c !== "\u003D",
|
||||
);
|
||||
let parameterName = res2.result;
|
||||
position = res2.position;
|
||||
|
||||
// 11.4.
|
||||
parameterName = parameterName.toLowerCase();
|
||||
|
||||
// 11.5.
|
||||
if (position < endOfInput) {
|
||||
if (input[position] == "\u003B") continue;
|
||||
position++;
|
||||
}
|
||||
|
||||
// 11.6.
|
||||
if (position >= endOfInput) break;
|
||||
|
||||
// 11.7.
|
||||
let parameterValue = null;
|
||||
|
||||
// 11.8.
|
||||
if (input[position] == "\u0022") {
|
||||
// 11.8.1.
|
||||
const res = collectHttpQuotedString(input, position, true);
|
||||
parameterValue = res.result;
|
||||
position = res.position;
|
||||
|
||||
// 11.8.2.
|
||||
position++;
|
||||
} else { // 11.9.
|
||||
// 11.9.1.
|
||||
const res = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c !== "\u003B",
|
||||
);
|
||||
parameterValue = res.result;
|
||||
position = res.position;
|
||||
|
||||
// 11.9.2.
|
||||
parameterValue = parameterValue.replaceAll(
|
||||
new RegExp(`[${HTTP_WHITESPACE}]+$`, "g"),
|
||||
"",
|
||||
);
|
||||
|
||||
// 11.9.3.
|
||||
if (parameterValue === "") continue;
|
||||
}
|
||||
|
||||
// 11.9.
|
||||
if (
|
||||
parameterName !== "" && HTTP_TOKEN_CODE_POINT_RE.test(parameterName) &&
|
||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE.test(parameterValue) &&
|
||||
!mimeType.parameters.has(parameterName)
|
||||
) {
|
||||
mimeType.parameters.set(parameterName, parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
// 12.
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
window.__bootstrap.mimesniff = { parseMimeType };
|
||||
})(this);
|
|
@ -4545,10 +4545,38 @@
|
|||
fromByteArray,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
*/
|
||||
function decode(bytes, encoding) {
|
||||
const BOMEncoding = BOMSniff(bytes);
|
||||
let start = 0;
|
||||
if (BOMEncoding !== null) {
|
||||
encoding = BOMEncoding;
|
||||
if (BOMEncoding === "UTF-8") start = 3;
|
||||
else start = 2;
|
||||
}
|
||||
return new TextDecoder(encoding).decode(bytes.slice(start));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
*/
|
||||
function BOMSniff(bytes) {
|
||||
const BOM = bytes.subarray(0, 3);
|
||||
if (BOM[0] === 0xEF && BOM[1] === 0xBB && BOM[2] === 0xBF) {
|
||||
return "UTF-8";
|
||||
}
|
||||
if (BOM[0] === 0xFE && BOM[1] === 0xFF) return "UTF-16BE";
|
||||
if (BOM[0] === 0xFF && BOM[1] === 0xFE) return "UTF-16LE";
|
||||
return null;
|
||||
}
|
||||
|
||||
window.TextEncoder = TextEncoder;
|
||||
window.TextDecoder = TextDecoder;
|
||||
window.atob = atob;
|
||||
window.btoa = btoa;
|
||||
window.__bootstrap = window.__bootstrap || {};
|
||||
window.__bootstrap.encoding = { decode };
|
||||
window.__bootstrap.base64 = base64;
|
||||
})(this);
|
||||
|
|
19
op_crates/web/internal.d.ts
vendored
19
op_crates/web/internal.d.ts
vendored
|
@ -5,6 +5,25 @@
|
|||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare var infra: {
|
||||
collectSequenceOfCodepoints(
|
||||
input: string,
|
||||
position: number,
|
||||
condition: (char: string) => boolean,
|
||||
): {
|
||||
result: string;
|
||||
position: number;
|
||||
};
|
||||
};
|
||||
|
||||
declare var mimesniff: {
|
||||
parseMimeType(input: string): {
|
||||
type: string;
|
||||
subtype: string;
|
||||
parameters: Map<string, string>;
|
||||
} | null;
|
||||
};
|
||||
|
||||
declare var eventTarget: {
|
||||
EventTarget: typeof EventTarget;
|
||||
};
|
||||
|
|
|
@ -6,10 +6,18 @@ use std::path::PathBuf;
|
|||
/// Load and execute the javascript code.
|
||||
pub fn init(isolate: &mut JsRuntime) {
|
||||
let files = vec![
|
||||
(
|
||||
"deno:op_crates/web/00_infra.js",
|
||||
include_str!("00_infra.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/web/01_dom_exception.js",
|
||||
include_str!("01_dom_exception.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/web/01_mimesniff.js",
|
||||
include_str!("01_mimesniff.js"),
|
||||
),
|
||||
(
|
||||
"deno:op_crates/web/02_event.js",
|
||||
include_str!("02_event.js"),
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f897da00871cf39366bc2f0ceec051c65bc75703
|
||||
Subproject commit a522daf78a71c2252d10c978f09cf0575aceb794
|
|
@ -787,6 +787,29 @@
|
|||
"fileReader.any.js": true,
|
||||
"url": {
|
||||
"url-format.any.js": true
|
||||
},
|
||||
"reading-data-section": {
|
||||
"Determining-Encoding.any.js": true,
|
||||
"FileReader-event-handler-attributes.any.js": true,
|
||||
"FileReader-multiple-reads.any.js": true,
|
||||
"filereader_abort.any.js": true,
|
||||
"filereader_error.any.js": true,
|
||||
"filereader_events.any.js": false,
|
||||
"filereader_readAsArrayBuffer.any.js": true,
|
||||
"filereader_readAsBinaryString.any.js": true,
|
||||
"filereader_readAsDataURL.any.js": true,
|
||||
"filereader_readAsText.any.js": true,
|
||||
"filereader_readystate.any.js": true,
|
||||
"filereader_result.any.js": [
|
||||
"result is null during \"loadstart\" event for readAsText",
|
||||
"result is null during \"loadstart\" event for readAsDataURL",
|
||||
"result is null during \"loadstart\" event for readAsArrayBuffer",
|
||||
"result is null during \"loadstart\" event for readAsBinaryString",
|
||||
"result is null during \"progress\" event for readAsText",
|
||||
"result is null during \"progress\" event for readAsDataURL",
|
||||
"result is null during \"progress\" event for readAsArrayBuffer",
|
||||
"result is null during \"progress\" event for readAsBinaryString"
|
||||
]
|
||||
}
|
||||
},
|
||||
"html": {
|
||||
|
|
Loading…
Add table
Reference in a new issue