diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/FileAPI/url | |
parent | Initial commit. (diff) | |
download | firefox-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')
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> |