Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions test/fixtures/wpt/FileAPI/BlobURL/WEB_FEATURES.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rules:
- "**": [file]
Original file line number Diff line number Diff line change
Expand Up @@ -247,5 +247,52 @@
});
}, "Blob URL area element click should enforce noopener for a cross-top-level-site navigation");

const open_blob_url_window_via_svg_a_click = (blob_url) => `
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const a = document.createElementNS("http://www.w3.org/2000/svg", "a");
a.setAttribute("href", "${blob_url}");
a.setAttribute("target", "_blank");
a.setAttribute("rel", "opener");
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.textContent = "click";
a.appendChild(text);
svg.appendChild(a);
document.body.appendChild(svg);
a.dispatchEvent(new MouseEvent("click", {bubbles: true}));
`;

// Tests blob URL SVG `<a target="_blank" rel="opener">` click for same and
// cross partition iframes.
promise_test(t => {
return new Promise(async (resolve, reject) => {
try {
// Creates same and cross partition iframes.
const noopener_response_queue = token();

const [cross_site_iframe_uuid, same_site_iframe_uuid] = await create_test_iframes(t, token());

const blob = new Blob([opener_check_frame_html(noopener_response_queue)], {type : "text/html"});
const blob_url = URL.createObjectURL(blob);

// Attempt to click blob URL in cross partition iframe.
await send(cross_site_iframe_uuid, open_blob_url_window_via_svg_a_click(blob_url));
const noopener_response_1 = await receive(noopener_response_queue);
if (noopener_response_1 !== opener_null_response) {
reject(`Blob URL page opener wasn't null in not-same-top-level-site iframe.`);
}

// Attempt to click blob URL in same partition iframe.
await send(same_site_iframe_uuid, open_blob_url_window_via_svg_a_click(blob_url));
const noopener_response_2 = await receive(noopener_response_queue);
if (noopener_response_2 !== opener_not_null_response) {
reject(`Blob URL page opener was null in same-top-level-site iframe`);
}
resolve();
} catch (e) {
reject(e);
}
});
}, "Blob URL SVG <a> element click should enforce noopener for a cross-top-level-site navigation");

</script>
</body>
47 changes: 47 additions & 0 deletions test/fixtures/wpt/FileAPI/BlobURL/opaque-origin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!doctype html>
<html>
<head>
<meta charset=utf-8>
<title>Opaque origin blob URL created from workers</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
</head>
<body>
<script>

