Skip to content
Permalink
Browse files
navigator.userActivation API to query user activation state
https://bugs.webkit.org/show_bug.cgi?id=245240

Reviewed by Youenn Fablet and Geoffrey Garen.

Implementation of the UserActivation interface:
https://html.spec.whatwg.org/#the-useractivation-interface

Which was added to HTML via:
whatwg/html#8254

The API provides web content a view into DOMWindow::hasTransientActivation()
and DOMWindow::hasStickyActivation(). This change excludes anything
related "MessageEvent", which has not yet been agreed to by the WHATWG.

The tests have been modified to work on WebKit infrastructure without
changing the intent of the original tests. In particular:

* `domains[www1]` and so on, so those are changed to `hosts[alt][]`
* Domains are differentiated by using different ports.
* Some of the tests are missing `await`.
* Some tests would run before the body was ready.

A PR has been sent to WPT to address these issues:
web-platform-tests/wpt#36881

The tests are update to 1b73dcd.

Some test coming form WTP are non-standard. In particular, tests that
rely on `window.open()` and `.requestFullscreen()` are not spec'ed
to consume user activation. This is a known issue and will be addressed
as a followup in the specs and potentially as changes in WebKit.

* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/activation-trigger-keyboard-enter.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/activation-trigger-keyboard-escape.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/activation-trigger-mouse-left.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/activation-trigger-mouse-right.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/activation-trigger-pointerevent.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/chained-setTimeout-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/chained-setTimeout.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/chained-setTimeout.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/chained-setTimeout.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-crossorigin.sub-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-crossorigin.sub.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-crossorigin.sub.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-crossorigin.sub.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-sameorigin-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-sameorigin.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-sameorigin.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/consumption-sameorigin.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/detached-iframe-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/detached-iframe.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/message-event-activation-api-iframe-cross-origin.sub.tentative.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/message-event-init.tentative-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/message-event-init.tentative.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-crossorigin.sub-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-crossorigin.sub.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-crossorigin.sub.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-sameorigin-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-sameorigin.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-sameorigin.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/navigation-state-reset-sameorigin.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/no-activation-thru-escape-key-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/no-activation-thru-escape-key.html:
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-crossorigin.sub-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-crossorigin.sub.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-crossorigin.sub.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-crossorigin.sub.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-sameorigin-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-sameorigin.html: Renamed from LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-sameorigin.tentative.html.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/propagation-sameorigin.tentative-expected.txt: Removed.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/user-activation-interface-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/html/user-activation/user-activation-interface.html: Added.
* LayoutTests/platform/gtk/fast/dom/navigator-detached-no-crash-expected.txt:
* LayoutTests/platform/ios-wk2/TestExpectations:
* LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/user-activation/chained-setTimeout.tentative-expected.txt: Removed.
* LayoutTests/platform/mac-wk1/fast/dom/navigator-detached-no-crash-expected.txt:
* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/html/user-activation/no-activation-thru-escape-key-expected.txt:
* LayoutTests/platform/mac-wk2/fast/dom/navigator-detached-no-crash-expected.txt:
* Source/WTF/Scripts/Preferences/WebPreferencesExperimental.yaml:
* Source/WebCore/CMakeLists.txt:
* Source/WebCore/DerivedSources-input.xcfilelist:
* Source/WebCore/DerivedSources-output.xcfilelist:
* Source/WebCore/DerivedSources.make:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/bindings/js/WebCoreBuiltinNames.h:
* Source/WebCore/html/NavigatorUserActivation.cpp: Added.
(WebCore::NavigatorUserActivation::NavigatorUserActivation):
(WebCore::NavigatorUserActivation::userActivation):
(WebCore::NavigatorUserActivation::from):
(WebCore::NavigatorUserActivation::supplementName):
* Source/WebCore/html/NavigatorUserActivation.h: Added.
* Source/WebCore/html/URLSearchParams.h:
* Source/WebCore/html/UserActivation.cpp: Added.
(WebCore::UserActivation::create):
(WebCore::UserActivation::UserActivation):
(WebCore::UserActivation::navigator):
(WebCore::UserActivation::window const):
(WebCore::UserActivation::hasBeenActive const):
(WebCore::UserActivation::isActive const):
* Source/WebCore/html/UserActivation.h: Added.
* Source/WebCore/html/UserActivation.idl: Added.
* Source/WebCore/page/DOMWindow.cpp:
(WebCore::DOMWindow::hasStickyActivation const):
* Source/WebCore/page/DOMWindow.h:
* Source/WebCore/page/Navigator+UserActivation.idl: Added.

Canonical link: https://commits.webkit.org/256572@main
  • Loading branch information
