Bug 1234121: Unify FocusedChild() in Accessible base class r=Jamie

Differential Revision: https://phabricator.services.mozilla.com/D154949
This commit is contained in:
Morgan Rae Reschenberg 2022-08-19 02:42:30 +00:00
parent 498f9328f9
commit 5e8dfcb2e3
25 changed files with 124 additions and 207 deletions

View File

@ -74,6 +74,16 @@ bool Accessible::IsBefore(const Accessible* aAcc) const {
return otherPos > 0;
}
Accessible* Accessible::FocusedChild() {
Accessible* doc = nsAccUtils::DocumentFor(this);
Accessible* child = doc->FocusedChild();
if (child && (child == this || child->Parent() == this)) {
return child;
}
return nullptr;
}
const nsRoleMapEntry* Accessible::ARIARoleMap() const {
return aria::GetRoleMapFromIndex(mRoleMapEntryIndex);
}

View File

@ -201,6 +201,11 @@ class Accessible {
virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild) = 0;
/**
* Return the focused child if any.
*/
virtual Accessible* FocusedChild();
/**
* Return ARIA role map if any.
*/

View File

@ -80,7 +80,7 @@ LocalAccessible* ApplicationAccessible::LocalChildAtPoint(
return nullptr;
}
LocalAccessible* ApplicationAccessible::FocusedChild() {
Accessible* ApplicationAccessible::FocusedChild() {
LocalAccessible* focus = FocusMgr()->FocusedAccessible();
if (focus && focus->LocalParent() == this) {
return focus;

View File

@ -48,7 +48,7 @@ class ApplicationAccessible : public AccessibleWrap {
virtual LocalAccessible* LocalChildAtPoint(
int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) override;
virtual LocalAccessible* FocusedChild() override;
virtual Accessible* FocusedChild() override;
// ActionAccessible
virtual KeyBinding AccessKey() const override;

View File

@ -47,8 +47,10 @@
#include "mozilla/HTMLEditor.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/a11y/DocAccessibleParent.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLSelectElement.h"
@ -264,10 +266,34 @@ void DocAccessible::ApplyARIAState(uint64_t* aState) const {
if (mParent) mParent->ApplyARIAState(aState);
}
LocalAccessible* DocAccessible::FocusedChild() {
Accessible* DocAccessible::FocusedChild() {
// Return an accessible for the current global focus, which does not have to
// be contained within the current document.
return FocusMgr()->FocusedAccessible();
if (Accessible* focusedAcc = FocusMgr()->FocusedAccessible()) {
return focusedAcc;
}
nsFocusManager* focusManagerDOM = nsFocusManager::GetFocusManager();
if (!focusManagerDOM) {
return nullptr;
}
if (!XRE_IsParentProcess()) {
// DocAccessibleParent's don't exist in the content
// process, so we can't return anything useful if this
// is the case.
return nullptr;
}
// If we call GetFocusedBrowsingContext from the chrome process
// it returns the BrowsingContext for the focused _window_, which
// is not helpful here. Instead use GetFocusedBrowsingContextInChrome
// which returns the content BrowsingContext that has focus.
dom::BrowsingContext* focusedContext =
focusManagerDOM->GetFocusedBrowsingContextInChrome();
DocAccessibleParent* focusedDoc =
DocAccessibleParent::GetFrom(focusedContext);
return focusedDoc ? focusedDoc->GetFocusedAcc() : nullptr;
}
void DocAccessible::TakeFocus() const {

View File

@ -68,7 +68,7 @@ class DocAccessible : public HyperTextAccessibleWrap,
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
virtual void Description(nsString& aDescription) const override;
virtual LocalAccessible* FocusedChild() override;
virtual Accessible* FocusedChild() override;
virtual mozilla::a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;
virtual uint64_t NativeInteractiveState() const override;

View File

@ -466,15 +466,6 @@ bool LocalAccessible::NativelyUnavailable() const {
nsGkAtoms::_true, eCaseMatters);
}
LocalAccessible* LocalAccessible::FocusedChild() {
LocalAccessible* focus = FocusMgr()->FocusedAccessible();
if (focus && (focus == this || focus->LocalParent() == this)) {
return focus;
}
return nullptr;
}
Accessible* LocalAccessible::ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild) {
Accessible* child = LocalChildAtPoint(aX, aY, aWhichChild);

View File

@ -255,11 +255,6 @@ class LocalAccessible : public nsISupports, public Accessible {
virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild) override;
/**
* Return the focused child if any.
*/
virtual LocalAccessible* FocusedChild();
virtual Relation RelationByType(RelationType aType) const override;
//////////////////////////////////////////////////////////////////////////////

View File

@ -15,6 +15,7 @@
#include "xpcAccEvents.h"
#include "nsAccUtils.h"
#include "TextRange.h"
#include "RootAccessible.h"
#if defined(XP_WIN)
# include "AccessibleWrap.h"
@ -22,7 +23,6 @@
# include "mozilla/mscom/PassthruProxy.h"
# include "mozilla/mscom/Ptr.h"
# include "nsWinUtils.h"
# include "RootAccessible.h"
#else
# include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
#endif
@ -1275,6 +1275,16 @@ void DocAccessibleParent::SelectionRanges(nsTArray<TextRange>* aRanges) const {
}
}
Accessible* DocAccessibleParent::FocusedChild() {
LocalAccessible* outerDoc = OuterDocOfRemoteBrowser();
if (!outerDoc) {
return nullptr;
}
RootAccessible* rootDocument = outerDoc->RootAccessible();
return rootDocument->FocusedChild();
}
void DocAccessibleParent::URL(nsAString& aURL) const {
if (!mBrowsingContext) {
return;

View File

@ -317,6 +317,8 @@ class DocAccessibleParent : public RemoteAccessible,
virtual void SelectionRanges(nsTArray<TextRange>* aRanges) const override;
virtual Accessible* FocusedChild() override;
void URL(nsAString& aURL) const;
// Tracks cached reverse relations (ie. those not set explicitly by an

View File

@ -187,7 +187,6 @@ double MaxValue() const override;
double Step() const override;
bool SetCurValue(double aValue);
RemoteAccessible* FocusedChild();
Accessible* ChildAtPoint(
int32_t aX, int32_t aY,
LocalAccessible::EWhichChildAtPoint aWhichChild) override;

View File

@ -1416,50 +1416,6 @@ mozilla::ipc::IPCResult DocAccessibleChild::RecvStep(const uint64_t& aID,
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessibleChild::RecvFocusedChild(
const uint64_t& aID, PDocAccessibleChild** aResultDoc,
uint64_t* aResultID) {
*aResultDoc = nullptr;
*aResultID = 0;
LocalAccessible* acc = IdToAccessible(aID);
if (!acc) {
return IPC_OK();
}
LocalAccessible* result = acc->FocusedChild();
if (result) {
// LocalAccessible::FocusedChild can return a LocalAccessible from any
// document, not just a descendant of the caller's document. Check that it
// is really a descendant.
DocAccessible* doc = result->Document();
if (!doc) {
MOZ_ASSERT_UNREACHABLE("Focused child is unbound from doc.");
return IPC_OK();
}
while (doc != mDoc) {
doc = doc->ParentDocument();
if (!doc) {
// result's document is not a descendant.
return IPC_OK();
}
}
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();
}
mozilla::ipc::IPCResult DocAccessibleChild::RecvLanguage(const uint64_t& aID,
nsString* aLocale) {
LocalAccessible* acc = IdToAccessible(aID);

View File

@ -417,10 +417,6 @@ class DocAccessibleChild : public DocAccessibleChildBase {
virtual mozilla::ipc::IPCResult RecvStep(const uint64_t& aID,
double* aStep) 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;
virtual mozilla::ipc::IPCResult RecvDocType(const uint64_t& aID,

View File

@ -330,8 +330,6 @@ child:
[Nested=inside_sync] sync Step(uint64_t aID) returns(double aStep);
async TakeFocus(uint64_t aID);
[Nested=inside_sync] sync FocusedChild(uint64_t aID)
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);

View File

@ -888,45 +888,6 @@ double RemoteAccessible::Step() const {
return step;
}
RemoteAccessible* RemoteAccessible::FocusedChild() {
if (IsOuterDoc()) {
// 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.
RemoteAccessible* child = RemoteFirstChild();
if (!child) {
return (State() & states::FOCUSED) ? this : nullptr;
}
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;
}
Accessible* RemoteAccessible::ChildAtPoint(
int32_t aX, int32_t aY, LocalAccessible::EWhichChildAtPoint aWhichChild) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {

View File

@ -227,33 +227,12 @@ static const uint64_t kCacheInitialized = ((uint64_t)0x1) << 63;
- (id)moxFocusedUIElement {
MOZ_ASSERT(mGeckoAccessible);
LocalAccessible* acc = mGeckoAccessible->AsLocal();
RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
mozAccessible* focusedChild = nil;
if (acc) {
LocalAccessible* focusedGeckoChild = acc->FocusedChild();
if (focusedGeckoChild) {
focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
} else {
dom::BrowserParent* browser = dom::BrowserParent::GetFocused();
if (browser) {
a11y::DocAccessibleParent* proxyDoc =
browser->GetTopLevelDocAccessible();
if (proxyDoc) {
mozAccessible* nativeRemoteChild =
GetNativeFromGeckoAccessible(proxyDoc);
return [nativeRemoteChild accessibilityFocusedUIElement];
}
}
}
} else if (proxy) {
RemoteAccessible* focusedGeckoChild = proxy->FocusedChild();
if (focusedGeckoChild) {
focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
}
}
// This only gets queried on the web area or the root group
// so just use the doc's focused child instead of trying to get
// the focused child of mGeckoAccessible.
Accessible* doc = nsAccUtils::DocumentFor(mGeckoAccessible);
mozAccessible* focusedChild =
GetNativeFromGeckoAccessible(doc->FocusedChild());
if ([focusedChild isAccessibilityElement]) {
return focusedChild;

View File

@ -19,6 +19,7 @@ addAccessibleTask(
contentDocAcc,
"default-iframe-body-id"
);
const root = getRootAccessible(document);
testStates(textbox, STATE_FOCUSABLE, 0, STATE_FOCUSED);
@ -28,11 +29,6 @@ addAccessibleTask(
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",
@ -42,7 +38,13 @@ addAccessibleTask(
is(
getAccessibleDOMNodeID(iframeDocAcc.focusedChild),
"textbox",
"correct focusedChild from child doc"
"correct focusedChild from iframe"
);
is(
getAccessibleDOMNodeID(root.focusedChild),
"textbox",
"correct focusedChild from root"
);
ok(!iframe.focusedChild, "correct focusedChild from iframe (null)");
@ -61,6 +63,11 @@ addAccessibleTask(
"default-iframe-body-id",
"correct focusedChild of child doc from iframe"
);
is(
getAccessibleDOMNodeID(root.focusedChild),
"default-iframe-body-id",
"correct focusedChild of child doc from root"
);
},
{ topLevel: false, iframe: true, remoteIframe: true }
);

View File

@ -1267,15 +1267,9 @@ MsaaAccessible::get_accFocus(
if (!mAcc) {
return CO_E_OBJNOTCONNECTED;
}
LocalAccessible* localAcc = LocalAcc();
if (!localAcc) {
return E_NOTIMPL; // XXX Not supported for RemoteAccessible yet.
}
// Return the current IAccessible child that has focus
LocalAccessible* focusedAccessible = localAcc->FocusedChild();
if (focusedAccessible == localAcc) {
Accessible* focusedAccessible = mAcc->FocusedChild();
if (focusedAccessible == mAcc) {
pvarChild->vt = VT_I4;
pvarChild->lVal = CHILDID_SELF;
} else if (focusedAccessible) {

View File

@ -66,54 +66,48 @@ already_AddRefed<IUnknown> MsaaRootAccessible::GetInternalUnknown() {
////////////////////////////////////////////////////////////////////////////////
// MsaaRootAccessible
STDMETHODIMP
MsaaRootAccessible::get_accFocus(
/* [retval][out] */ VARIANT __RPC_FAR* pvarChild) {
HRESULT hr = MsaaDocAccessible::get_accFocus(pvarChild);
if (FAILED(hr) || pvarChild->vt != VT_EMPTY || !IsWin8OrLater()) {
// 1. We got a definite result (either failure or an accessible); or
// 2. This is Windows 7, where we don't want to retrieve the focus from a
// remote document because this causes mysterious intermittent crashes
// when we're called by UIA clients; see bug 1424505.
return hr;
MsaaRootAccessible::accNavigate(
/* [in] */ long navDir,
/* [optional][in] */ VARIANT varStart,
/* [retval][out] */ VARIANT __RPC_FAR* pvarEndUpAt) {
// Special handling for NAVRELATION_EMBEDS.
// When we only have a single process, this can be handled the same way as
// any other relation.
// However, for multi process, the normal relation mechanism doesn't work
// because it can't handle remote objects.
if (navDir != NAVRELATION_EMBEDS || varStart.vt != VT_I4 ||
varStart.lVal != CHILDID_SELF) {
// We only handle EMBEDS on the root here.
// Forward to the base implementation.
return MsaaDocAccessible::accNavigate(navDir, varStart, pvarEndUpAt);
}
// The base implementation reported no focus.
// Focus might be in a remote document.
// (The base implementation can't handle this.)
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
return S_FALSE;
if (!pvarEndUpAt) {
return E_INVALIDARG;
}
dom::BrowserParent* browser = dom::BrowserParent::GetFocused();
if (!browser) {
return hr;
RootAccessible* rootAcc = RootAcc();
if (!rootAcc) {
return CO_E_OBJNOTCONNECTED;
}
DocAccessibleParent* docProxy = browser->GetTopLevelDocAccessible();
if (!docProxy) {
return hr;
Accessible* target = nullptr;
// Get the document in the active tab.
RemoteAccessible* docProxy = rootAcc->GetPrimaryRemoteTopLevelContentDoc();
if (docProxy) {
target = docProxy;
} else {
// The base implementation could handle this, but we may as well
// just handle it here.
Relation rel = rootAcc->RelationByType(RelationType::EMBEDS);
target = rel.Next();
}
RefPtr<IDispatch> docDisp = already_AddRefed(NativeAccessible(docProxy));
if (!docDisp) {
if (!target) {
return E_FAIL;
}
RefPtr<IAccessible> docIa;
hr = docDisp->QueryInterface(IID_IAccessible, (void**)getter_AddRefs(docIa));
MOZ_ASSERT(SUCCEEDED(hr));
MOZ_ASSERT(docIa);
// Ask this document for its focused descendant.
// We return this as is to the client except for CHILDID_SELF (see below).
hr = docIa->get_accFocus(pvarChild);
if (FAILED(hr)) {
return hr;
}
if (pvarChild->vt == VT_I4 && pvarChild->lVal == CHILDID_SELF) {
// The document itself has focus.
// We're handling a call to accFocus on the root accessible,
// so replace CHILDID_SELF with the document accessible.
pvarChild->vt = VT_DISPATCH;
docDisp.forget(&pvarChild->pdispVal);
}
VariantInit(pvarEndUpAt);
pvarEndUpAt->pdispVal = NativeAccessible(target);
pvarEndUpAt->vt = VT_DISPATCH;
return S_OK;
}

View File

@ -35,8 +35,10 @@ class MsaaRootAccessible : public MsaaDocAccessible {
*/
already_AddRefed<IUnknown> GetInternalUnknown();
virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accFocus(
/* [retval][out] */ VARIANT __RPC_FAR* pvarChild) override;
virtual /* [id] */ HRESULT STDMETHODCALLTYPE accNavigate(
/* [in] */ long navDir,
/* [optional][in] */ VARIANT varStart,
/* [retval][out] */ VARIANT __RPC_FAR* pvarEndUpAt) override;
private:
// DECLARE_AGGREGATABLE declares the internal IUnknown methods as well as

View File

@ -552,15 +552,7 @@ xpcAccessible::GetFocusedChild(nsIAccessible** aChild) {
if (!IntlGeneric()) return NS_ERROR_FAILURE;
if (RemoteAccessible* proxy = IntlGeneric()->AsRemote()) {
#if defined(XP_WIN)
return NS_ERROR_NOT_IMPLEMENTED;
#else
NS_IF_ADDREF(*aChild = ToXPC(proxy->FocusedChild()));
#endif
} else {
NS_IF_ADDREF(*aChild = ToXPC(Intl()->FocusedChild()));
}
NS_IF_ADDREF(*aChild = ToXPC(IntlGeneric()->FocusedChild()));
return NS_OK;
}

View File

@ -612,7 +612,7 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase,
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: LocalAccessible
LocalAccessible* XULTreeItemAccessibleBase::FocusedChild() {
Accessible* XULTreeItemAccessibleBase::FocusedChild() {
return FocusMgr()->FocusedAccessible() == this ? this : nullptr;
}

View File

@ -152,7 +152,7 @@ class XULTreeItemAccessibleBase : public AccessibleWrap {
virtual uint64_t NativeInteractiveState() const override;
virtual int32_t IndexInParent() const override;
virtual Relation RelationByType(RelationType aType) const override;
virtual LocalAccessible* FocusedChild() override;
virtual Accessible* FocusedChild() override;
virtual void SetSelected(bool aSelect) override;
virtual void TakeFocus() const override;

View File

@ -386,7 +386,7 @@ void XULTreeGridCellAccessible::Shutdown() {
LeafAccessible::Shutdown();
}
LocalAccessible* XULTreeGridCellAccessible::FocusedChild() { return nullptr; }
Accessible* XULTreeGridCellAccessible::FocusedChild() { return nullptr; }
ENameValueFlag XULTreeGridCellAccessible::Name(nsString& aName) const {
aName.Truncate();

View File

@ -125,7 +125,7 @@ class XULTreeGridCellAccessible : public LeafAccessible,
virtual nsRect BoundsInAppUnits() const override;
virtual nsIntRect BoundsInCSSPixels() const override;
virtual ENameValueFlag Name(nsString& aName) const override;
virtual LocalAccessible* FocusedChild() override;
virtual Accessible* FocusedChild() override;
virtual already_AddRefed<AccAttributes> NativeAttributes() override;
virtual int32_t IndexInParent() const override;
virtual Relation RelationByType(RelationType aType) const override;