promise_test(t => {
return new Promise((resolve) => {
const worker_code = `
const blob = new Blob([''], {type: 'text/javascript'});
const url = URL.createObjectURL(blob);
self.postMessage(url);
`;
const worker = new Worker("data:text/javascript," + encodeURIComponent(worker_code));
worker.onmessage = t.step_func(e => {
assert_regexp_match(e.data, /^blob:null\//, "Blob URL should have 'null' as serialized origin");
resolve();
});
});
}, "Blob URL created from data URL dedicated worker should have 'null' origin");

promise_test(t => {
return new Promise((resolve) => {
const worker_code = `
self.onconnect = e => {
const blob = new Blob([''], {type: 'text/javascript'});
const url = URL.createObjectURL(blob);
e.ports[0].postMessage(url);
};
`;
const worker = new SharedWorker("data:text/javascript," + encodeURIComponent(worker_code));
worker.port.onmessage = t.step_func(e => {
assert_regexp_match(e.data, /^blob:null\//, "Blob URL should have 'null' as serialized origin");
resolve();
});
worker.port.start();
});
}, "Blob URL created from data URL shared worker should have 'null' origin");

</script>
</body>
</html>
2 changes: 2 additions & 0 deletions test/fixtures/wpt/FileAPI/FileReader/WEB_FEATURES.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rules:
- "**": [file]
8 changes: 7 additions & 1 deletion test/fixtures/wpt/FileAPI/FileReaderSync.worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test(() => {

test(() => {
var data = readerSync.readAsDataURL(empty_blob);
assert_equals(data.indexOf("data:"), 0);
assert_equals(data, "data:application/octet-stream;base64,");
}, "readAsDataURL with empty blob");

test(() => {
Expand All @@ -41,6 +41,12 @@ test(() => {
assert_equals(data, "");
}, "readAsBinaryString with empty blob");

test(() => {
var data = readerSync.readAsBinaryString(new Blob(["σ"]));
assert_equals(data.length, 2, "The result length is 2");
assert_equals(data, "\xcf\x83", "The result is \xcf\x83");
}, "readAsBinaryString with multi-byte UTF-8 char");

test(() => {
var data = readerSync.readAsArrayBuffer(blob);
assert_true(data instanceof ArrayBuffer);
Expand Down
5 changes: 2 additions & 3 deletions test/fixtures/wpt/FileAPI/WEB_FEATURES.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
features:
- name: file
files: "**"
rules:
- "*": [file]
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Blob data isolation from input ArrayBuffers</title>
<link rel="help" href="https://w3c.github.io/FileAPI/#constructorBlob">

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<script>
"use strict";

// These tests verify that Blob correctly copies its input data, so that
// subsequent modifications to the input do not affect the Blob's contents.
// This is especially important to test with small buffers that might trigger
// buffer pool optimizations in some implementations.

function readBlobAsArrayBuffer(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(blob);
});
}

promise_test(async () => {
const input = new Uint8Array([1, 2, 3, 4]);
const blob = new Blob([input]);

input[0] = 99;

const result = new Uint8Array(await readBlobAsArrayBuffer(blob));
assert_array_equals(result, [1, 2, 3, 4], "Blob data should not be affected by modifying the input Uint8Array");
}, "Modifying a Uint8Array after Blob construction does not affect the Blob");

promise_test(async () => {
const input = new Uint8Array([5, 6, 7, 8]);
const inputBuffer = input.buffer;
const blob = new Blob([inputBuffer]);

input[0] = 99;

const result = new Uint8Array(await readBlobAsArrayBuffer(blob));
assert_array_equals(result, [5, 6, 7, 8], "Blob data should not be affected by modifying the underlying ArrayBuffer");
}, "Modifying an ArrayBuffer after Blob construction does not affect the Blob");

promise_test(async () => {
// Test with a view into a larger buffer (common pattern that exercises offset handling)
const largerBuffer = new ArrayBuffer(16);
const view = new Uint8Array(largerBuffer, 4, 4);
view.set([10, 11, 12, 13]);

const blob = new Blob([view]);

view[0] = 99;

const result = new Uint8Array(await readBlobAsArrayBuffer(blob));
assert_array_equals(result, [10, 11, 12, 13], "Blob data should not be affected by modifying a typed array view");
}, "Modifying a typed array view after Blob construction does not affect the Blob");

promise_test(async () => {
// Very small buffer - most likely to hit buffer pool optimizations
const input = new Uint8Array([42]);
const blob = new Blob([input]);

input[0] = 0;

const result = new Uint8Array(await readBlobAsArrayBuffer(blob));
assert_array_equals(result, [42], "Blob data should not be affected even with single-byte input");
}, "Modifying a single-byte Uint8Array after Blob construction does not affect the Blob");

promise_test(async () => {
const blob = new Blob([new Uint8Array([1, 2, 3, 4])]);

const result1 = await readBlobAsArrayBuffer(blob);
new Uint8Array(result1)[0] = 99;

const result2 = new Uint8Array(await readBlobAsArrayBuffer(blob));
assert_array_equals(
result2,
[1, 2, 3, 4],
"Blob data should not be affected by modifying a previously-read ArrayBuffer"
);
}, "Modifying a returned ArrayBuffer does not affect subsequent reads");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// META: title=Blob constructor with detached ArrayBuffer/ArrayBufferView
// META: script=../support/Blob.js
'use strict';

function detachBuffer(buffer) {
// Transfer the buffer to detach it. Works in both window and worker contexts.
new MessageChannel().port1.postMessage(buffer, [buffer]);
}

test_blob(function() {
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);
view[0] = 0x41;
view[1] = 0x42;
view[2] = 0x43;
view[3] = 0x44;
detachBuffer(buffer);
return new Blob([view]);
}, {
expected: '',
type: '',
desc: 'Blob from a detached ArrayBufferView should be empty'
});

test_blob(function() {
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer, 1, 2); // offset=1, length=2
view[0] = 0x41;
view[1] = 0x42;
detachBuffer(buffer);
return new Blob([view]);
}, {
expected: '',
type: '',
desc: 'Blob from a detached ArrayBufferView with offset should be empty'
});

test_blob(function() {
const buffer = new ArrayBuffer(4);
detachBuffer(buffer);
return new Blob([buffer]);
}, {
expected: '',
type: '',
desc: 'Blob from a detached ArrayBuffer should be empty'
});

test_blob(function() {
const buffer = new ArrayBuffer(4);
const view = new Uint8Array(buffer);
view[0] = 0x41;
detachBuffer(buffer);
// Mix detached view with a string
return new Blob([view, 'hello']);
}, {
expected: 'hello',
type: '',
desc: 'Blob from a detached ArrayBufferView mixed with string, detached part ignored'
});
35 changes: 35 additions & 0 deletions test/fixtures/wpt/FileAPI/blob/Blob-constructor-endings.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// META: title=Blob constructor: endings option
[
'transparent',
'native'
].forEach(value => test(t => {
assert_class_string(new Blob([], {endings: value}), 'Blob',
`Constructor should allow "${value}" endings`);
}, `Valid "endings" value: ${JSON.stringify(value)}`));

[
null,
'',
'invalidEnumValue',
'Transparent',
'NATIVE',
0,
{}
].forEach(value => test(t => {
assert_throws_js(TypeError, () => new Blob([], {endings: value}),
'Blob constructor should throw');
}, `Invalid "endings" value: ${JSON.stringify(value)}`));

test(t => {
const test_error = {name: 'test'};
assert_throws_exactly(
test_error,
() => new Blob([], { get endings() { throw test_error; }}),
'Blob constructor should propagate exceptions from "endings" property');
}, 'Exception propagation from options');

test(t => {
let got = false;
new Blob([], { get endings() { got = true; } });
assert_true(got, 'The "endings" property was accessed during construction.');
}, 'The "endings" options property is used');
35 changes: 0 additions & 35 deletions test/fixtures/wpt/FileAPI/blob/Blob-constructor-endings.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,6 @@
});
}

[
'transparent',
'native'
].forEach(value => test(t => {
assert_class_string(new Blob([], {endings: value}), 'Blob',
`Constructor should allow "${value}" endings`);
}, `Valid "endings" value: ${JSON.stringify(value)}`));

[
null,
'',
'invalidEnumValue',
'Transparent',
'NATIVE',
0,
{}
].forEach(value => test(t => {
assert_throws_js(TypeError, () => new Blob([], {endings: value}),
'Blob constructor should throw');
}, `Invalid "endings" value: ${JSON.stringify(value)}`));

test(t => {
const test_error = {name: 'test'};
assert_throws_exactly(
test_error,
() => new Blob([], { get endings() { throw test_error; }}),
'Blob constructor should propagate exceptions from "endings" property');
}, 'Exception propagation from options');

test(t => {
let got = false;
new Blob([], { get endings() { got = true; } });
assert_true(got, 'The "endings" property was accessed during construction.');
}, 'The "endings" options property is used');

[
{name: 'LF', input: '\n', native: native_ending},
{name: 'CR', input: '\r', native: native_ending},
Expand Down
Loading
Loading