diff options
Diffstat (limited to 'testing/web-platform/tests/web-share')
34 files changed, 1218 insertions, 0 deletions
diff --git a/testing/web-platform/tests/web-share/META.yml b/testing/web-platform/tests/web-share/META.yml new file mode 100644 index 0000000000..d2eccca6f4 --- /dev/null +++ b/testing/web-platform/tests/web-share/META.yml @@ -0,0 +1,5 @@ +spec: https://w3c.github.io/web-share/ +suggested_reviewers: + - ewilligers + - mgiuca + - marcoscaceres diff --git a/testing/web-platform/tests/web-share/canShare-files.https.html b/testing/web-platform/tests/web-share/canShare-files.https.html new file mode 100644 index 0000000000..2c2f2ff969 --- /dev/null +++ b/testing/web-platform/tests/web-share/canShare-files.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: canShare with files</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + 'use strict'; + const textFile = new File(['hello'], 'hello.txt', {type:'text/plain'}); + const emptyFile = new File([''], 'empty'); + + test(() => { + assert_throws_js(TypeError, () => { navigator.canShare({files: textFile}) }); + }, 'canShare with single file'); + + test(() => { + assert_equals(navigator.canShare({files: []}), false); + }, 'canShare with empty file list'); + + test(() => { + assert_equals(navigator.canShare({files: [emptyFile]}), true); + }, 'canShare with single file list'); + + test(() => { + assert_equals(navigator.canShare({files: [textFile, emptyFile]}), true); + }, 'canShare with file list'); + + test(() => { + assert_equals(navigator.canShare({files: [textFile, emptyFile, textFile]}), true); + }, 'canShare with repeated file'); + + test(() => { + assert_equals(navigator.canShare({url: 'https://example.com/', files: [textFile, emptyFile]}), true); + }, 'canShare with file list and url'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/canShare-insecure.http.html b/testing/web-platform/tests/web-share/canShare-insecure.http.html new file mode 100644 index 0000000000..ce81c7c0c8 --- /dev/null +++ b/testing/web-platform/tests/web-share/canShare-insecure.http.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: canShare from non-secure context</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + assert_false('canShare' in navigator, 'navigator has attribute \'canShare\'.'); + }, 'navigator.canShare must be undefined in non-secure context'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/canShare.https.html b/testing/web-platform/tests/web-share/canShare.https.html new file mode 100644 index 0000000000..f0aed9a244 --- /dev/null +++ b/testing/web-platform/tests/web-share/canShare.https.html @@ -0,0 +1,159 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: canShare method tests</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + assert_false( + navigator.canShare(), + "no arguments (uses default argument value, which is empty dictionary)" + ); + + assert_false(navigator.canShare({}), "empty dictionary not allowed"); + + assert_false(navigator.canShare(undefined), "empty dictionary not allowed"); + + assert_false(navigator.canShare(null), "empty dictionary not allowed"); + + assert_false( + navigator.canShare({ unused: "unexpected field" }), + "results in empty dictionary, which is not allowed" + ); + }, "canShare() empty and default dictionary"); + + test(() => { + assert_true(navigator.canShare({ url: "http://a.b" }), "http URL is ok"); + + assert_true(navigator.canShare({ url: "https://a.b" }), "https URL is ok"); + + assert_false( + navigator.canShare({ url: "http://a.b:65536" }), + "URL is invalid" + ); + + assert_false( + navigator.canShare({ url: "data:the url" }), + "data URL is not allowed" + ); + + assert_false( + navigator.canShare({ url: "file:///usr/" }), + "file URL is not allowed" + ); + + assert_true( + navigator.canShare({ + url: "https://a.b/path?query#fragment", + }), + "canShare with URL" + ); + + assert_true( + navigator.canShare({ + url: { + toString() { + return "https://a.b/"; + }, + }, + }), + "canShare URL as with object with stringifier" + ); + + assert_true( + navigator.canShare( + { url: "" }, + "canShare with empty URL, which resolves as the doc's base URL" + ) + ); + + assert_true( + navigator.canShare({ + url: "//a.b/path?query#fragment", + }), + "canShare with URL having no scheme" + ); + + assert_true( + navigator.canShare({ + url: "relative", + }), + "canShare relative URL, resolved against API base URL" + ); + }, "canShare() url member"); + + test(() => { + assert_false( + navigator.canShare({ title: undefined }), + "canShare with attribute undefined is equivalent to omitting the attribute" + ); + + assert_true(navigator.canShare({ title: "subject" }), "canShare with title"); + + assert_true(navigator.canShare({ title: null }), "stringified null"); + }, "canShare() title member"); + + test(() => { + assert_true(navigator.canShare({ text: "" }), "ok to share empty text"); + + assert_true( + navigator.canShare({ text: "some text 🤔" }), + "ok to share unicode" + ); + + assert_true(navigator.canShare({ text: 123 }), "number is stringified"); + }, "canShare() text member"); + + test(() => { + const file = new File(["hello"], "file", { type: "text/plain" }); + const file2 = new File([], "file2"); + + assert_false(navigator.canShare({ files: [] }), "empty list is not allowed"); + + assert_false( + navigator.canShare({ + url: "https://a.b:800000", + files: [file, file2], + }), + "invalid URL invalidates the share" + ); + + assert_true( + navigator.canShare({ files: [file] }), + "single file is ok to share" + ); + + assert_true( + navigator.canShare({ files: [file, file2, file] }), + "repeated files is ok to share" + ); + + assert_true( + navigator.canShare({ + files: [file, file2], + text: "some texts", + url: "https://example.com/", + }), + "is ok to share files, text, and url together" + ); + }, "canShare() files member"); + + test(() => { + assert_true( + navigator.canShare({ + title: "subject", + text: "body", + url: "https://a.b/", + files: [new File([], "file")], + unused: "unexpected field", + }), + "canShare with unexpected field" + ); + }, "canShare() multiple members"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html b/testing/web-platform/tests/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html new file mode 100644 index 0000000000..773da84c0c --- /dev/null +++ b/testing/web-platform/tests/web-share/disabled-by-permissions-policy-cross-origin.https.sub.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: is disabled by permissions policy cross-origin</title> + <link + rel="help" + href="https://w3c.github.io/web-share/#permissions-policy" + /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body></body> + <script> + const crossOrigin = "https://{{hosts[alt][]}}:{{ports[https][0]}}"; + const sameOriginPath = "/web-share/resources/post-message.html"; + const crossOriginSrc = `${crossOrigin}${sameOriginPath}`; + const shareData = { + title: "WebShare Test", + text: "This is a test of the Web Share API", + url: "https://example.com/", + }; + + function waitForMessage(message) { + return new Promise((resolve) => { + window.addEventListener("message", function listener(event) { + if (event.data.action !== message) return; + window.removeEventListener("message", listener); + resolve(event.data); + }); + }); + } + + async function loadIframe(t, src, allowList) { + const iframe = document.createElement("iframe"); + if (allowList !== undefined) iframe.allow = allowList; + t.add_cleanup(() => { + iframe.remove(); + }); + await new Promise((resolve) => { + iframe.src = src; + document.body.appendChild(iframe); + iframe.onload = resolve; + }); + await waitForMessage("loaded"); + return iframe; + } + + promise_test(async (t) => { + assert_true("share" in navigator, "navigator.share is exposed"); + const iframe = await loadIframe(t, crossOriginSrc); + const iframeWindow = iframe.contentWindow; + iframeWindow.postMessage({ action: "share", data: shareData }, "*"); + const data = await waitForMessage("share"); + assert_equals(data.result, "error"); + assert_equals(data.error, "NotAllowedError"); + }, "share() is disabled by default 'self' by permissions policy for cross-origin iframes"); + + promise_test(async (t) => { + assert_true("share" in navigator, "navigator.share is exposed"); + const iframe = await loadIframe(t, crossOriginSrc, "web-share 'none'"); + const iframeWindow = iframe.contentWindow; + iframeWindow.postMessage({ action: "share", data: shareData }, "*"); + const data = await waitForMessage("share"); + assert_equals(data.result, "error"); + assert_equals(data.error, "NotAllowedError"); + }, "share() is disabled explicitly by permissions policy for cross-origin iframe"); + + promise_test(async (t) => { + assert_true("share" in navigator, "navigator.share is exposed"); + const iframe = await loadIframe(t, crossOriginSrc, "web-share 'self'"); + const iframeWindow = iframe.contentWindow; + iframeWindow.postMessage({ action: "share", data: shareData }, "*"); + const data = await waitForMessage("share"); + assert_equals(data.result, "error"); + assert_equals(data.error, "NotAllowedError"); + }, "share() not allowed, as only allowed to share with self"); + + promise_test(async (t) => { + assert_true("canShare" in navigator, "navigator.canShare is exposed"); + const iframe = await loadIframe(t, crossOriginSrc); + const iframeWindow = iframe.contentWindow; + iframeWindow.postMessage({ action: "canShare", data: shareData }, "*"); + const data = await waitForMessage("canShare"); + assert_equals(data.result, false, "Expected false, as it can't share."); + }, "canShare() not allowed to share by default permissions policy cross-origin"); + + promise_test(async (t) => { + assert_true("canShare" in navigator, "navigator.canShare is exposed"); + const iframe = await loadIframe( + t, + crossOriginSrc, + `web-share ${crossOrigin}` + ); + iframe.contentWindow.postMessage( + { action: "canShare", data: shareData }, + "*" + ); + const data = await waitForMessage("canShare"); + assert_equals( + data.result, + true, + `Expected true, is it can now share on ${origin}.` + ); + }, "canShare() is allowed by permissions policy to share cross-origin on a particular origin"); + + promise_test(async (t) => { + assert_true("canShare" in navigator, "navigator.canShare is exposed"); + const iframe = await loadIframe(t, sameOriginPath, "web-share 'self'"); + iframe.contentWindow.postMessage( + { action: "canShare", data: shareData }, + "*" + ); + const data = await waitForMessage("canShare"); + assert_equals( + data.result, + true, + "Expected true, at it can share with self." + ); + }, "canShare() with self"); + </script> +</html> diff --git a/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html b/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html new file mode 100644 index 0000000000..e9ea903965 --- /dev/null +++ b/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: Can be disabled by permissions policy</title> + <link rel="help" href="https://w3c.github.io/web-share/#permissions-policy"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> + promise_test(async (t) => { + await test_driver.bless("web share"); + await promise_rejects_dom(t, "NotAllowedError", navigator.share({})); + }, "share() can be disabled by permissions policy"); + + test((t) => { + assert_false( + navigator.canShare({ text: "foo" }), + "not allowed to share" + ); + }, "canShare() can be disabled by permissions policy"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html.headers b/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html.headers new file mode 100644 index 0000000000..83f5a343fa --- /dev/null +++ b/testing/web-platform/tests/web-share/disabled-by-permissions-policy.https.sub.html.headers @@ -0,0 +1 @@ +Permissions-Policy: web-share=() diff --git a/testing/web-platform/tests/web-share/feature-policy-listed.tentative.html b/testing/web-platform/tests/web-share/feature-policy-listed.tentative.html new file mode 100644 index 0000000000..8f709e68e7 --- /dev/null +++ b/testing/web-platform/tests/web-share/feature-policy-listed.tentative.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: policy is listed</title> + <link rel="help" href="https://w3c.github.io/web-share/#permissions-policy"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + 'use strict'; + + test(() => { + assert_true("permissionsPolicy" in document, "permissionsPolicy is implemented"); + assert_true(document.permissionsPolicy.allowsFeature('web-share')); + }, 'allowsFeature() returns true for web-share'); + + test(() => { + assert_true("permissionsPolicy" in document, "permissionsPolicy is implemented"); + assert_true(document.permissionsPolicy.features().includes('web-share')); + }, 'features() includes web-share'); + + test(() => { + assert_true("permissionsPolicy" in document, "permissionsPolicy is implemented"); + assert_true(document.permissionsPolicy.allowedFeatures().includes('web-share')); + }, 'allowedFeatures() includes web-share'); + + test(() => { + assert_true("permissionsPolicy" in document, "permissionsPolicy is implemented"); + assert_equals(Array.from(document.permissionsPolicy.getAllowlistForFeature('web-share')).length, 1); + }, 'allowsFeature() returns a single entry for web-share'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/idlharness.https.window.js b/testing/web-platform/tests/web-share/idlharness.https.window.js new file mode 100644 index 0000000000..fbe59864af --- /dev/null +++ b/testing/web-platform/tests/web-share/idlharness.https.window.js @@ -0,0 +1,17 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +// https://w3c.github.io/web-share/ + +'use strict'; + +idl_test( + ['web-share'], + ['html'], + idl_array => { + idl_array.add_objects({ + Navigator: ['navigator'] + }); + } +); diff --git a/testing/web-platform/tests/web-share/resources/blank.html b/testing/web-platform/tests/web-share/resources/blank.html new file mode 100644 index 0000000000..84f43b333a --- /dev/null +++ b/testing/web-platform/tests/web-share/resources/blank.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<meta charset="utf-8"> diff --git a/testing/web-platform/tests/web-share/resources/manual-helper.js b/testing/web-platform/tests/web-share/resources/manual-helper.js new file mode 100644 index 0000000000..15bb17936e --- /dev/null +++ b/testing/web-platform/tests/web-share/resources/manual-helper.js @@ -0,0 +1,100 @@ +// Internal function. Returns [instruction, list] DOM elements. +function setupManualShareTestCommon() { + const div = document.createElement('div'); + document.body.appendChild(div); + + const instruction = document.createElement('div'); + instruction.id = 'instruction'; + div.appendChild(instruction); + + const shareButton = document.createElement('input'); + shareButton.id = 'share_button'; + shareButton.value = 'Share button'; + shareButton.type = 'button'; + div.appendChild(shareButton); + + let heading = document.createElement('h2'); + heading.innerText = 'Instructions:'; + instruction.appendChild(heading); + let list = document.createElement('ol'); + instruction.appendChild(list); + let item = document.createElement('li'); + list.appendChild(item); + item.innerText = 'Click the Share button.'; + + return [instruction, list]; +} + +// Sets up the page for running manual tests. Automatically creates the +// instructions (based on the parameters) and the share button. +function setupManualShareTest(expected_share_data) { + const {title, text, url, files} = expected_share_data; + let [instruction, list] = setupManualShareTestCommon(); + let item = document.createElement('li'); + list.appendChild(item); + item.innerText = 'Choose a valid share target.'; + + heading = document.createElement('h2'); + heading.innerText = 'Pass the test iff the target app received:'; + instruction.appendChild(heading); + + list = document.createElement('ul'); + instruction.appendChild(list); + + item = document.createElement('li'); + list.appendChild(item); + item.innerText = `title = "${title}"`; + item = document.createElement('li'); + list.appendChild(item); + item.innerText = `text = "${text}"`; + item = document.createElement('li'); + list.appendChild(item); + item.innerText = `url = "${url}"`; + if (files) { + item = document.createElement('li'); + list.appendChild(item); + item.innerText = `files = ${files.length} file(s)`; + for (let file of files) { + const div = document.createElement('div'); + if (file.type.startsWith('text/')) { + const reader = new FileReader(); + reader.onload = () => { + div.textContent = reader.result; + }; + reader.readAsText(file); + } else if (file.type.startsWith('image/')) { + const image = document.createElement('img'); + image.src = URL.createObjectURL(file); + image.alt = file.name; + div.appendChild(image); + } + item.appendChild(div); + } + } +} + +function setupManualShareTestRequiringCancellation() { + const [instruction, list] = setupManualShareTestCommon(); + const item = document.createElement('li'); + list.appendChild(item); + item.innerText = 'Cancel the share dialog.'; +} + +// Returns a promise. When the user clicks the button, calls +// |click_handler| and resolves the promise with the result. +function callWhenButtonClicked(click_handler) { + return new Promise((resolve, reject) => { + document.querySelector('#share_button').onclick = () => { + try { + const result = click_handler(); + resolve(result); + } catch (e) { + reject(e); + } + }; + }); +} + +function getAbsoluteUrl(url) { + return new URL(url, document.baseURI).toString(); +} diff --git a/testing/web-platform/tests/web-share/resources/post-message.html b/testing/web-platform/tests/web-share/resources/post-message.html new file mode 100644 index 0000000000..30134e2901 --- /dev/null +++ b/testing/web-platform/tests/web-share/resources/post-message.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script> + window.addEventListener("message", async (event) => { + const { + data: { action, data }, + } = event; + const response = {}; + try { + switch (action) { + case "share": + await test_driver.bless("share"); + await navigator.share(data); + response.result = "unreached"; + break; + case "canShare": + response.result = navigator.canShare(data); + break; + } + } catch (error) { + response.result = "error"; + response.error = error.name; + } finally { + event.source.postMessage({ ...response, action }, "*"); + } + }); + + // send message to parent that we loaded + function sendLoadMessage() { + window.parent.postMessage({ action: "loaded" }, "*"); + } +</script> +<body onload="sendLoadMessage()"></body> diff --git a/testing/web-platform/tests/web-share/share-cancel-manual.https.html b/testing/web-platform/tests/web-share/share-cancel-manual.https.html new file mode 100644 index 0000000000..e27d1975be --- /dev/null +++ b/testing/web-platform/tests/web-share/share-cancel-manual.https.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share cancelled by user</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + setupManualShareTestRequiringCancellation(); + + promise_test(t => { + return callWhenButtonClicked(() => promise_rejects_dom( + t, 'AbortError', + navigator.share({title: 'the title', text: 'the message', + url: 'https://example.com'}))); + }, 'share with user cancellation'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-consume-activation.https.html b/testing/web-platform/tests/web-share/share-consume-activation.https.html new file mode 100644 index 0000000000..301469f83d --- /dev/null +++ b/testing/web-platform/tests/web-share/share-consume-activation.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: consume user activation</title> + <link rel="help" href="https://w3c.github.io/web-share/#share-method"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> + promise_test(async t => { + // Not activated by user gesture, so not allowed! + await promise_rejects_dom(t, "NotAllowedError", navigator.share()); + + await test_driver.bless("web share"); + + // We have a gesture, but the URL is invalid - so TypeError! + await promise_rejects_js( + t, + TypeError, + navigator.share({ url: "http://example.com:65536" }) + ); + + // The activation has been consumed, so calling share() again would require + // a new gesture. + await promise_rejects_dom(t, "NotAllowedError", navigator.share()); + }, "Calling share consumes user activation"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-empty.https.html b/testing/web-platform/tests/web-share/share-empty.https.html new file mode 100644 index 0000000000..0bca2f2960 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-empty.https.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: Share no known fields</title> + <link rel="help" href="https://w3c.github.io/web-share/#share-method"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> + promise_test(async t => { + await test_driver.bless("web share", () => { + return promise_rejects_js(t, TypeError, navigator.share()); + }); + }, "share with no arguments (same as empty dictionary)"); + + promise_test(async t => { + await test_driver.bless("web share", () => { + return promise_rejects_js(t, TypeError, navigator.share({})); + }); + }, "share with an empty dictionary"); + + promise_test(async t => { + await test_driver.bless("web share", () => { + return promise_rejects_js(t, TypeError, navigator.share(undefined)); + }); + }, "share with a undefined argument (same as empty dictionary)"); + + promise_test(async t => { + await test_driver.bless("web share", () => { + return promise_rejects_js(t, TypeError, navigator.share(null)); + }); + }, "share with a null argument (same as empty dictionary)"); + + promise_test(async t => { + await test_driver.bless("web share", () => { + return promise_rejects_js( + t, + TypeError, + navigator.share({ unused: "unexpected field" }) + ); + }); + }, "share with a dictionary containing only surplus fields"); + + promise_test(async t => { + await test_driver.bless("web share"); + await promise_rejects_js(t, TypeError, navigator.share({ files: [] })); + }, "share with an empty files member") + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-extra-argument-manual.https.html b/testing/web-platform/tests/web-share/share-extra-argument-manual.https.html new file mode 100644 index 0000000000..47ef7fa115 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-extra-argument-manual.https.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Surplus arguments ignored</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + setupManualShareTest( + {title: 'the title', text: 'the message', url: 'https://example.com'}); + callWhenButtonClicked(() => navigator.share( + {title: 'the title', text: 'the message', url: 'https://example.com'}, + 'more than required')); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-extra-field-manual.https.html b/testing/web-platform/tests/web-share/share-extra-field-manual.https.html new file mode 100644 index 0000000000..8b479bb02b --- /dev/null +++ b/testing/web-platform/tests/web-share/share-extra-field-manual.https.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Surplus fields ignored</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + setupManualShareTest( + {title: 'the title', text: 'the message', url: 'https://example.com'}); + callWhenButtonClicked(() => navigator.share( + {title: 'the title', text: 'the message', url: 'https://example.com', + unused: 'unexpected field'})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-files-manual.https.html b/testing/web-platform/tests/web-share/share-files-manual.https.html new file mode 100644 index 0000000000..c3941bb406 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-files-manual.https.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share multiple files</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const options = {type: 'text/plain'}; + const first = new File(['one'], 'first.txt', options); + const second = new File(['two'], 'second.txt', options); + const third = new File(['three'], 'third.txt', options); + const fourth = new File(['four'], 'fourth.txt', options); + const fifth = new File(['five'], 'fifth.txt', options); + const data = { + title: 'Counting to 5', + text: 'Here are the numbers', + url: 'https://example.com/', + files: [first, second, third, fourth, fifth] + }; + setupManualShareTest(data); + callWhenButtonClicked(() => navigator.share(data)); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-image-manual.tentative.https.html b/testing/web-platform/tests/web-share/share-image-manual.tentative.https.html new file mode 100644 index 0000000000..48ea4c378c --- /dev/null +++ b/testing/web-platform/tests/web-share/share-image-manual.tentative.https.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share single image file</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const fileBits = [ + '<svg xmlns="http://www.w3.org/2000/svg" width="80px" height="80px">', + '<circle cx="40" cy="40" r="28" fill="lime" />', + '</svg>' + ]; + const fileName = 'circle.svg'; + const options = {type: 'image/svg+xml'}; + const file = new File(fileBits, fileName, options); + setupManualShareTest({title: '', text: '', url: '', files: [file]}); + callWhenButtonClicked(() => navigator.share({files: [file]})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-non-string-manual.https.html b/testing/web-platform/tests/web-share/share-non-string-manual.https.html new file mode 100644 index 0000000000..b70f8fc23d --- /dev/null +++ b/testing/web-platform/tests/web-share/share-non-string-manual.https.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share non-string types (test implicit conversion)</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + // Expect that each of the non-string values is converted into a string. + setupManualShareTest( + {title: 'true', text: 'the object', url: getAbsoluteUrl('384957')}); + + const objectWithToString = {toString() { return 'the object'; }}; + callWhenButtonClicked(() => navigator.share( + {title: true, text: objectWithToString, url: 384957})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-null-manual.https.html b/testing/web-platform/tests/web-share/share-null-manual.https.html new file mode 100644 index 0000000000..4c74225cbe --- /dev/null +++ b/testing/web-platform/tests/web-share/share-null-manual.https.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share null and undefined values</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + // Expect null to be converted into the string 'null'. On the other + // hand, undefined should be equivalent to omitting the attribute. + setupManualShareTest( + {title: 'null', text: '', url: getAbsoluteUrl('null')}); + callWhenButtonClicked(() => navigator.share( + {title: null, text: undefined, url: null})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-securecontext.http.html b/testing/web-platform/tests/web-share/share-securecontext.http.html new file mode 100644 index 0000000000..6344330c6b --- /dev/null +++ b/testing/web-platform/tests/web-share/share-securecontext.http.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share from non-secure context</title> + <link rel="help" href="https://w3c.github.io/web-share/#extensions-to-the-navigator-interface"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + test(() => { + assert_false('share' in navigator, 'navigator has attribute \'share\'.'); + }, 'navigator.share must be undefined in non-secure context'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-sharePromise-internal-slot.https.html b/testing/web-platform/tests/web-share/share-sharePromise-internal-slot.https.html new file mode 100644 index 0000000000..eb8bf7e4c7 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-sharePromise-internal-slot.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <link rel="help" href="https://github.com/w3c/web-share/pull/113" /> + <title>WebShare Test: only one share at a time</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <button> + <script> + setup({ allow_uncaught_exception:true }); + promise_test(async t => { + const button = document.querySelector("button"); + const p = new Promise(r => { + button.onclick = () => { + const promises = []; + promises.push( + navigator.share({ title: "should be pending" }), + navigator.share({ title: "should reject" }), + navigator.share({ title: "should also reject" }) + ); + r(promises); + }; + }); + test_driver.click(button); + const [, promise2, promise3] = await p; + await Promise.all([ + promise_rejects_dom(t, "InvalidStateError", promise2), + promise_rejects_dom(t, "InvalidStateError", promise3) + ]); + }, "Only allow one share call at a time, which is controlled by the [[sharePromise]] internal slot."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-simple-manual.https.html b/testing/web-platform/tests/web-share/share-simple-manual.https.html new file mode 100644 index 0000000000..d4a1f9b1c8 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-simple-manual.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Simple share</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const url = 'https://www.example.com/some/path?some_query#some_fragment'; + const data = {title: 'the title', text: 'the message', url, files: []}; + setupManualShareTest(data); + callWhenButtonClicked(() => navigator.share(data)).then( + result => { + // Check that promise resolved with undefined. This is guarded + // by an if statement because we do not want it to register as a + // test if it passes (since this is a manual test). + if (result !== undefined) { + assert_equals(result, undefined); + } + }); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-unicode-strings-manual.https.html b/testing/web-platform/tests/web-share/share-unicode-strings-manual.https.html new file mode 100644 index 0000000000..8cdaa87de7 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-unicode-strings-manual.https.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share with non-ASCII Unicode strings</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + // Title is a string with BMP and non-BMP characters. + // Text contains invalid surrogates which should be converted into U+FFFD. + // URL contains non-ASCII characters in host and path. + const title = 'fáncy 写作 😱'; + const url = 'https://测试.example.com/📄'; + // Host is IDNA-encoded. Path is percent-encoded. + const url_ascii = 'https://xn--0zwm56d.example.com/%F0%9F%93%84'; + setupManualShareTest({title, text: '\ufffdx', url: url_ascii}); + callWhenButtonClicked(() => navigator.share({title, text: '\ud9a3x', url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-unicode-strings-nonutf8-manual.https.html b/testing/web-platform/tests/web-share/share-unicode-strings-nonutf8-manual.https.html new file mode 100644 index 0000000000..deabec3def --- /dev/null +++ b/testing/web-platform/tests/web-share/share-unicode-strings-nonutf8-manual.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="windows-1252"> + <title>WebShare Test: Share with non-ASCII Unicode strings in a Latin-1-encoded page</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + assert_equals(document.characterSet, 'windows-1252'); + + // Exact same test as in share-unicode-strings-manual.html, with same + // expectations. This tests that the page's encoding (ISO-8859-1) is + // ignored and Unicode characters are always percent-encoded in UTF-8. + const title = 'f\xe1ncy \u5199\u4f5c \ud83d\ude31'; + const url = 'https://\u6d4b\u8bd5.example.com/\ud83d\udcc4'; + // Host is IDNA-encoded. Path is percent-encoded with UTF-8. + const url_ascii = 'https://xn--0zwm56d.example.com/%F0%9F%93%84'; + setupManualShareTest({title, text: '\ufffdx', url: url_ascii}); + callWhenButtonClicked(() => navigator.share({title, text: '\ud9a3x', url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-empty-manual.https.html b/testing/web-platform/tests/web-share/share-url-empty-manual.https.html new file mode 100644 index 0000000000..4038dab8cf --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-empty-manual.https.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share with empty URL</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + setupManualShareTest({title: '', text: '', url: document.baseURI}); + callWhenButtonClicked(() => navigator.share({url: ''})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-encoding-manual.https.html b/testing/web-platform/tests/web-share/share-url-encoding-manual.https.html new file mode 100644 index 0000000000..0f03b5a8b7 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-encoding-manual.https.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share URL with illegal characters (test percent encoding)</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const url = 'http://example.com/foo\\ab%63\r\n\t "<>`{}'; + // Expect '\' to normalize to '/', "%63" to normalize to 'c', '\r\n\t' + // to be removed, and all the other illegal characters to be percent-escaped. + const url_encoded = 'http://example.com/foo/abc%20%22%3C%3E%60%7B%7D'; + setupManualShareTest({title: '', text: '', url: url_encoded}); + callWhenButtonClicked(() => navigator.share({url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-invalid.https.html b/testing/web-platform/tests/web-share/share-url-invalid.https.html new file mode 100644 index 0000000000..cb76458c24 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-invalid.https.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: Share with an invalid URL</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "http://a.com:65536" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects when URL is invalid"); + + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "file:///etc/passwd" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects file:// URLs"); + + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "wss://a.com/" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects wss: URLs"); + + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "about:config" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects about: URLs"); + + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "chrome://about" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects chrome: URLs"); + + promise_test(async t => { + await test_driver.bless(); + const promise = navigator.share({ url: "javascript:window.alert('error')" }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects javascript: URLs"); + + promise_test(async t => { + await test_driver.bless(); + const file = new File([], "text/plain"); + const promise = navigator.share({ url: URL.createObjectURL(file) }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects blob: URLs"); + + promise_test(async t => { + const encoder = new TextEncoder(); + const encoded = encoder.encode( + `<meta http-equiv="refresh" content="1;url=http://example.com/">` + ); + const file = new File(encoded, "text/html"); + const url = URL.createObjectURL(file); + const reader = new FileReader(); + reader.readAsDataURL(file); + const dataURL = await new Promise(resolve => { + reader.addEventListener("load", () => { + resolve(reader.result); + }); + }); + await test_driver.bless(); + const promise = navigator.share({ url: dataURL }); + return promise_rejects_js(t, TypeError, promise); + }, "share() rejects data: URLs"); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-noscheme-manual.https.html b/testing/web-platform/tests/web-share/share-url-noscheme-manual.https.html new file mode 100644 index 0000000000..e9d7eefeb7 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-noscheme-manual.https.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share with URL without a scheme</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const url = '//www.example.com/some/path?some_query#some_fragment'; + setupManualShareTest({title: '', text: '', url: getAbsoluteUrl(url)}); + callWhenButtonClicked(() => navigator.share({url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-pathonly-manual.https.html b/testing/web-platform/tests/web-share/share-url-pathonly-manual.https.html new file mode 100644 index 0000000000..0487bc8521 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-pathonly-manual.https.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share with absolute path-only URL</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const url = '/some/path?some_query#some_fragment'; + setupManualShareTest({title: '', text: '', url: getAbsoluteUrl(url)}); + callWhenButtonClicked(() => navigator.share({url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-url-relative-manual.https.html b/testing/web-platform/tests/web-share/share-url-relative-manual.https.html new file mode 100644 index 0000000000..bbea3860a5 --- /dev/null +++ b/testing/web-platform/tests/web-share/share-url-relative-manual.https.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share with relative URL</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="resources/manual-helper.js"></script> + <base href="https://www.example.com/some/path.html"> + </head> + <body> + <script> + setup({explicit_timeout: true}); + + const url = 'foo'; + setupManualShareTest({title: '', text: '', url: getAbsoluteUrl(url)}); + callWhenButtonClicked(() => navigator.share({url})); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/share-without-user-gesture.https.html b/testing/web-platform/tests/web-share/share-without-user-gesture.https.html new file mode 100644 index 0000000000..1b2a764dbe --- /dev/null +++ b/testing/web-platform/tests/web-share/share-without-user-gesture.https.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>WebShare Test: Share without user gesture error</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <script> + promise_test(t => { + return promise_rejects_dom( + t, 'NotAllowedError', + navigator.share({title: 'the title', text: 'the message', + url: 'https://example.com'})); + }, 'share without a user gesture'); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/web-share/test-fully-active.https.html b/testing/web-platform/tests/web-share/test-fully-active.https.html new file mode 100644 index 0000000000..46991d3370 --- /dev/null +++ b/testing/web-platform/tests/web-share/test-fully-active.https.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <title>WebShare Test: consume user activation</title> + <link rel="help" href="https://github.com/w3c/web-share/pull/219" /> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + </head> + <body> + <script> + async function loadIframe() { + const iframe = document.createElement("iframe"); + iframe.src = "./resources/blank.html"; + document.body.appendChild(iframe); + await new Promise((resolve) => { + iframe.addEventListener("load", resolve); + }); + return iframe; + } + + promise_test(async (t) => { + const iframe = await loadIframe(); + const { contentWindow } = iframe; + const { navigator, DOMException } = contentWindow; + const data = { text: "text" }; + iframe.remove(); + await promise_rejects_dom( + t, + "InvalidStateError", + DOMException, + navigator.share(data), + "Expected promise rejected with InvalidStateError from .share()" + ); + }, "calling share() on non-fully active document returns a promise rejected with InvalidStateError"); + + promise_test(async (t) => { + const iframe = await loadIframe(); + const { contentWindow } = iframe; + const { navigator, DOMException } = contentWindow; + const data = { text: "text" }; + + // Acquire transient activation, but make the iframe non-fully-active + await test_driver.bless( + "web share", + () => { + iframe.remove(); + }, + contentWindow + ); + + await promise_rejects_dom( + t, + "InvalidStateError", + DOMException, + navigator.share(data), + "Expected promise rejected with InvalidStateError from .share()" + ); + }, "calling share() with transient activation on non-fully active document returns a promise rejected with InvalidStateError"); + + promise_test(async (t) => { + const iframe = await loadIframe(); + const { contentWindow } = iframe; + const { navigator } = contentWindow; + const data = { text: "text" }; + + assert_true(navigator.canShare(data), "doc is fully active, so true"); + + iframe.remove(); + + assert_false(navigator.canShare(data), "not fully active, so false"); + }, "calling canShare() on a non-fully active document returns false"); + </script> + </body> +</html> |