Bug 1431256 part 1: Accessible HandlerProvider: Implement a method to optimally retrieve all children in a single call. r=MarcoZ

When considering a large document, a huge number of the children we return are text leaf nodes.
Marshaling full objects is expensive, but for text leaf nodes, the client is only interested in the text and a few other pieces of information.
Therefore, rather than returning the full object for text leaf accessibles, we just return the text and other necessary information.
For other non-text children, we return the full object as usual.

In addition, clients normally use the IEnumVARIANT interface to retrieve children in a single call.
However, it doesn't allow you to specify a starting index.
Therefore, you must first call the Reset method to reset the starting point to 0.
Practically, this means an extra cross-process call whenever the caller fetches children.
When dealing with a large document, this can be a significant number of wasted calls.
This new method retrieves all children always starting at the first using a single call.

MozReview-Commit-ID: A9lc7BBTWdb

--HG--
extra : rebase_source : d50507c182ab7760c17c5e7bb9956f46a3dc188c
This commit is contained in:
James Teh 2018-03-21 10:08:53 -04:00
parent 62b7a93603
commit 39c71764d7
3 changed files with 168 additions and 0 deletions

View File

@ -761,6 +761,154 @@ HandlerProvider::get_RelationsInfo(IARelationData** aRelations,
return hr;
}
// Helper function for GetAllChildrenMainThread.
static bool
SetChildDataForTextLeaf(NEWEST_IA2_INTERFACE* acc, AccChildData& data)
{
const VARIANT kChildIdSelf = {VT_I4};
VARIANT varVal;
// 1. Check whether this is a text leaf.
// 1.1. A text leaf always has ROLE_SYSTEM_TEXT or ROLE_SYSTEM_WHITESPACE.
HRESULT hr = acc->get_accRole(kChildIdSelf, &varVal);
if (FAILED(hr)) {
return false;
}
if (varVal.vt != VT_I4) {
return false;
}
long role = varVal.lVal;
if (role != ROLE_SYSTEM_TEXT && role != ROLE_SYSTEM_WHITESPACE) {
return false;
}
// 1.2. A text leaf doesn't support IAccessibleText.
RefPtr<IAccessibleText> iaText;
hr = acc->QueryInterface(IID_IAccessibleText, getter_AddRefs(iaText));
if (SUCCEEDED(hr)) {
return false;
}
// 1.3. A text leaf doesn't have children.
long count;
hr = acc->get_accChildCount(&count);
if (FAILED(hr) || count != 0) {
return false;
}
// 2. Update |data| with the data for this text leaf.
// Because marshaling objects is more expensive than marshaling other data,
// we just marshal the data we need for text leaf children, rather than
// marshaling the full accessible object.
// |data| has already been zeroed, so we don't need to do anything if these
// calls fail.
acc->get_accName(kChildIdSelf, &data.mText);
data.mTextRole = role;
acc->get_uniqueID(&data.mTextId);
acc->get_accState(kChildIdSelf, &varVal);
data.mTextState = varVal.lVal;
acc->accLocation(&data.mTextLeft, &data.mTextTop, &data.mTextWidth,
&data.mTextHeight, kChildIdSelf);
return true;
}
void
HandlerProvider::GetAllChildrenMainThread(AccChildData** aChildren,
ULONG* aNChildren,
HRESULT* hr)
{
MOZ_ASSERT(aChildren);
MOZ_ASSERT(aNChildren);
MOZ_ASSERT(NS_IsMainThread());
if (!mTargetUnk) {
*hr = CO_E_OBJNOTCONNECTED;
return;
}
RefPtr<NEWEST_IA2_INTERFACE> acc;
*hr = mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID,
getter_AddRefs(acc));
if (FAILED(*hr)) {
return;
}
long count;
*hr = acc->get_accChildCount(&count);
if (FAILED(*hr)) {
return;
}
MOZ_ASSERT(count >= 0);
if (count == 0) {
*aChildren = nullptr;
*aNChildren = 0;
return;
}
RefPtr<IEnumVARIANT> enumVar;
*hr = mTargetUnk.get()->QueryInterface(IID_IEnumVARIANT,
getter_AddRefs(enumVar));
if (FAILED(*hr)) {
return;
}
auto rawChildren = MakeUnique<VARIANT[]>(count);
*hr = enumVar->Next((ULONG)count, rawChildren.get(), aNChildren);
if (FAILED(*hr)) {
*aChildren = nullptr;
*aNChildren = 0;
return;
}
*aChildren = static_cast<AccChildData*>(::CoTaskMemAlloc(
sizeof(AccChildData) * *aNChildren));
for (ULONG index = 0; index < *aNChildren; ++index) {
(*aChildren)[index] = {};
AccChildData& child = (*aChildren)[index];
MOZ_ASSERT(rawChildren[index].vt == VT_DISPATCH);
MOZ_ASSERT(rawChildren[index].pdispVal);
RefPtr<NEWEST_IA2_INTERFACE> childAcc;
*hr = rawChildren[index].pdispVal->QueryInterface(NEWEST_IA2_IID,
getter_AddRefs(childAcc));
rawChildren[index].pdispVal->Release();
MOZ_ASSERT(SUCCEEDED(*hr));
if (FAILED(*hr)) {
continue;
}
if (!SetChildDataForTextLeaf(childAcc, child)) {
// This isn't a text leaf. Marshal the accessible.
childAcc.forget(&child.mAccessible);
// We must wrap this accessible in an Interceptor.
ToWrappedObject(&child.mAccessible);
}
}
*hr = S_OK;
}
HRESULT
HandlerProvider::get_AllChildren(AccChildData** aChildren,
ULONG* aNChildren)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
HRESULT hr;
if (!mscom::InvokeOnMainThread("HandlerProvider::GetAllChildrenMainThread",
this,
&HandlerProvider::GetAllChildrenMainThread,
aChildren, aNChildren, &hr)) {
return E_FAIL;
}
return hr;
}
} // namespace a11y
} // namespace mozilla

View File

@ -64,6 +64,8 @@ public:
long* aNAttribRuns) override;
STDMETHODIMP get_RelationsInfo(IARelationData** aRelations,
long* aNRelations) override;
STDMETHODIMP get_AllChildren(AccChildData** aChildren,
ULONG* aNChildren) override;
private:
~HandlerProvider() = default;
@ -95,6 +97,8 @@ private:
void GetRelationsInfoMainThread(IARelationData** aRelations,
long* aNRelations,
HRESULT* result);
void GetAllChildrenMainThread(AccChildData** aChildren, ULONG* aNChildren,
HRESULT* result);
Atomic<uint32_t> mRefCnt;
Mutex mMutex; // Protects mSerializer

View File

@ -151,6 +151,19 @@ typedef struct _IARelationData
long mNTargets;
} IARelationData;
typedef struct _AccChildData
{
NEWEST_IA2_INTERFACE* mAccessible;
BSTR mText;
long mTextRole;
long mTextId;
long mTextState;
long mTextLeft;
long mTextTop;
long mTextWidth;
long mTextHeight;
} AccChildData;
[object,
uuid(IGECKOBACKCHANNEL_IID),
pointer_default(unique)]
@ -166,6 +179,9 @@ interface IGeckoBackChannel : IUnknown
[propget] HRESULT RelationsInfo(
[out, size_is(,*aNRelations)] IARelationData** aRelations,
[out] long* aNRelations);
[propget] HRESULT AllChildren(
[out, size_is(,*aNChildren)] AccChildData** aChildren,
[out] ULONG* aNChildren);
}
[uuid(1e545f07-f108-4912-9471-546827a80983)]