Bug 1688972 - Make FocusedChild work across document/process boundaries. r=Jamie

Differential Revision: https://phabricator.services.mozilla.com/D103102
This commit is contained in:
Eitan Isaacson 2021-02-01 17:54:23 +00:00
Родитель db6f70d5f5
Коммит d3876360b5
5 изменённых файлов: 102 добавлений и 17 удалений

Просмотреть файл

@ -1524,15 +1524,30 @@ mozilla::ipc::IPCResult DocAccessibleChild::RecvTakeFocus(const uint64_t& aID) {
}
mozilla::ipc::IPCResult DocAccessibleChild::RecvFocusedChild(
const uint64_t& aID, uint64_t* aChild, bool* aOk) {
*aChild = 0;
*aOk = false;
const uint64_t& aID, PDocAccessibleChild** aResultDoc,
uint64_t* aResultID) {
*aResultDoc = nullptr;
*aResultID = 0;
Accessible* acc = IdToAccessible(aID);
if (acc) {
Accessible* child = acc->FocusedChild();
if (child) {
*aChild = reinterpret_cast<uint64_t>(child->UniqueID());
*aOk = true;
if (!acc) {
return IPC_OK();
}
Accessible* result = acc->FocusedChild();
if (result) {
// Accessible::FocusedChild can return an Accessible from a descendant
// document.
DocAccessibleChild* resultDoc = result->Document()->IPCDoc();
// We've sent the constructor for this document to the parent process.
// However, because the constructor is async, the parent process might
// get the result of this (sync) method before it runs the constructor.
// If we send this document in this case, the parent process will crash.
// Therefore, we only do this if the parent process has explicitly told
// us that the document has been constructed there.
if (resultDoc && resultDoc->IsConstructedInParentProcess()) {
*aResultDoc = resultDoc;
*aResultID =
result->IsDoc() ? 0 : reinterpret_cast<uint64_t>(result->UniqueID());
}
}
return IPC_OK();

Просмотреть файл

@ -442,9 +442,9 @@ class DocAccessibleChild : public DocAccessibleChildBase {
virtual mozilla::ipc::IPCResult RecvTakeFocus(const uint64_t& aID) override;
virtual mozilla::ipc::IPCResult RecvFocusedChild(const uint64_t& aID,
uint64_t* aChild,
bool* aOk) override;
virtual mozilla::ipc::IPCResult RecvFocusedChild(
const uint64_t& aID, PDocAccessibleChild** aResultDoc,
uint64_t* aResultID) override;
virtual mozilla::ipc::IPCResult RecvLanguage(const uint64_t& aID,
nsString* aLocale) override;

Просмотреть файл

@ -310,7 +310,7 @@ child:
async TakeFocus(uint64_t aID);
nested(inside_sync) sync FocusedChild(uint64_t aID)
returns(uint64_t aChild, bool aOk);
returns(nullable PDocAccessible aResultDoc, uint64_t aResultID);
nested(inside_sync) sync Language(uint64_t aID) returns(nsString aLocale);
nested(inside_sync) sync DocType(uint64_t aID) returns(nsString aType);

Просмотреть файл

@ -762,10 +762,40 @@ double ProxyAccessible::Step() {
void ProxyAccessible::TakeFocus() { Unused << mDoc->SendTakeFocus(mID); }
ProxyAccessible* ProxyAccessible::FocusedChild() {
uint64_t childID = 0;
bool ok = false;
Unused << mDoc->SendFocusedChild(mID, &childID, &ok);
return ok ? mDoc->GetAccessible(childID) : nullptr;
if (mOuterDoc) {
// If FocusedChild was called on an outer doc, it should behave
// like a non-doc accessible and return its focused child, or null.
// If the inner doc is OOP (fission), calling FocusedChild on the outer
// doc would return null.
MOZ_ASSERT(ChildrenCount() == 1);
ProxyAccessible* child = FirstChild();
MOZ_ASSERT(child->IsDoc());
return (child->State() & states::FOCUSED) ? child : nullptr;
}
auto* doc = mDoc;
uint64_t id = mID;
if (IsDoc()) {
// If this is a doc we should return the focused descendant, not just the
// direct child. In order to do that, we need to get a doc that is in
// the same process as the focused accessible. So we need the focused doc.
if (dom::BrowserParent* browser = dom::BrowserParent::GetFocused()) {
if (auto* focusedDoc = browser->GetTopLevelDocAccessible()) {
if (!focusedDoc->IsTopLevel()) {
// Redirect SendFocusedChild to OOP iframe doc.
doc = focusedDoc;
}
}
}
}
PDocAccessibleParent* resultDoc = nullptr;
uint64_t resultID = 0;
Unused << doc->SendFocusedChild(id, &resultDoc, &resultID);
auto* useDoc = static_cast<DocAccessibleParent*>(resultDoc);
// If useDoc is null, this means there is no focused child.
return useDoc ? useDoc->GetAccessible(resultID) : nullptr;
}
ProxyAccessible* ProxyAccessible::ChildAtPoint(

Просмотреть файл

@ -11,9 +11,15 @@ loadScripts(
);
addAccessibleTask(
`<input id="textbox" value="hello"/>`,
`<div role="group"><input id="textbox" value="hello"/></div>`,
async function(browser, iframeDocAcc, contentDocAcc) {
const textbox = findAccessibleChildByID(iframeDocAcc, "textbox");
const iframe = findAccessibleChildByID(contentDocAcc, "default-iframe-id");
const iframeDoc = findAccessibleChildByID(
contentDocAcc,
"default-iframe-body-id"
);
testStates(textbox, STATE_FOCUSABLE, 0, STATE_FOCUSED);
let onFocus = waitForEvent(EVENT_FOCUS, textbox);
@ -21,6 +27,40 @@ addAccessibleTask(
await onFocus;
testStates(textbox, STATE_FOCUSABLE | STATE_FOCUSED, 0);
if (Services.appinfo.OS == "WINNT") {
// focusedChild XPCOM method not implemented in windows.
return;
}
is(
getAccessibleDOMNodeID(contentDocAcc.focusedChild),
"textbox",
"correct focusedChild from top doc"
);
is(
getAccessibleDOMNodeID(iframeDocAcc.focusedChild),
"textbox",
"correct focusedChild from child doc"
);
ok(!iframe.focusedChild, "correct focusedChild from iframe (null)");
onFocus = waitForEvent(EVENT_FOCUS, iframeDoc);
iframeDoc.takeFocus();
await onFocus;
is(
getAccessibleDOMNodeID(contentDocAcc.focusedChild),
"default-iframe-body-id",
"correct focusedChild of child doc from top doc"
);
is(
getAccessibleDOMNodeID(iframe.focusedChild),
"default-iframe-body-id",
"correct focusedChild of child doc from iframe"
);
},
{ topLevel: false, iframe: true, remoteIframe: true }
);