Skip to content

Commit

Permalink
Portals: focus() inside portal should change active element
Browse files Browse the repository at this point in the history
Allows a portal document's activeElement to update when focus is called.
Note that focus events won't be dispatched as the portal doesn't get
page focus, and the portal WebContents isn't set as the focused
WebContents (with the exception of orphaned portals). Updating the
activeElement also means an autofocused element inside a portal will
receive focus when the portal is activated.

More discussion here: WICG/portals#257

Change-Id: If67be1f424114653983b473a80c515337d49596a
Bug: 1059404
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2566072
Commit-Queue: Adithya Srinivasan <[email protected]>
Reviewed-by: Lucas Gadani <[email protected]>
Reviewed-by: Jeremy Roman <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Reviewed-by: David Trainor <[email protected]>
Cr-Commit-Position: refs/heads/master@{#853647}
GitOrigin-RevId: 11c3d6ba0f0c5e26aec2c764f9f95d037c23fc10
  • Loading branch information
a4sriniv authored and copybara-github committed Feb 12, 2021
1 parent 017ea00 commit 5efa864
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
3 changes: 0 additions & 3 deletions blink/renderer/core/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8195,9 +8195,6 @@ bool Document::IsSlotAssignmentOrLegacyDistributionDirty() const {
}

bool Document::IsFocusAllowed() const {
if (GetFrame() && GetFrame()->GetPage()->InsidePortal())
return false;

if (!GetFrame() || GetFrame()->IsMainFrame() ||
LocalFrame::HasTransientUserActivation(GetFrame())) {
// 'autofocus' runs Element::focus asynchronously at which point the
Expand Down
48 changes: 48 additions & 0 deletions blink/web_tests/external/wpt/portals/portals-focus.sub.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
}
}, "test that an element inside a portal cannot steal focus");

promise_test(async () => {
let portal = await createPortal(document, new URL("resources/focus-page-with-button.html", location.href));
try {
let activeElementUpdated = new Promise(r => {
portal.onmessage = e => r(e.data.activeElementUpdated)
});
portal.postMessage('focus-update-active-element');
assert_true(await activeElementUpdated);
} finally {
document.body.removeChild(portal);
}
}, "test that activeElement inside a portal is updated after focus() is called");

promise_test(async t => {
let portal = await createPortal(document, new URL("resources/focus-page-with-x-origin-iframe.sub.html", location.href));
try {
Expand All @@ -42,6 +55,19 @@
}
}, "test that an element inside a portal's x-origin subframe cannot steal focus");

promise_test(async () => {
let portal = await createPortal(document, new URL("resources/focus-page-with-x-origin-iframe.sub.html", location.href));
try {
portal.postMessage("focus-update-active-element");
let {activeElementUpdated} = await new Promise(r => {
portal.onmessage = e => r(e.data);
});
assert_true(activeElementUpdated);
} finally {
document.body.removeChild(portal);
}
}, "test that a portal's x-origin subframe becomes active element on focus");

promise_test(async t => {
let win = await openBlankPortalHost();
let doc = win.document;
Expand Down Expand Up @@ -87,6 +113,28 @@
}
}, "test that a x-origin iframe inside an adopted portal cannot steal focus");

promise_test(async () => {
let win = await openBlankPortalHost();
let doc = win.document;
try {
let portal = await createPortal(doc, new URL("resources/focus-page-with-autofocus.html", location.href));
portal.postMessage('check-active-element');
let result = await new Promise(r => {
portal.onmessage = e => r(e.data);
});
assert_true(result, "autofocused element is active element");

await portal.activate();
win.portalHost.postMessage('check-active-element');
result = await new Promise(r => {
win.portalHost.onmessage = e => r(e.data)
});
assert_true(result, "autofocused element is still active element");
} finally {
win.close();
}
}, "test that autofocus inside a portal works");

const TAB = "\ue004"; // https://w3c.github.io/webdriver/#keyboard-actions
const SPACE = " "
const RETURN = "\r";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<body>
<button id="one">one</button>
<button id="two" autofocus>two</button>
<button id="three">three</button>
<script>
function messageHandler(e) {
if (e.data === 'check-active-element') {
let autofocusedButton = document.querySelector('#two');
e.source.postMessage(document.activeElement === autofocusedButton);
}
}

window.portalHost.onmessage = messageHandler;
window.onportalactivate = e => {
let portal = e.adoptPredecessor();
portal.onmessage = messageHandler;
document.body.appendChild(portal);
}
</script>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
button.onfocus = () => e.source.postMessage({focused: true}, {targetOrigin: "*"});
button.focus();
}

if (e.data == "focus-update-active-element") {
let button = document.querySelector("button");
button.focus();
e.source.postMessage({activeElementUpdated: document.activeElement === button}, {targetOrigin: "*"});
}
}

if (window.portalHost)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<body>
<script>
function handleMessage(e) {
if (e.data == "focus") {
async function handleMessage(e) {
if (e.data == "focus" || e.data == "focus-update-active-element") {
let iframe = document.querySelector("iframe");
iframe.contentWindow.postMessage("focus", "*");
iframe.contentWindow.postMessage(e.data, "*");
}
}

Expand Down

0 comments on commit 5efa864

Please sign in to comment.