marcoscaceres authored and eric-carlson committed Nov 11, 2022
1 parent 82e5917 commit 2df18f66ddd3a59cb6f0f32bac746137e95a8d0b
Show file tree
Hide file tree
Showing 59 changed files with 635 additions and 188 deletions.
@@ -8,22 +8,24 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/utils.js"></script>
</head>
<body>
<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';
test_driver.send_keys(document.body, ENTER_KEY);

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,
@@ -39,6 +41,7 @@ <h1>Test for keyboard activation trigger for ENTER key</h1>
assert_false(consumed,
"ENTER keyup should have no activation after keydown consumption");
}, "Activation through ENTER keyboard event");
}
</script>
</body>
</html>
@@ -8,21 +8,23 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/utils.js"></script>
</head>
<body>
<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';
test_driver.send_keys(document.body, ESCAPE_KEY);

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,
@@ -33,6 +35,7 @@ <h1>Test for keyboard activation trigger for ESCAPE key</h1>
assert_false(consumed,
"ESCAPE keyup should have no activation after keydown consumption");
}, "Activation through ESCAPE keyboard event");
}
</script>
</body>
</html>
@@ -8,20 +8,22 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/utils.js"></script>
</head>
<body>
<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 () => {
test_driver.click(document.body);

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,
@@ -37,6 +39,7 @@ <h1>Test for click activation trigger</h1>
assert_false(consumed,
"click should have no activation after mousedown consumption");
}, "Activation through left-click mouse event");
}
</script>
</body>
</html>
@@ -9,13 +9,14 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/utils.js"></script>
</head>
<body>
<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})
@@ -53,6 +54,7 @@ <h1>Test for right-click activation trigger</h1>
assert_false(consumed,
"contextmenu should have no activation after mousedown consumption");
}, "Activation through right-click mouse event");
}
</script>
</body>
</html>
@@ -12,13 +12,14 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/utils.js"></script>
</head>
<body>
<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 () => {
@@ -57,6 +58,7 @@ <h1>Test for pointerevent click activation trigger</h1>
pointer_type + " click should have no activation after pointerup consumption");
}
}, "Activation through " + pointer_type + " pointerevent click");
}
</script>
</body>
</html>
@@ -0,0 +1,14 @@
User activation state in chained setTimeout calls

Tests that user activation state is visible in arbitrary call depth of setTimeout.

Click anywhere in the document.

PASS Chained setTimeout test
PASS Call-depth=1: initial activation states are false
PASS Call-depth=2: initial activation states are false
PASS Call-depth=3: initial activation states are false
PASS Call-depth=1: after-click activation states are true
PASS Call-depth=2: after-click activation states are true
PASS Call-depth=3: after-click activation states are true

@@ -1,8 +1,4 @@
<!DOCTYPE html>
<!--
Tentative due to:
https://github.com/whatwg/html/issues/1983
-->
<html>
<head>
<script src="/resources/testharness.js"></script>

This file was deleted.

@@ -0,0 +1,18 @@
User activation consumption across cross-origin frame boundary

Tests that user activation consumption resets the transient states in all cross-origin frames.

Click anywhere on the yellow area.
Click anywhere on the green area (child frame).


PASS Consumption test
PASS Child1 frame initial state
PASS Child2 frame initial state
PASS Grandchild frame initial state
PASS Parent frame initial state
FAIL Child2 frame final state assert_false: expected false got true
FAIL Child1 frame final state assert_false: expected false got true
PASS Grand child frame final state
FAIL Parent frame final state assert_false: expected false got true

@@ -1,8 +1,4 @@
<!DOCTYPE html>
<!--
Tentative due to:
https://github.com/whatwg/html/issues/1983
-->
<html>
<head>
<meta name="timeout" content="long">
@@ -99,20 +95,27 @@
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";
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/consumption-crossorigin-child.sub.html";
document.body.appendChild(childXO);
}
</script>
</head>
<body>
<body onload="createIframes()">
<h1>User activation consumption across cross-origin frame boundary</h1>
<p>Tests that user activation consumption resets the transient states in all cross-origin frames.</p>
<ol id="instructions">
<li>Click anywhere on the yellow area.
<li>Click anywhere on the green area (child frame).
</ol>
<iframe id="child1" width="300px" height="40px"
src="http://{{domains[www1]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html">
</iframe>
<iframe id="child-xo" width="300px" height="140px"
src="http://{{domains[www2]}}:{{ports[http][0]}}/html/user-activation/resources/consumption-crossorigin-child.sub.html">
</iframe>
</body>
</html>

This file was deleted.

@@ -0,0 +1,17 @@
User activation consumption across same-origin frame boundary

Tests that user activation consumption resets the transient states in all same-origin frames.

Click anywhere on the green area (child frame).


PASS Consumption test
PASS Child1 frame initial state
PASS Child2 frame initial state
PASS Grandchild frame initial state
PASS Parent frame initial state
FAIL Child2 frame final state assert_false: expected false got true
FAIL Child1 frame final state assert_false: expected false got true
FAIL Grand child frame final state assert_false: expected false got true
FAIL Parent frame final state assert_false: expected false got true

@@ -1,8 +1,4 @@
<!DOCTYPE html>
<!--
Tentative due to:
https://github.com/whatwg/html/issues/1983
-->
<html>
<head>
<script src="/resources/testharness.js"></script>
@@ -30,7 +26,7 @@
assert_false(navigator.userActivation.hasBeenActive);
}, "Parent frame initial state");

test_driver.click(document.getElementById("child-so"));
return test_driver.click(document.getElementById("child-so"));
}

function finishReportPhase() {
@@ -44,7 +40,7 @@
consumption_test.done();
}

window.addEventListener("message", event => {
window.addEventListener("message", async event => {
var msg = JSON.parse(event.data);

if (msg.type == 'child-one-loaded') {
@@ -88,25 +84,32 @@
// Phase switching.
if (msg.type.endsWith("-loaded")) {
if (--num_children_to_load == 0)
finishLoadPhase();
await 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/consumption-sameorigin-child.html";
document.body.appendChild(childSO);
}
</script>
</head>
<body>
<body onload="createIframes()" >
<h1>User activation consumption across same-origin frame boundary</h1>
<p>Tests that user activation consumption resets the transient states in all same-origin frames.</p>
<ol id="instructions">
<li>Click anywhere on the green area (child frame).
</ol>
<iframe id="child1" width="300px" height="40px"
src="resources/child-one.html">
</iframe>
<iframe id="child-so" width="300px" height="140px"
src="resources/consumption-sameorigin-child.html">
</iframe>
</body>
</html>

This file was deleted.

@@ -0,0 +1,3 @@

PASS navigator.userActivation retains state even if global is removed

0 comments on commit 2df18f6

Please sign in to comment.