Skip to content

Commit

Permalink
Add showPicker() method to HTMLSelectElement interface
Browse files Browse the repository at this point in the history
This CL adds an experimental method to the HTMLSelectElement interface
to allow web developers to show the browser picker for select elements.
It requires a user gesture and is disallowed for cross-origin iframes.

Intent to prototype:
https://groups.google.com/a/chromium.org/g/blink-dev/c/6MUAqY2r3Hg

Demo: https://select-show-picker.glitch.me/
Spec: whatwg/html#9754
Bug: 1485010
Change-Id: I822bff7b313dfa840b2a72edb66fcb4a2c7cb58d
  • Loading branch information
lukewarlow authored and chromium-wpt-export-bot committed Sep 20, 2023
1 parent be778b5 commit c31dd35
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<title>Test showPicker() in an iframe</title>
<script type=module>
const urlParams = new URLSearchParams(location.search);
const documentDomain = urlParams.get('documentDomain');
if (documentDomain) {
document.domain = documentDomain;
}

let securityErrors = [];
const select = document.createElement("select");
try {
select.showPicker();
} catch (error) {
if (error instanceof DOMException && error.name == 'SecurityError') {
securityErrors.push("select");
}
}
parent.postMessage(securityErrors.join(','), "*");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<title>Test showPicker() called from cross-origin iframe</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<body>
<iframe id="iframe1"></iframe>
<iframe id="iframe2"></iframe>
<iframe id="iframe3"></iframe>
<iframe id="iframe4"></iframe>
</body>
<script>
function waitForSecurityErrors() {
return new Promise((resolve) => {
window.addEventListener("message", (event) => resolve(event.data), {
once: true,
});
});
}

promise_test(async (t) => {
iframe1.src =
new URL("resources/", self.location).pathname +
"show-picker-child-iframe.html";

// Wait for the iframe to report security errors when calling showPicker().
const securityErrors = await waitForSecurityErrors();
assert_equals(
securityErrors,
"",
"In same-origin iframes, showPicker() does not throw a SecurityError."
);
});

promise_test(async (t) => {
iframe2.src =
get_host_info().HTTP_NOTSAMESITE_ORIGIN +
new URL("resources/", self.location).pathname +
"show-picker-child-iframe.html";

// Wait for the iframe to report security errors when calling showPicker().
const securityErrors = await waitForSecurityErrors();
assert_equals(
securityErrors,
"select",
"In cross-origin iframes, showPicker() throws a SecurityError."
);
});

promise_test(async (t) => {
iframe3.src =
new URL("resources/", self.location).pathname +
"show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;

// Wait for the iframe to report security errors when calling showPicker().
const securityErrors = await waitForSecurityErrors();
assert_equals(
securityErrors,
"",
"In same-origin but cross-origin-domain iframes, showPicker() does not throw a SecurityError."
);
});

promise_test(async (t) => {
document.domain = get_host_info().ORIGINAL_HOST;
iframe4.src =
get_host_info().HTTP_REMOTE_ORIGIN +
new URL("resources/", self.location).pathname +
"show-picker-child-iframe.html?documentDomain=" + get_host_info().ORIGINAL_HOST;

// Wait for the iframe to report security errors when calling showPicker().
const securityErrors = await waitForSecurityErrors();
assert_equals(
securityErrors,
"select",
"In cross-origin but same-origin-domain iframes, showPicker() throws a SecurityError."
);
});
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<title>Test showPicker() disabled/readonly requirement</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>
<body></body>
<script type=module>
test(() => {
const select = document.createElement("select");
select.setAttribute("disabled", "");

assert_throws_dom('InvalidStateError', () => { select.showPicker(); });
}, 'select showPicker() throws when disabled');

test(() => {
const select = document.createElement("select");
select.setAttribute("readonly", "");

assert_throws_dom('InvalidStateError', () => { select.showPicker(); });
}, 'select showPicker() throws when readonly');
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<title>Test showPicker() user gesture requirement</title>
<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>
<body></body>
<script type=module>
test(() => {
const select = document.createElement("select");

assert_throws_dom('NotAllowedError', () => { select.showPicker(); });
}, `select showPicker() requires a user gesture`);

promise_test(async t => {
const select = document.createElement("select");

await test_driver.bless('show picker');
select.showPicker();
select.blur();
}, `select showPicker() does not throw when user activation is active`);
</script>

0 comments on commit c31dd35

Please sign in to comment.