diff options
Diffstat (limited to '')
25 files changed, 1290 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/user-activation/META.yml b/testing/web-platform/tests/html/user-activation/META.yml new file mode 100644 index 0000000000..e50fcfc84f --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/META.yml @@ -0,0 +1,3 @@ +spec: https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation +suggested_reviewers: + - marcoscaceres diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html new file mode 100644 index 0000000000..be32d999b1 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-enter.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event"> + <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> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Test for keyboard activation trigger for ENTER key</h1> + <p>Tests user activation from a ENTER keyboard event.</p> + <input type="text" autofocus /> + <ol id="instructions"> + <li>Press ENTER key. + </ol> + <script> + function runTests() { + promise_test(async () => { + const ENTER_KEY = '\uE007'; + + let keydown_event = getEvent('keydown'); + let keypress_event = getEvent('keypress'); + let keyup_event = getEvent('keyup'); + + await test_driver.send_keys(document.body, ENTER_KEY); + + await keydown_event; + let consumed = await consumeTransientActivation(); + assert_true(consumed, + "ENTER keydown event should result in activation"); + + await keypress_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "ENTER keypress should have no activation after keydown consumption"); + + await keyup_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "ENTER keyup should have no activation after keydown consumption"); + }, "Activation through ENTER keyboard event"); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html new file mode 100644 index 0000000000..82c94d84c2 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/activation-trigger-keyboard-escape.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event"> + <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> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Test for keyboard activation trigger for ESCAPE key</h1> + <p>Tests missing user activation from a ESCAPE keyboard event.</p> + <input type="text" autofocus /> + <ol id="instructions"> + <li>Press ESCAPE key. + </ol> + <script> + function runTests() { + promise_test(async () => { + const ESCAPE_KEY = '\uE00C'; + + let keydown_event = getEvent('keydown'); + let keyup_event = getEvent('keyup'); + + await test_driver.send_keys(document.body, ESCAPE_KEY); + + await keydown_event; + let consumed = await consumeTransientActivation(); + assert_false(consumed, + "ESCAPE keydown event should not result in activation"); + + await keyup_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "ESCAPE keyup should have no activation after keydown consumption"); + }, "Activation through ESCAPE keyboard event"); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html new file mode 100644 index 0000000000..75a248c425 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-left.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event"> + <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> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Test for click activation trigger</h1> + <p>Tests user activation from a mouse click.</p> + <ol id="instructions"> + <li>Click anywhere in the document. + </ol> + <script> + function runTests() { + promise_test(async () => { + + let mousedown_event = getEvent('mousedown'); + let mouseup_event = getEvent('mouseup'); + let click_event = getEvent('click'); + + await test_driver.click(document.body); + + await mousedown_event; + let consumed = await consumeTransientActivation(); + assert_true(consumed, + "mousedown event should result in activation"); + + await mouseup_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "mouseup should have no activation after mousedown consumption"); + + await click_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "click should have no activation after mousedown consumption"); + }, "Activation through left-click mouse event"); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html new file mode 100644 index 0000000000..f98a54fe23 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/activation-trigger-mouse-right.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Test for right-click activation trigger</h1> + <p>Tests user activation from a mouse right-click.</p> + <ol id="instructions"> + <li>Right-click anywhere in the document. + </ol> + <script> + function runTests() { + promise_test(async () => { + var actions = new test_driver.Actions(); + actions.pointerMove(0, 0, {origin: document.body}) + .pointerDown({button: actions.ButtonType.RIGHT}) + .pointerUp({button: actions.ButtonType.RIGHT}) + .send(); + + // In most non-Windows platforms the right-click context-menu appears on mousedown, so + // mouseup and auxclick events are not received by the page if the menu is modal. We + // are suppressing the context-menu to guarantee receiving those later events. + document.body.addEventListener("contextmenu", e => e.preventDefault()); + + let mousedown_event = getEvent('mousedown'); + let mouseup_event = getEvent('mouseup'); + let auxclick_event = getEvent('auxclick'); + let contextmenu_event = getEvent('contextmenu'); + + await mousedown_event; + let consumed = await consumeTransientActivation(); + assert_true(consumed, + "mousedown event should result in activation"); + + await mouseup_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "mouseup should have no activation after mousedown consumption"); + + await auxclick_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "auxclick should have no activation after mousedown consumption"); + + await contextmenu_event; + consumed = await consumeTransientActivation(); + assert_false(consumed, + "contextmenu should have no activation after mousedown consumption"); + }, "Activation through right-click mouse event"); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html b/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html new file mode 100644 index 0000000000..e3042b718d --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/activation-trigger-pointerevent.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html> +<head> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event"> + <meta name="variant" content="?mouse"> + <meta name="variant" content="?pen"> + <meta name="variant" content="?touch"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Test for pointerevent click activation trigger</h1> + <p>Tests user activation from a pointer click.</p> + <ol id="instructions"> + <li>Click anywhere in the document. + </ol> + <script> + function runTests() { + let pointer_type = location.search.substring(1); + + promise_test(async () => { + const test_pointer = pointer_type + "TestPointer"; + + let pointerdown_event = getEvent('pointerdown'); + let pointerup_event = getEvent('pointerup'); + let click_event = getEvent('click'); + + await new test_driver.Actions().addPointer(test_pointer, pointer_type) + .pointerMove(0, 0, {origin:document.body, sourceName:test_pointer}) + .pointerDown({sourceName:test_pointer}) + .pointerUp({sourceName:test_pointer}) + .send(); + + await pointerdown_event; + let consumed_pointerdown = await consumeTransientActivation(); + await pointerup_event; + let consumed_pointerup = await consumeTransientActivation(); + await click_event; + let consumed_click = await consumeTransientActivation(); + + if (pointer_type === "mouse") { + assert_true(consumed_pointerdown, + pointer_type + " pointerdown event should result in activation"); + assert_false(consumed_pointerup, + pointer_type + " pointerup should have no activation after pointerdown consumption"); + assert_false(consumed_click, + pointer_type + " click should have no activation after pointerdown consumption"); + } else { + assert_false(consumed_pointerdown, + pointer_type + " pointerdown event should not result in activation"); + assert_true(consumed_pointerup, + pointer_type + " pointerup event should result in activation"); + assert_false(consumed_click, + pointer_type + " click should have no activation after pointerup consumption"); + } + }, "Activation through " + pointer_type + " pointerevent click"); + } + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/chained-setTimeout.html b/testing/web-platform/tests/html/user-activation/chained-setTimeout.html new file mode 100644 index 0000000000..a530837392 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/chained-setTimeout.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html> +<head> + <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> + <script> + let chained_timeout_test = async_test("Chained setTimeout test"); + + const max_call_depth = 3; + const delay_ms = 10; + + function testInitialStates(depth) { + assert_true(1 <= depth && depth <= max_call_depth); + + chained_timeout_test.step_timeout(() => { + let test_name = "Call-depth=" + depth + ": initial activation states are false"; + test(() => { + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + }, test_name); + + if (depth < max_call_depth) + testInitialStates(depth+1); + else + test_driver.click(document.body); + }, delay_ms); + } + + function testFinalStates(depth) { + assert_true(1 <= depth && depth <= max_call_depth); + + chained_timeout_test.step_timeout(() => { + let test_name = "Call-depth=" + depth + ": after-click activation states are true"; + test(() => { + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + }, test_name); + + if (depth < max_call_depth) + testFinalStates(depth+1); + else + chained_timeout_test.done(); + }, delay_ms) + } + + function run() { + window.addEventListener("click", event => { + testFinalStates(1); + }); + + testInitialStates(1); + } + </script> +</head> +<body onload="run()"> + <h1>User activation state in chained setTimeout calls</h1> + <p>Tests that user activation state is visible in arbitrary call depth of setTimeout.</p> + <ol id="instructions"> + <li>Click anywhere in the document. + </ol></body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/detached-iframe.html b/testing/web-platform/tests/html/user-activation/detached-iframe.html new file mode 100644 index 0000000000..af3d23072b --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/detached-iframe.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> + <head> + <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> + <script src="resources/utils.js"></script> + </head> + <body></body> + <script> + async function attachIframe() { + const iframe = document.createElement("iframe"); + iframe.src = "about:blank"; + await new Promise((r) => { + iframe.addEventListener("load", r, { once: true }); + document.body.append(iframe); + }); + return iframe; + } + + promise_test(async () => { + const iframe = await attachIframe(); + const { userActivation } = iframe.contentWindow.navigator; + + assert_false( + userActivation.isActive, + "No transient activation before click" + ); + assert_false( + userActivation.hasBeenActive, + "No sticky activation before click" + ); + + // Confirm we have activation + await test_driver.bless("click", null, iframe.contentWindow); + assert_true(userActivation.isActive, "is active after click"); + assert_true(userActivation.hasBeenActive, "has been active"); + + // Remove the context + iframe.remove(); + assert_equals(iframe.contentWindow, null, "No more global"); + assert_true(userActivation.isActive, "isActive"); + assert_true(userActivation.hasBeenActive, "hasBeenActive"); + }, "navigator.userActivation retains state even if global is removed"); + </script> +</html> diff --git a/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html b/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html new file mode 100644 index 0000000000..2a16f45496 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<!-- + Tentative due to: + https://github.com/whatwg/html/issues/1983 +--> +<html> +<head> +<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> + <h1>Clicking in iframe has activation state in child via MessageEvent</h1> + <ol id="instructions"> + <li>Click inside the red area. + </ol> + <iframe id="child" width="200" height="200"> + </iframe> + <script> + async_test(function(t) { + var child = document.getElementById("child"); + child.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-message-event-api.html"; + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + window.addEventListener("message", t.step_func(event => { + if (event.data == 'child-loaded') { + // values have false after load + assert_true(event.userActivation != null); + assert_false(event.userActivation.isActive); + assert_false(event.userActivation.hasBeenActive); + test_driver.click(child); + } else if (event.data == 'child-clicked') { + // values have activation state on click + assert_true(navigator.userActivation.hasBeenActive); + assert_true(event.userActivation != null); + assert_true(event.userActivation.isActive); + assert_true(event.userActivation.hasBeenActive); + child.contentWindow.postMessage('report', "*"); + } else if (event.data == 'child-report') { + assert_false(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + assert_true(event.userActivation != null); + assert_false(event.userActivation.isActive); + assert_true(event.userActivation.hasBeenActive); + child.contentWindow.postMessage('report-no-activation', "*"); + } else if (event.data == 'child-report-no-activation') { + assert_equals(event.userActivation, null); + t.done(); + } + })); + }, "Message propagates values on post"); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html b/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html new file mode 100644 index 0000000000..1f3ef55170 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/message-event-init.tentative.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<!-- + Tentative due to: + https://github.com/whatwg/html/issues/1983 +--> +<title>MessageEvent constructor</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(function() { + var ev = new MessageEvent("test", { userActivation: navigator.userActivation }) + assert_equals(ev.userActivation, navigator.userActivation, "userActivation attribute") +}, "MessageEventInit user activation set") +test(function() { + var ev = new MessageEvent("test") + assert_equals(ev.userActivation, null, "userActivation attribute") +}, "MessageEventInit user activation not set") + +</script> diff --git a/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html b/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html new file mode 100644 index 0000000000..5e16e9d46c --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/navigation-state-reset-crossorigin.sub.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html> +<head> + <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> + <script src="resources/utils.js"></script> +</head> +<body> + <h1>Post-navigation activation state in child</h1> + <p>Tests that navigating a cross-origin child frame resets its activation states.</p> + <ol id="instructions"> + <li>Click inside the yellow area. + </ol> + <iframe id="child" width="200" height="50"> + </iframe> + <script> + async_test(function(t) { + var child = document.getElementById("child"); + child.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html"; + window.addEventListener("message", t.step_func(event => { + // Test driver can send messages too... + if (typeof event.data !== "string") return; + + var msg = JSON.parse(event.data); + if (msg.type == 'child-one-loaded') { + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + + delayByFrames(() => test_driver.click(child), 5); + } else if (msg.type == 'child-one-clicked') { + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + assert_true(msg.isActive); + assert_true(msg.hasBeenActive); + + child.src = "http://{{hosts[alt][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html"; + } else if (msg.type == 'child-two-loaded') { + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + + t.done(); + } + })); + }, "Post-navigation state reset."); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html b/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html new file mode 100644 index 0000000000..c240f96b2c --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/navigation-state-reset-sameorigin.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html> + <head> + <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> + <h1>Post-navigation activation state in child</h1> + <p> + Tests that navigating a same-origin child frame resets its activation + states. + </p> + <ol id="instructions"> + <li>Click inside the yellow area.</li> + </ol> + + <iframe id="child" width="200" height="50"> </iframe> + <script> + function message(type) { + return new Promise((resolve) => { + window.addEventListener("message", function listener(event) { + const data = JSON.parse(event.data); + if (data.type === type) { + window.removeEventListener("message", listener); + resolve(data); + } + }); + }); + } + promise_test(async (t) => { + var child = document.getElementById("child"); + child.src = "./resources/child-one.html"; + const unclickeData = await message("child-one-loaded"); + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + assert_false(unclickeData.isActive); + assert_false(unclickeData.hasBeenActive); + + const [, child1Data] = await Promise.all([ + test_driver.click(child), + message("child-one-clicked"), + ]); + + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + assert_true(child1Data.isActive); + assert_true(child1Data.hasBeenActive); + + child.src = "./resources/child-two.html"; + + const child2Data = await message("child-two-loaded"); + + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + assert_false(child2Data.isActive); + assert_false(child2Data.hasBeenActive); + }, "Post-navigation state reset."); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html b/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html new file mode 100644 index 0000000000..0045e20788 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/no-activation-thru-escape-key.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<html> + <head> + <title>No user activation through 'Escape' key</title> + <meta name="timeout" content="long"> + <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/> + <link rel="author" title="Google" href="http://www.google.com "/> + <link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation"> + <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> + <style> + #target { + width: 40ex; + background-color: yellow; + } + </style> + <script type="text/javascript"> + let keydown_event_fired = false; + let keyup_event_fired = false; + + function run() { + let textbox_elem = document.getElementById("target"); + let test_esc_key = async_test("'Escape' key doesn't activate a page."); + + test_esc_key.step(() => { + assert_true(!!navigator.userActivation, "This test requires user activation query API"); + }); + + textbox_elem.focus(); + + on_event(textbox_elem, "keydown", () => { + test_esc_key.step(() => { + keydown_event_fired = true; + assert_false(navigator.userActivation.isActive, "No user activation on keydown"); + }); + }); + + on_event(textbox_elem, "keyup", () => { + test_esc_key.step(() => { + if (keydown_event_fired) + keyup_event_fired = true; + assert_true(keydown_event_fired, "keydown event fired before keyup"); + assert_false(navigator.userActivation.isActive, "No user activation on keyup"); + }); + }); + + // Inject mouse inputs. + const escape_key = "\uE00C"; + test_driver + .send_keys(textbox_elem, escape_key) + .then(() => { + assert_true(keyup_event_fired, "keydown event fired before keyup"); + test_esc_key.done(); + }); + } + </script> + </head> + <body onload="run()"> + <h1>No user activation through 'Escape' key</h1> + <h4>Tests that pressing/releasing 'Escape' key is not treated as a user activation.</h4> + <input id="target" value="Press and release the 'Esc' key." /> + </body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html b/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html new file mode 100644 index 0000000000..d317764036 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/propagation-crossorigin.sub.html @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html> +<head> + <meta name="timeout" content="long"> + <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> + <script src="resources/utils.js"></script> + <script> + // Frame layout: + // top=origin0:this-file [ + // child1=origin1:child-one.html, + // child-xo=origin2:propagation-crossorigin-child.html [ + // gchild=origin3:child-two.html + // ] + // ] + let propagation_test = async_test("Propagation test"); + + let num_children_to_load = 3; + let num_children_to_report = 3; + + function finishLoadPhase() { + assert_equals(num_children_to_load, 0); + + test(() => { + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + }, "Parent frame initial state"); + + delayByFrames(() => test_driver.click(document.getElementById("child-xo")), 7); + } + + function finishReportPhase() { + assert_equals(num_children_to_report, 0); + + test(() => { + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + }, "Parent frame final state"); + + propagation_test.done(); + } + + window.addEventListener("message", event => { + // Test driver can send messages too... + if (typeof event.data !== "string") return; + + var msg = JSON.parse(event.data); + + if (msg.type == 'child-one-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child1 frame initial state"); + } else if (msg.type == 'child-crossorigin-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child2 frame initial state"); + } else if (msg.type == 'child-two-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Grandchild frame initial state"); + } else if (msg.type == 'child-one-report') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child1 frame final state"); + } else if (msg.type == 'child-crossorigin-report') { + // This msg was triggered by a user click. + test(() => { + assert_true(msg.isActive); + assert_true(msg.hasBeenActive); + }, "Child2 frame final state"); + + // Ask remaining frames to report states. + let ask_report = JSON.stringify({"type": "report"}); + frames[0].postMessage(ask_report, "*"); + frames[1].frames[0].postMessage(ask_report, "*"); + } else if (msg.type == 'child-two-report') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Grand child frame final state"); + } + + // Phase switching. + if (msg.type.endsWith("-loaded")) { + if (--num_children_to_load == 0) + finishLoadPhase(); + } else if (msg.type.endsWith("-report")) { + if (--num_children_to_report == 0) + finishReportPhase(); + } + }); + async function createIframes() { + const child1 = document.createElement("iframe"); + child1.src = "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html"; + child1.id = "child1"; + document.body.appendChild(child1); + await new Promise((resolve) => { + child1.onload = resolve; + document.body.appendChild(child1); + }); + const childXO = document.createElement("iframe"); + childXO.id = "child-xo"; + childXO.src = "http://{{hosts[alt][]}}:{{ports[http][1]}}/html/user-activation/resources/propagation-crossorigin-child.sub.html"; + document.body.appendChild(childXO); + } + </script> +</head> +<body onload="createIframes()"> + <h1>User activation propagation across cross-origin frame boundary</h1> + <p>Tests that user activation does not propagate across cross-origin frame boundary.</p> + <ol id="instructions"> + <li>Click anywhere on the green area (child frame). + </ol> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html b/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html new file mode 100644 index 0000000000..31a7222448 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/propagation-same-and-cross-origin.sub.html @@ -0,0 +1,151 @@ +<!DOCTYPE html> +<html> + <head> + <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> + <script src="resources/utils.js"></script> + </head> + <body> + <h1>Check that cross origin iframes don't get activated</h1> + <p> + Tests that activating a same-origin navigable doesn't activate a cross + origin navigable. + </p> + <ol id="instructions"> + <li>Click inside the yellow area.</li> + </ol> + <h1>Same origin</h1> + <iframe id="so-child" width="200" height="50"></iframe> + <h1>Cross origin</h1> + <iframe id="xo-child" width="200" height="50"></iframe> + </body> + <script> + const soChild = document.getElementById("so-child"); + const xoChild = document.getElementById("xo-child"); + + function requestXOReport() { + xoChild.contentWindow.postMessage( + JSON.stringify({ type: "report" }), + "*" + ); + return receiveMessage(`child-two-report`); + } + + promise_setup(() => { + soChild.src = "./resources/child-one.html"; + xoChild.src = + "http://{{hosts[alt][]}}:{{ports[http][0]}}/html/user-activation/resources/child-two.html"; + return Promise.all([ + receiveMessage("child-one-loaded"), + receiveMessage("child-two-loaded"), + ]); + }); + + promise_test(async (t) => { + const unclickedCrossOrigin = await requestXOReport(); + const soActivation = soChild.contentWindow.navigator.userActivation; + assert_false( + navigator.userActivation.isActive, + "top-frame navigator.userActivation.isActive must be false" + ); + assert_false( + navigator.userActivation.hasBeenActive, + "top-frame navigator.userActivation.hasBeenActive must be false" + ); + + assert_false(soActivation.isActive, "child-one isActive must be false"); + assert_false( + soActivation.hasBeenActive, + "child-one hasBeenActive must be false" + ); + assert_false( + unclickedCrossOrigin.isActive, + "child-two isActive must be false" + ); + assert_false( + unclickedCrossOrigin.hasBeenActive, + "child-two hasBeenActive must be false" + ); + }, "Check Initial states of user activation are all false"); + + promise_test(async (t) => { + await test_driver.click(soChild); + const xoActivation = await requestXOReport(); + const soActivation = soChild.contentWindow.navigator.userActivation; + assert_true( + navigator.userActivation.isActive, + "top-frame navigator.userActivation.isActive must be true" + ); + assert_true( + navigator.userActivation.hasBeenActive, + "top-frame navigator.userActivation.hasBeenActive must be true" + ); + assert_true(soActivation.isActive, "child-one isActive must be true"); + assert_true( + soActivation.hasBeenActive, + "child-one hasBeenActive must be true" + ); + assert_false(xoActivation.isActive, "child-two isActive must be false"); + assert_false( + xoActivation.hasBeenActive, + "child-two hasBeenActive must be false" + ); + }, "Check that activating a same-origin navigable doesn't activate a cross origin navigable"); + + promise_test(async (t) => { + await consumeTransientActivation(); + const soActivation = soChild.contentWindow.navigator.userActivation; + // Before click... + assert_false( + navigator.userActivation.isActive, + "top-frame navigator.userActivation.isActive must be false" + ); + assert_true( + navigator.userActivation.hasBeenActive, + "top-frame navigator.userActivation.hasBeenActive must be true" + ); + assert_false(soActivation.isActive, "child-one isActive must be false"); + assert_true( + soActivation.hasBeenActive, + "child-one hasBeenActive must be true" + ); + const xoActivation = await requestXOReport(); + assert_false(xoActivation.isActive, "child-two isActive must be false"); + assert_false( + xoActivation.hasBeenActive, + "child-two hasBeenActive must be false" + ); + + // Click! + const [, xoActivationAfterClick] = await Promise.all([ + test_driver.click(xoChild), + receiveMessage("child-two-clicked"), + ]); + + // After click... + assert_true( + navigator.userActivation.isActive, + "top-frame navigator.userActivation.isActive must be true" + ); + assert_true( + navigator.userActivation.hasBeenActive, + "top-frame navigator.userActivation.hasBeenActive must remain true" + ); + assert_true( + xoActivationAfterClick.isActive, + "child-two isActive must be true" + ); + assert_true( + xoActivationAfterClick.hasBeenActive, + "child-two hasBeenActive must be true" + ); + assert_false(soActivation.isActive, "child-one isActive must be false"); + assert_true( + soActivation.hasBeenActive, + "child-one hasBeenActive must remain true" + ); + }, "Clicking on the cross-origin navigable activates parent navigable."); + </script> +</html> diff --git a/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html b/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html new file mode 100644 index 0000000000..94d15c2d41 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/propagation-sameorigin.html @@ -0,0 +1,120 @@ +<!DOCTYPE html> +<html> +<head> + <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> + <script> + // Frame layout: + // top=this-file [ + // child1=child-one.html, + // child-so=propagation-sameorigin-child.html [ + // gchild=child-two.html + // ] + // ] + let propagation_test = async_test("Propagation test"); + + let num_children_to_load = 3; + let num_children_to_report = 3; + + function finishLoadPhase() { + assert_equals(num_children_to_load, 0); + + test(() => { + assert_false(navigator.userActivation.isActive); + assert_false(navigator.userActivation.hasBeenActive); + }, "Parent frame initial state"); + + test_driver.click(document.getElementById("child-so")); + } + + function finishReportPhase() { + assert_equals(num_children_to_report, 0); + + test(() => { + assert_true(navigator.userActivation.isActive); + assert_true(navigator.userActivation.hasBeenActive); + }, "Parent frame final state"); + + propagation_test.done(); + } + + window.addEventListener("message", event => { + // Test driver can send messages too... + if (typeof event.data !== "string") return; + + var msg = JSON.parse(event.data); + + if (msg.type == 'child-one-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child1 frame initial state"); + } else if (msg.type == 'child-sameorigin-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child2 frame initial state"); + } else if (msg.type == 'child-two-loaded') { + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Grandchild frame initial state"); + } else if (msg.type == 'child-one-report') { + // Siblings (same or cross origin) should not be activated per spec + // Spec issue discussing: https://github.com/whatwg/html/issues/9831 + test(() => { + assert_false(msg.isActive); + assert_false(msg.hasBeenActive); + }, "Child1 frame final state"); + } else if (msg.type == 'child-sameorigin-report') { + // This msg was triggered by a user click. + test(() => { + assert_true(msg.isActive); + assert_true(msg.hasBeenActive); + }, "Child2 frame final state"); + + // Ask remaining frames to report states. + let ask_report = JSON.stringify({"type": "report"}); + frames[0].postMessage(ask_report, "*"); + frames[1].frames[0].postMessage(ask_report, "*"); + } else if (msg.type == 'child-two-report') { + test(() => { + assert_true(msg.isActive); + assert_true(msg.hasBeenActive); + }, "Grand child frame final state"); + } + + // Phase switching. + if (msg.type.endsWith("-loaded")) { + if (--num_children_to_load == 0) + finishLoadPhase(); + } else if (msg.type.endsWith("-report")) { + if (--num_children_to_report == 0) + finishReportPhase(); + } + }); + async function createIframes() { + const child1 = document.createElement("iframe"); + child1.src = "resources/child-one.html"; + child1.id = "child1"; + await new Promise((resolve) => { + child1.onload = resolve; + document.body.appendChild(child1); + }); + const childSO = document.createElement("iframe"); + childSO.id = "child-so"; + childSO.src = "resources/propagation-sameorigin-child.html"; + document.body.appendChild(childSO); + } + </script> +</head> +<body onload="createIframes()"> + <h1>User activation propagation across same-origin frame boundary</h1> + <p>Tests that user activation propagates across same-origin frame boundary.</p> + <ol id="instructions"> + <li>Click anywhere on the green area (child frame). + </ol> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html b/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html new file mode 100644 index 0000000000..a0001633c2 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/child-message-event-api.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<body style="background: red;"> + <script> + window.parent.postMessage("child-loaded", + {targetOrigin: "*", includeUserActivation: true}); + window.addEventListener("click", event => { + window.parent.postMessage("child-clicked", + {targetOrigin: "*", includeUserActivation: true}); + var win = window.open('404.html'); + win.close(); + }); + + window.addEventListener("message", event => { + if (event.data == "report") { + window.parent.postMessage("child-report", + {targetOrigin: "*", includeUserActivation: true}); + } + if (event.data == "report-no-activation") { + window.parent.postMessage("child-report-no-activation", + {targetOrigin: "*", includeUserActivation: false}); + } + }); + </script> +</body> diff --git a/testing/web-platform/tests/html/user-activation/resources/child-one.html b/testing/web-platform/tests/html/user-activation/resources/child-one.html new file mode 100644 index 0000000000..9668372620 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/child-one.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<body style="background: yellow;"> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-one-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.top.postMessage(JSON.stringify({ + "type": "child-one-clicked", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + + window.addEventListener("message", event => { + var msg = JSON.parse(event.data); + if (msg.type == "report") { + window.top.postMessage(JSON.stringify({ + "type": "child-one-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + } + }); + </script> +</body> diff --git a/testing/web-platform/tests/html/user-activation/resources/child-two.html b/testing/web-platform/tests/html/user-activation/resources/child-two.html new file mode 100644 index 0000000000..9fb68dbd51 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/child-two.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<body style="background: lightgrey;"> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-two-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.top.postMessage(JSON.stringify({ + "type": "child-two-clicked", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + + window.addEventListener("message", event => { + var msg = JSON.parse(event.data); + if (msg.type == "report") { + window.top.postMessage(JSON.stringify({ + "type": "child-two-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + } + }); + </script> +</body> diff --git a/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html b/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html new file mode 100644 index 0000000000..518e000d0b --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/consumption-crossorigin-child.sub.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-crossorigin-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.open().close(); + + window.top.postMessage(JSON.stringify({ + "type": "child-crossorigin-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + </script> +</head> +<body style="background: lightgreen;"> + <!-- The midpoint of this frame should be outside the grandchild frame. --> + <div style="height: 75px;">Cross-origin child frame</div> + <iframe id="child2" width="270px" height="30px" + src="http://{{hosts[][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html"> + </iframe> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html b/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html new file mode 100644 index 0000000000..9e421fc0f1 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/consumption-sameorigin-child.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-sameorigin-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.open().close(); + + window.top.postMessage(JSON.stringify({ + "type": "child-sameorigin-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + </script> +</head> +<body style="background: lightgreen;"> + <!-- The midpoint of this frame should be outside the grandchild frame. --> + <div style="height: 75px;">Same-origin child frame</div> + <iframe id="child2" width="270px" height="30px" + src="child-two.html"> + </iframe> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html b/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html new file mode 100644 index 0000000000..e920566a21 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/propagation-crossorigin-child.sub.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-crossorigin-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.top.postMessage(JSON.stringify({ + "type": "child-crossorigin-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + </script> +</head> +<body style="background: lightgreen;"> + <!-- The midpoint of this frame should be outside the grandchild frame. --> + <div style="height: 75px;">Cross-origin child frame</div> + <iframe id="child2" width="270px" height="30px" + src="http://{{hosts[][]}}:{{ports[http][1]}}/html/user-activation/resources/child-two.html"> + </iframe> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html b/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html new file mode 100644 index 0000000000..69ad50cb71 --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/propagation-sameorigin-child.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> + <script> + window.top.postMessage(JSON.stringify({ + "type": "child-sameorigin-loaded", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + + window.addEventListener("click", event => { + window.top.postMessage(JSON.stringify({ + "type": "child-sameorigin-report", + "isActive": navigator.userActivation.isActive, + "hasBeenActive": navigator.userActivation.hasBeenActive + }), "*"); + }); + </script> +</head> +<body style="background: lightgreen;"> + <!-- The midpoint of this frame should be outside the grandchild frame. --> + <div style="height: 75px;">Same-origin child frame</div> + <iframe id="child2" width="270px" height="30px" + src="child-two.html"> + </iframe> +</body> +</html> diff --git a/testing/web-platform/tests/html/user-activation/resources/utils.js b/testing/web-platform/tests/html/user-activation/resources/utils.js new file mode 100644 index 0000000000..5d3302583f --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/resources/utils.js @@ -0,0 +1,48 @@ +function delayByFrames(f, num_frames) { + function recurse(depth) { + if (depth == 0) + f(); + else + requestAnimationFrame(() => recurse(depth-1)); + } + recurse(num_frames); +} + +// Returns a Promise which is resolved with the event object when the event is +// fired. +function getEvent(eventType) { + return new Promise(resolve => { + document.body.addEventListener(eventType, e => resolve(e), {once: true}); + }); +} + + +// Returns a Promise which is resolved with a "true" iff transient activation +// was available and successfully consumed. +// +// This function relies on Fullscreen API to check/consume user activation +// state. +async function consumeTransientActivation() { + try { + await document.body.requestFullscreen(); + await document.exitFullscreen(); + return true; + } catch(e) { + return false; + } +} + +function receiveMessage(type) { + return new Promise((resolve) => { + window.addEventListener("message", function listener(event) { + if (typeof event.data !== "string") { + return; + } + const data = JSON.parse(event.data); + if (data.type === type) { + window.removeEventListener("message", listener); + resolve(data); + } + }); + }); +} diff --git a/testing/web-platform/tests/html/user-activation/user-activation-interface.html b/testing/web-platform/tests/html/user-activation/user-activation-interface.html new file mode 100644 index 0000000000..8ece08f11d --- /dev/null +++ b/testing/web-platform/tests/html/user-activation/user-activation-interface.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> +<head> + <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> + <script src="resources/utils.js"></script> +</head> +<body onload="runTests()"> + <h1>Basic test for navigator.userActivation interface</h1> + <p>Tests that navigator.userActivation shows user activation states.</p> + <ol id="instructions"> + <li>Click anywhere in the document. + </ol> + <script> + function runTests() { + promise_test(async () => { + assert_true(!!navigator.userActivation, "This test requires navigator.userActivation API"); + + assert_false(navigator.userActivation.hasBeenActive, "No sticky activation before click"); + assert_false(navigator.userActivation.isActive, "No transient activation before click"); + + await test_driver.click(document.body); + + assert_true(navigator.userActivation.hasBeenActive, "Has sticky activation after click"); + assert_true(navigator.userActivation.isActive, "Has transient activation after click"); + }, "navigator.userActivation shows correct states before/after a click"); + } + </script> +</body> +</html> |