summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/FileAPI/url
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/FileAPI/url
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/FileAPI/url')
-rw-r--r--testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html62
-rw-r--r--testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html26
-rw-r--r--testing/web-platform/tests/FileAPI/url/resources/create-helper.html7
-rw-r--r--testing/web-platform/tests/FileAPI/url/resources/create-helper.js4
-rw-r--r--testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js71
-rw-r--r--testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html7
-rw-r--r--testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js9
-rw-r--r--testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html32
-rw-r--r--testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html23
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-charset.window.js34
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-format.any.js70
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js115
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-in-tags.window.js48
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-lifetime.html56
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-reload.window.js36
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js72
-rw-r--r--testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js68
-rw-r--r--testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html45
-rw-r--r--testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html28
-rw-r--r--testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html12
-rw-r--r--testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html27
21 files changed, 852 insertions, 0 deletions
diff --git a/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html b/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html
new file mode 100644
index 0000000000..ce9d680709
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/cross-global-revoke.sub.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<body>
+<script>
+async_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ const frame = document.createElement('iframe');
+ frame.setAttribute('style', 'display:none;');
+ frame.src = 'resources/revoke-helper.html';
+ document.body.appendChild(frame);
+
+ frame.onload = t.step_func(e => {
+ frame.contentWindow.postMessage({url: url}, '*');
+ });
+
+ self.addEventListener('message', t.step_func(e => {
+ if (e.source !== frame.contentWindow) return;
+ assert_equals(e.data, 'revoked');
+ promise_rejects_js(t, TypeError, fetch(url)).then(t.step_func_done());
+ }));
+}, 'It is possible to revoke same-origin blob URLs from different frames.');
+
+async_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ const worker = new Worker('resources/revoke-helper.js');
+ worker.onmessage = t.step_func(e => {
+ assert_equals(e.data, 'revoked');
+ promise_rejects_js(t, TypeError, fetch(url)).then(t.step_func_done());
+ });
+ worker.postMessage({url: url});
+}, 'It is possible to revoke same-origin blob URLs from a different worker global.');
+
+async_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ const frame = document.createElement('iframe');
+ frame.setAttribute('style', 'display:none;');
+ frame.src = get_host_info().HTTP_REMOTE_ORIGIN + '/FileAPI/url/resources/revoke-helper.html';
+ document.body.appendChild(frame);
+
+ frame.onload = t.step_func(e => {
+ frame.contentWindow.postMessage({url: url}, '*');
+ });
+
+ self.addEventListener('message', t.step_func(e => {
+ if (e.source !== frame.contentWindow) return;
+ assert_equals(e.data, 'revoked');
+ fetch(url).then(response => response.text()).then(t.step_func_done(text => {
+ assert_equals(text, blob_contents);
+ }), t.unreached_func('Unexpected promise rejection'));
+ }));
+}, 'It is not possible to revoke cross-origin blob URLs.');
+
+</script>
diff --git a/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html b/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html
new file mode 100644
index 0000000000..0052b26fa6
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/multi-global-origin-serialization.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Blob URL serialization (specifically the origin) in multi-global situations</title>
+<link rel="help" href="https://w3c.github.io/FileAPI/#unicodeBlobURL">
+<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<!-- this page is the entry global -->
+
+<iframe src="//{{domains[www]}}:{{location[port]}}/FileAPI/support/incumbent.sub.html"></iframe>
+
+<script>
+"use strict";
+setup({ single_test: true });
+document.domain = "{{host}}";
+
+window.onload = () => {
+ const url = frames[0].createBlobURL();
+ const desired = "blob:{{location[scheme]}}://www1";
+ assert_equals(url.substring(0, desired.length), desired,
+ "Origin should contain www1, from the current settings object");
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/FileAPI/url/resources/create-helper.html b/testing/web-platform/tests/FileAPI/url/resources/create-helper.html
new file mode 100644
index 0000000000..fa6cf4e671
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/resources/create-helper.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+self.addEventListener('message', e => {
+ let url = URL.createObjectURL(e.data.blob);
+ e.source.postMessage({url: url}, '*');
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/FileAPI/url/resources/create-helper.js b/testing/web-platform/tests/FileAPI/url/resources/create-helper.js
new file mode 100644
index 0000000000..e6344f700c
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/resources/create-helper.js
@@ -0,0 +1,4 @@
+self.addEventListener('message', e => {
+ let url = URL.createObjectURL(e.data.blob);
+ self.postMessage({url: url});
+});
diff --git a/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js b/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js
new file mode 100644
index 0000000000..a81ea1e7b1
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/resources/fetch-tests.js
@@ -0,0 +1,71 @@
+// This method generates a number of tests verifying fetching of blob URLs,
+// allowing the same tests to be used both with fetch() and XMLHttpRequest.
+//
+// |fetch_method| is only used in test names, and should describe the
+// (javascript) method being used by the other two arguments (i.e. 'fetch' or 'XHR').
+//
+// |fetch_should_succeed| is a callback that is called with the Test and a URL.
+// Fetching the URL is expected to succeed. The callback should return a promise
+// resolved with whatever contents were fetched.
+//
+// |fetch_should_fail| similarly is a callback that is called with the Test, a URL
+// to fetch, and optionally a method to use to do the fetch. If no method is
+// specified the callback should use the 'GET' method. Fetching of these URLs is
+// expected to fail, and the callback should return a promise that resolves iff
+// fetching did indeed fail.
+function fetch_tests(fetch_method, fetch_should_succeed, fetch_should_fail) {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+
+ return fetch_should_succeed(t, url).then(text => {
+ assert_equals(text, blob_contents);
+ });
+ }, 'Blob URLs can be used in ' + fetch_method);
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+
+ return fetch_should_succeed(t, url + '#fragment').then(text => {
+ assert_equals(text, blob_contents);
+ });
+ }, fetch_method + ' with a fragment should succeed');
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+ URL.revokeObjectURL(url);
+
+ return fetch_should_fail(t, url);
+ }, fetch_method + ' of a revoked URL should fail');
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+ URL.revokeObjectURL(url + '#fragment');
+
+ return fetch_should_succeed(t, url).then(text => {
+ assert_equals(text, blob_contents);
+ });
+ }, 'Only exact matches should revoke URLs, using ' + fetch_method);
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+
+ return fetch_should_fail(t, url + '?querystring');
+ }, 'Appending a query string should cause ' + fetch_method + ' to fail');
+
+ promise_test(t => {
+ const url = URL.createObjectURL(blob);
+
+ return fetch_should_fail(t, url + '/path');
+ }, 'Appending a path should cause ' + fetch_method + ' to fail');
+
+ for (const method of ['HEAD', 'POST', 'DELETE', 'OPTIONS', 'PUT', 'CUSTOM']) {
+ const url = URL.createObjectURL(blob);
+
+ promise_test(t => {
+ return fetch_should_fail(t, url, method);
+ }, fetch_method + ' with method "' + method + '" should fail');
+ }
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html
new file mode 100644
index 0000000000..adf5a014a6
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<script>
+self.addEventListener('message', e => {
+ URL.revokeObjectURL(e.data.url);
+ e.source.postMessage('revoked', '*');
+});
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js
new file mode 100644
index 0000000000..c3e05b64b1
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/resources/revoke-helper.js
@@ -0,0 +1,9 @@
+self.addEventListener('message', e => {
+ URL.revokeObjectURL(e.data.url);
+ // Registering a new object URL will make absolutely sure that the revocation
+ // has propagated. Without this at least in chrome it is possible for the
+ // below postMessage to arrive at its destination before the revocation has
+ // been fully processed.
+ URL.createObjectURL(new Blob([]));
+ self.postMessage('revoked');
+});
diff --git a/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html b/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html
new file mode 100644
index 0000000000..a52939a3eb
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/sandboxed-iframe.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>FileAPI Test: Verify behavior of Blob URL in unique origins</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="sandboxed-iframe" sandbox="allow-scripts"></iframe>
+
+<script>
+
+const iframe_scripts = [
+ 'resources/fetch-tests.js',
+ 'url-format.any.js',
+ 'url-in-tags.window.js',
+ 'url-with-xhr.any.js',
+ 'url-with-fetch.any.js',
+];
+
+let html = '<!doctype html>\n<meta charset="utf-8">\n<body>\n';
+html = html + '<script src="/resources/testharness.js"></' + 'script>\n';
+html = html + '<script>setup({"explicit_timeout": true});</' + 'script>\n';
+for (const script of iframe_scripts)
+ html = html + '<script src="' + script + '"></' + 'script>\n';
+
+const frame = document.querySelector('#sandboxed-iframe');
+frame.setAttribute('srcdoc', html);
+frame.setAttribute('style', 'display:none;');
+
+fetch_tests_from_window(frame.contentWindow);
+
+</script>
diff --git a/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html b/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html
new file mode 100644
index 0000000000..2c4921c034
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/unicode-origin.sub.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FileAPI Test: Verify origin of Blob URL</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+async_test(t => {
+ const frame = document.createElement('iframe');
+ self.addEventListener('message', t.step_func(e => {
+ if (e.source != frame.contentWindow) return;
+ const url = e.data.url;
+ assert_false(url.includes('天気の良い日'),
+ 'Origin should be ascii rather than unicode');
+ assert_equals(new URL(url).origin, e.origin,
+ 'Origin of URL should match origin of frame');
+ assert_true(url.startsWith('blob:{{location[scheme]}}://xn--'));
+ t.done();
+ }));
+ frame.src = '{{location[scheme]}}://{{domains[天気の良い日]}}:{{location[port]}}/FileAPI/support/url-origin.html';
+ document.body.appendChild(frame);
+}, 'Verify serialization of non-ascii origin in Blob URLs');
+</script>
diff --git a/testing/web-platform/tests/FileAPI/url/url-charset.window.js b/testing/web-platform/tests/FileAPI/url/url-charset.window.js
new file mode 100644
index 0000000000..777709b64a
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-charset.window.js
@@ -0,0 +1,34 @@
+async_test(t => {
+ // This could be detected as ISO-2022-JP, in which case there would be no
+ // <textarea>, and thus the script inside would be interpreted as actual
+ // script.
+ const blob = new Blob(
+ [
+ `aaa\u001B$@<textarea>\u001B(B<script>/* xss */<\/script></textarea>bbb`
+ ],
+ {type: 'text/html;charset=utf-8'});
+ const url = URL.createObjectURL(blob);
+ const win = window.open(url);
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ win.onload = t.step_func_done(() => {
+ assert_equals(win.document.charset, 'UTF-8');
+ });
+}, 'Blob charset should override any auto-detected charset.');
+
+async_test(t => {
+ const blob = new Blob(
+ [`<!doctype html>\n<meta charset="ISO-8859-1">`],
+ {type: 'text/html;charset=utf-8'});
+ const url = URL.createObjectURL(blob);
+ const win = window.open(url);
+ t.add_cleanup(() => {
+ win.close();
+ });
+
+ win.onload = t.step_func_done(() => {
+ assert_equals(win.document.charset, 'UTF-8');
+ });
+}, 'Blob charset should override <meta charset>.');
diff --git a/testing/web-platform/tests/FileAPI/url/url-format.any.js b/testing/web-platform/tests/FileAPI/url/url-format.any.js
new file mode 100644
index 0000000000..69c51113e6
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-format.any.js
@@ -0,0 +1,70 @@
+// META: timeout=long
+const blob = new Blob(['test']);
+const file = new File(['test'], 'name');
+
+test(t => {
+ const url_count = 5000;
+ let list = [];
+
+ t.add_cleanup(() => {
+ for (let url of list) {
+ URL.revokeObjectURL(url);
+ }
+ });
+
+ for (let i = 0; i < url_count; ++i)
+ list.push(URL.createObjectURL(blob));
+
+ list.sort();
+
+ for (let i = 1; i < list.length; ++i)
+ assert_not_equals(list[i], list[i-1], 'generated Blob URLs should be unique');
+}, 'Generated Blob URLs are unique');
+
+test(() => {
+ const url = URL.createObjectURL(blob);
+ assert_equals(typeof url, 'string');
+ assert_true(url.startsWith('blob:'));
+}, 'Blob URL starts with "blob:"');
+
+test(() => {
+ const url = URL.createObjectURL(file);
+ assert_equals(typeof url, 'string');
+ assert_true(url.startsWith('blob:'));
+}, 'Blob URL starts with "blob:" for Files');
+
+test(() => {
+ const url = URL.createObjectURL(blob);
+ assert_equals(new URL(url).origin, location.origin);
+ if (location.origin !== 'null') {
+ assert_true(url.includes(location.origin));
+ assert_true(url.startsWith('blob:' + location.protocol));
+ }
+}, 'Origin of Blob URL matches our origin');
+
+test(() => {
+ const url = URL.createObjectURL(blob);
+ const url_record = new URL(url);
+ assert_equals(url_record.protocol, 'blob:');
+ assert_equals(url_record.origin, location.origin);
+ assert_equals(url_record.host, '', 'host should be an empty string');
+ assert_equals(url_record.port, '', 'port should be an empty string');
+ const uuid_path_re = /\/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
+ assert_true(uuid_path_re.test(url_record.pathname), 'Path must end with a valid UUID');
+ if (location.origin !== 'null') {
+ const nested_url = new URL(url_record.pathname);
+ assert_equals(nested_url.origin, location.origin);
+ assert_equals(nested_url.pathname.search(uuid_path_re), 0, 'Path must be a valid UUID');
+ assert_true(url.includes(location.origin));
+ assert_true(url.startsWith('blob:' + location.protocol));
+ }
+}, 'Blob URL parses correctly');
+
+test(() => {
+ const url = URL.createObjectURL(file);
+ assert_equals(new URL(url).origin, location.origin);
+ if (location.origin !== 'null') {
+ assert_true(url.includes(location.origin));
+ assert_true(url.startsWith('blob:' + location.protocol));
+ }
+}, 'Origin of Blob URL matches our origin for Files');
diff --git a/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js b/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js
new file mode 100644
index 0000000000..1cdad79f7e
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-in-tags-revoke.window.js
@@ -0,0 +1,115 @@
+// META: timeout=long
+async_test(t => {
+ const run_result = 'test_frame_OK';
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<script>window.test_result = "' + run_result + '";</script>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+
+ const frame = document.createElement('iframe');
+ frame.setAttribute('src', url);
+ frame.setAttribute('style', 'display:none;');
+ document.body.appendChild(frame);
+ URL.revokeObjectURL(url);
+
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.test_result, run_result);
+ });
+}, 'Fetching a blob URL immediately before revoking it works in an iframe.');
+
+async_test(t => {
+ const run_result = 'test_frame_OK';
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<script>window.test_result = "' + run_result + '";</script>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+
+ const frame = document.createElement('iframe');
+ frame.setAttribute('src', '/common/blank.html');
+ frame.setAttribute('style', 'display:none;');
+ document.body.appendChild(frame);
+
+ frame.onload = t.step_func(() => {
+ frame.contentWindow.location = url;
+ URL.revokeObjectURL(url);
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.test_result, run_result);
+ });
+ });
+}, 'Fetching a blob URL immediately before revoking it works in an iframe navigation.');
+
+async_test(t => {
+ const run_result = 'test_frame_OK';
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<script>window.test_result = "' + run_result + '";</script>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+ const win = window.open(url);
+ URL.revokeObjectURL(url);
+ add_completion_callback(() => { win.close(); });
+
+ win.onload = t.step_func_done(() => {
+ assert_equals(win.test_result, run_result);
+ });
+}, 'Opening a blob URL in a new window immediately before revoking it works.');
+
+function receive_message_on_channel(t, channel_name) {
+ const channel = new BroadcastChannel(channel_name);
+ return new Promise(resolve => {
+ channel.addEventListener('message', t.step_func(e => {
+ resolve(e.data);
+ }));
+ });
+}
+
+function window_contents_for_channel(channel_name) {
+ return '<!doctype html>\n' +
+ '<script>\n' +
+ 'new BroadcastChannel("' + channel_name + '").postMessage("foobar");\n' +
+ 'self.close();\n' +
+ '</script>';
+}
+
+async_test(t => {
+ const channel_name = 'noopener-window-test';
+ const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'});
+ receive_message_on_channel(t, channel_name).then(t.step_func_done(t => {
+ assert_equals(t, 'foobar');
+ }));
+ const url = URL.createObjectURL(blob);
+ const win = window.open();
+ win.opener = null;
+ win.location = url;
+ URL.revokeObjectURL(url);
+}, 'Opening a blob URL in a noopener about:blank window immediately before revoking it works.');
+
+async_test(t => {
+ const run_result = 'test_script_OK';
+ const blob_contents = 'window.script_test_result = "' + run_result + '";';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+
+ const e = document.createElement('script');
+ e.setAttribute('src', url);
+ e.onload = t.step_func_done(() => {
+ assert_equals(window.script_test_result, run_result);
+ });
+
+ document.body.appendChild(e);
+ URL.revokeObjectURL(url);
+}, 'Fetching a blob URL immediately before revoking it works in <script> tags.');
+
+async_test(t => {
+ const channel_name = 'a-click-test';
+ const blob = new Blob([window_contents_for_channel(channel_name)], {type: 'text/html'});
+ receive_message_on_channel(t, channel_name).then(t.step_func_done(t => {
+ assert_equals(t, 'foobar');
+ }));
+ const url = URL.createObjectURL(blob);
+ const anchor = document.createElement('a');
+ anchor.href = url;
+ anchor.target = '_blank';
+ document.body.appendChild(anchor);
+ anchor.click();
+ URL.revokeObjectURL(url);
+}, 'Opening a blob URL in a new window by clicking an <a> tag works immediately before revoking the URL.');
diff --git a/testing/web-platform/tests/FileAPI/url/url-in-tags.window.js b/testing/web-platform/tests/FileAPI/url/url-in-tags.window.js
new file mode 100644
index 0000000000..f20b359901
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-in-tags.window.js
@@ -0,0 +1,48 @@
+async_test(t => {
+ const run_result = 'test_script_OK';
+ const blob_contents = 'window.test_result = "' + run_result + '";';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+
+ const e = document.createElement('script');
+ e.setAttribute('src', url);
+ e.onload = t.step_func_done(() => {
+ assert_equals(window.test_result, run_result);
+ });
+
+ document.body.appendChild(e);
+}, 'Blob URLs can be used in <script> tags');
+
+async_test(t => {
+ const run_result = 'test_frame_OK';
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<script>window.test_result = "' + run_result + '";</script>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+
+ const frame = document.createElement('iframe');
+ frame.setAttribute('src', url);
+ frame.setAttribute('style', 'display:none;');
+ document.body.appendChild(frame);
+
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.test_result, run_result);
+ });
+}, 'Blob URLs can be used in iframes, and are treated same origin');
+
+async_test(t => {
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<style>body { margin: 0; } .block { height: 5000px; }</style>\n' +
+ '<body>\n' +
+ '<a id="block1"></a><div class="block"></div>\n' +
+ '<a id="block2"></a><div class="block"></div>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+
+ const frame = document.createElement('iframe');
+ frame.setAttribute('src', url + '#block2');
+ document.body.appendChild(frame);
+ frame.contentWindow.onscroll = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.scrollY, 5000);
+ });
+}, 'Blob URL fragment is implemented.');
diff --git a/testing/web-platform/tests/FileAPI/url/url-lifetime.html b/testing/web-platform/tests/FileAPI/url/url-lifetime.html
new file mode 100644
index 0000000000..ad5d667193
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-lifetime.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const worker = new Worker('resources/create-helper.js');
+ let url;
+ return new Promise(resolve => {
+ worker.onmessage = e => resolve(e.data);
+ worker.postMessage({blob: blob});
+ }).then(data => {
+ url = data.url;
+ let result = fetch(url);
+ worker.terminate();
+ return result;
+ }).then(response => response.text()).then(text => {
+ assert_equals(text, blob_contents);
+ return new Promise(resolve => t.step_timeout(resolve, 100));
+ }).then(() => promise_rejects_js(t, TypeError, fetch(url)));
+}, 'Terminating worker revokes its URLs');
+
+promise_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const frame = document.createElement('iframe');
+ frame.setAttribute('style', 'display:none;');
+ frame.src = 'resources/create-helper.html';
+ document.body.appendChild(frame);
+
+ let url;
+ return new Promise(resolve => {
+ frame.onload = t.step_func(e => {
+ resolve(e);
+ });
+ }).then(e => {
+ frame.contentWindow.postMessage({blob: blob}, '*');
+ return new Promise(resolve => {
+ self.addEventListener('message', t.step_func(e => {
+ if (e.source === frame.contentWindow) resolve(e);
+ }));
+ });
+ }).then(e => {
+ url = e.data.url;
+ let fetch_result = fetch(url);
+ document.body.removeChild(frame);
+ return fetch_result;
+ }).then(response => response.text()).then(text => {
+ assert_equals(text, blob_contents);
+ return new Promise(resolve => t.step_timeout(resolve, 100));
+ }).then(() => promise_rejects_js(t, TypeError, fetch(url)));
+}, 'Removing an iframe revokes its URLs');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/FileAPI/url/url-reload.window.js b/testing/web-platform/tests/FileAPI/url/url-reload.window.js
new file mode 100644
index 0000000000..d333b3a74a
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-reload.window.js
@@ -0,0 +1,36 @@
+function blob_url_reload_test(t, revoke_before_reload) {
+ const run_result = 'test_frame_OK';
+ const blob_contents = '<!doctype html>\n<meta charset="utf-8">\n' +
+ '<script>window.test_result = "' + run_result + '";</script>';
+ const blob = new Blob([blob_contents], {type: 'text/html'});
+ const url = URL.createObjectURL(blob);
+
+ const frame = document.createElement('iframe');
+ frame.setAttribute('src', url);
+ frame.setAttribute('style', 'display:none;');
+ document.body.appendChild(frame);
+
+ frame.onload = t.step_func(() => {
+ if (revoke_before_reload)
+ URL.revokeObjectURL(url);
+ assert_equals(frame.contentWindow.test_result, run_result);
+ frame.contentWindow.test_result = null;
+ frame.onload = t.step_func_done(() => {
+ assert_equals(frame.contentWindow.test_result, run_result);
+ });
+ // Slight delay before reloading to ensure revoke actually has had a chance
+ // to be processed.
+ t.step_timeout(() => {
+ frame.contentWindow.location.reload();
+ }, 250);
+ });
+}
+
+async_test(t => {
+ blob_url_reload_test(t, false);
+}, 'Reloading a blob URL succeeds.');
+
+
+async_test(t => {
+ blob_url_reload_test(t, true);
+}, 'Reloading a blob URL succeeds even if the URL was revoked.');
diff --git a/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js b/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js
new file mode 100644
index 0000000000..54e6a3da5a
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-with-fetch.any.js
@@ -0,0 +1,72 @@
+// META: script=resources/fetch-tests.js
+// META: script=/common/gc.js
+
+function fetch_should_succeed(test, request) {
+ return fetch(request).then(response => response.text());
+}
+
+function fetch_should_fail(test, url, method = 'GET') {
+ return promise_rejects_js(test, TypeError, fetch(url, {method: method}));
+}
+
+fetch_tests('fetch', fetch_should_succeed, fetch_should_fail);
+
+promise_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob_type = 'image/png';
+ const blob = new Blob([blob_contents], {type: blob_type});
+ const url = URL.createObjectURL(blob);
+
+ return fetch(url).then(response => {
+ assert_equals(response.headers.get('Content-Type'), blob_type);
+ });
+}, 'fetch should return Content-Type from Blob');
+
+promise_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ const request = new Request(url);
+
+ // Revoke the object URL. Request should take a reference to the blob as
+ // soon as it receives it in open(), so the request succeeds even though we
+ // revoke the URL before calling fetch().
+ URL.revokeObjectURL(url);
+
+ return fetch_should_succeed(t, request).then(text => {
+ assert_equals(text, blob_contents);
+ });
+}, 'Revoke blob URL after creating Request, will fetch');
+
+promise_test(async t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ let request = new Request(url);
+
+ // Revoke the object URL. Request should take a reference to the blob as
+ // soon as it receives it in open(), so the request succeeds even though we
+ // revoke the URL before calling fetch().
+ URL.revokeObjectURL(url);
+
+ request = request.clone();
+ await garbageCollect();
+
+ const text = await fetch_should_succeed(t, request);
+ assert_equals(text, blob_contents);
+}, 'Revoke blob URL after creating Request, then clone Request, will fetch');
+
+promise_test(function(t) {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+
+ const result = fetch_should_succeed(t, url).then(text => {
+ assert_equals(text, blob_contents);
+ });
+
+ // Revoke the object URL. fetch should have already resolved the blob URL.
+ URL.revokeObjectURL(url);
+
+ return result;
+}, 'Revoke blob URL after calling fetch, fetch should succeed');
diff --git a/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js b/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js
new file mode 100644
index 0000000000..29d83080ab
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url-with-xhr.any.js
@@ -0,0 +1,68 @@
+// META: script=resources/fetch-tests.js
+
+function xhr_should_succeed(test, url) {
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.onload = test.step_func(() => {
+ assert_equals(xhr.status, 200);
+ assert_equals(xhr.statusText, 'OK');
+ resolve(xhr.response);
+ });
+ xhr.onerror = () => reject('Got unexpected error event');
+ xhr.send();
+ });
+}
+
+function xhr_should_fail(test, url, method = 'GET') {
+ const xhr = new XMLHttpRequest();
+ xhr.open(method, url);
+ const result1 = new Promise((resolve, reject) => {
+ xhr.onload = () => reject('Got unexpected load event');
+ xhr.onerror = resolve;
+ });
+ const result2 = new Promise(resolve => {
+ xhr.onreadystatechange = test.step_func(() => {
+ if (xhr.readyState !== xhr.DONE) return;
+ assert_equals(xhr.status, 0);
+ resolve();
+ });
+ });
+ xhr.send();
+ return Promise.all([result1, result2]);
+}
+
+fetch_tests('XHR', xhr_should_succeed, xhr_should_fail);
+
+async_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob_type = 'image/png';
+ const blob = new Blob([blob_contents], {type: blob_type});
+ const url = URL.createObjectURL(blob);
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.onloadend = t.step_func_done(() => {
+ assert_equals(xhr.getResponseHeader('Content-Type'), blob_type);
+ });
+ xhr.send();
+}, 'XHR should return Content-Type from Blob');
+
+async_test(t => {
+ const blob_contents = 'test blob contents';
+ const blob = new Blob([blob_contents]);
+ const url = URL.createObjectURL(blob);
+ const xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+
+ // Revoke the object URL. XHR should take a reference to the blob as soon as
+ // it receives it in open(), so the request succeeds even though we revoke the
+ // URL before calling send().
+ URL.revokeObjectURL(url);
+
+ xhr.onload = t.step_func_done(() => {
+ assert_equals(xhr.response, blob_contents);
+ });
+ xhr.onerror = t.unreached_func('Got unexpected error event');
+
+ xhr.send();
+}, 'Revoke blob URL after open(), will fetch');
diff --git a/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html
new file mode 100644
index 0000000000..7ae32512e0
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file-manual.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FileAPI Test: Creating Blob URL with File</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div>
+ <p>Test steps:</p>
+ <ol>
+ <li>Download <a href="/images/blue96x96.png">blue96x96.png</a> to local.</li>
+ <li>Select the local file (blue96x96.png) to run the test.</li>
+ </ol>
+</div>
+
+<form name="uploadData">
+ <input type="file" id="fileChooser">
+</form>
+
+<div id="log"></div>
+
+<script>
+ async_test(function(t) {
+ var fileInput = document.querySelector('#fileChooser');
+
+ fileInput.onchange = t.step_func(function(e) {
+ var blobURL, file = fileInput.files[0];
+
+ test(function() {
+ assert_true(file instanceof File, "FileList contains File");
+ }, "Check if FileList contains File");
+
+ test(function() {
+ blobURL = window.URL.createObjectURL(file);
+ assert_equals(typeof blobURL, "string", "Blob URL is type of string");
+ assert_equals(blobURL.indexOf("blob"), 0, "Blob URL's scheme is blob");
+ }, "Check if URL.createObjectURL(File) returns a Blob URL");
+
+ t.done();
+ });
+ });
+</script>
+
diff --git a/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html
new file mode 100644
index 0000000000..534c1de996
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url_createobjecturl_file_img-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FileAPI Test: Creating Blob URL with File as image source</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
+
+<div>
+ <p>Test steps:</p>
+ <ol>
+ <li>Download <a href="/images/blue96x96.png">blue96x96.png</a> to local.</li>
+ <li>Select the local file (blue96x96.png) to run the test.</li>
+ </ol>
+ <p>Pass/fail criteria:</p>
+ <p>Test passes if there is a filled blue square.</p>
+
+ <p><input type="file" accept="image/*" id="fileChooser"></p>
+ <p><img id="displayImage"></img></p>
+</div>
+
+<script>
+ var fileInput = document.querySelector("#fileChooser");
+ var img = document.querySelector("#displayImage");
+
+ fileInput.addEventListener("change", function(evt) {
+ img.src = window.URL.createObjectURL(fileInput.files[0]);
+ }, false);
+</script>
+
diff --git a/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html
new file mode 100644
index 0000000000..7d7390442d
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>FileAPI Reference File</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
+
+<p>Test passes if there is a filled blue square.</p>
+
+<p>
+ <img id="fileDisplay" src="/images/blue96x96.png">
+</p>
+
diff --git a/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html
new file mode 100644
index 0000000000..468dcb086d
--- /dev/null
+++ b/testing/web-platform/tests/FileAPI/url/url_xmlhttprequest_img.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>FileAPI Test: Creating Blob URL via XMLHttpRequest as image source</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="author" title="JunChen Xia" href="mailto:xjconlyme@gmail.com">
+<link rel="match" href="url_xmlhttprequest_img-ref.html">
+
+<p>Test passes if there is a filled blue square.</p>
+
+<p>
+ <img id="fileDisplay">
+</p>
+
+<script src="/common/reftest-wait.js"></script>
+<script>
+ var http = new XMLHttpRequest();
+ http.open("GET", "/images/blue96x96.png", true);
+ http.responseType = "blob";
+ http.onloadend = function() {
+ var fileDisplay = document.querySelector("#fileDisplay");
+ fileDisplay.src = window.URL.createObjectURL(http.response);
+ fileDisplay.onload = takeScreenshot;
+ };
+ http.send();
+</script>
+</html>