Bug 1898198: Fix RemoteAccessible::TakeFocus when the browser UI has focus. r=eeejay

Previously, this didn't move the focus at all in this case.

Differential Revision: https://phabricator.services.mozilla.com/D211314
This commit is contained in:
James Teh 2024-05-26 21:50:29 +00:00
parent ebd4094092
commit f0250b65b5
2 changed files with 90 additions and 1 deletions

View file

@ -19,6 +19,7 @@
#include "nsAccessibilityService.h"
#include "mozilla/Unused.h"
#include "nsAccUtils.h"
#include "nsFocusManager.h"
#include "nsTextEquivUtils.h"
#include "Pivot.h"
#include "Relation.h"
@ -1874,7 +1875,47 @@ bool RemoteAccessible::HasPrimaryAction() const {
return mCachedFields && mCachedFields->HasAttribute(CacheKey::PrimaryAction);
}
void RemoteAccessible::TakeFocus() const { Unused << mDoc->SendTakeFocus(mID); }
void RemoteAccessible::TakeFocus() const {
Unused << mDoc->SendTakeFocus(mID);
if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
auto* bp = static_cast<dom::BrowserParent*>(mDoc->Manager());
MOZ_ASSERT(bp);
dom::Element* owner = bp->GetOwnerElement();
if (fm->GetFocusedElement() == owner) {
// This remote document tree is already focused. We don't need to do
// anything else.
return;
}
}
// Otherwise, we need to focus the <browser> or <iframe> element embedding the
// remote document in the parent process. If `this` is in an OOP iframe, we
// first need to focus the embedder iframe (and any ancestor OOP iframes). If
// the parent process embedder element were already focused, that would happen
// automatically, but it isn't. We can't simply focus the parent process
// embedder element before calling mDoc->SendTakeFocus because that would
// cause the remote document to restore focus to the last focused element,
// which we don't want.
DocAccessibleParent* embeddedDoc = mDoc;
Accessible* embedder = mDoc->Parent();
while (embedder) {
MOZ_ASSERT(embedder->IsOuterDoc());
RemoteAccessible* embedderRemote = embedder->AsRemote();
if (!embedderRemote) {
// This is the element in the parent process which embeds the remote
// document.
embedder->TakeFocus();
break;
}
// This is a remote <iframe>.
if (embeddedDoc->IsTopLevelInContentProcess()) {
// We only need to focus OOP iframes because these are where we cross
// process boundaries.
Unused << embedderRemote->mDoc->SendTakeFocus(embedderRemote->mID);
}
embeddedDoc = embedderRemote->mDoc;
embedder = embeddedDoc->Parent();
}
}
void RemoteAccessible::ScrollTo(uint32_t aHow) const {
Unused << mDoc->SendScrollTo(mID, aHow);

View file

@ -71,3 +71,51 @@ addAccessibleTask(
},
{ topLevel: false, iframe: true, remoteIframe: true }
);
function focusURLBar() {
info("Focusing the URL bar");
const focused = waitForEvent(
EVENT_FOCUS,
event => event.accessible.role == ROLE_ENTRY
);
gURLBar.focus();
return focused;
}
/**
* Test takeFocus on web content when focus is in the browser UI.
*/
addAccessibleTask(
`
<button id="outerButton">outerButton</button>
<iframe src="data:text/html,<body id='innerDoc'><button id='innerButton'>innerButton</button>"></iframe>
`,
async function testFocusContentWhileUiFocused(browser, docAcc) {
await focusURLBar();
info("Focusing docAcc");
let focused = waitForEvent(EVENT_FOCUS, docAcc);
docAcc.takeFocus();
await focused;
await focusURLBar();
info("Focusing outerButton");
const outerButton = findAccessibleChildByID(docAcc, "outerButton");
focused = waitForEvent(EVENT_FOCUS, outerButton);
outerButton.takeFocus();
await focused;
await focusURLBar();
info("Focusing innerButton");
const innerButton = findAccessibleChildByID(docAcc, "outerButton");
focused = waitForEvent(EVENT_FOCUS, innerButton);
innerButton.takeFocus();
await focused;
await focusURLBar();
info("Focusing outerButton");
focused = waitForEvent(EVENT_FOCUS, outerButton);
outerButton.takeFocus();
await focused;
},
{ chrome: true, topLevel: true, iframe: true, remoteIframe: true }
);