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
--- a/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -756,11 +756,159 @@ HandlerProvider::get_RelationsInfo(IARel
&HandlerProvider::GetRelationsInfoMainThread,
aRelations, aNRelations, &hr)) {
return E_FAIL;
}
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
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -59,16 +59,18 @@ public:
STDMETHODIMP Refresh(DynamicIA2Data* aOutData) override;
STDMETHODIMP get_AllTextInfo(BSTR* aText,
IAccessibleHyperlink*** aHyperlinks,
long* aNHyperlinks,
IA2TextSegment** aAttribRuns,
long* aNAttribRuns) override;
STDMETHODIMP get_RelationsInfo(IARelationData** aRelations,
long* aNRelations) override;
+ STDMETHODIMP get_AllChildren(AccChildData** aChildren,
+ ULONG* aNChildren) override;
private:
~HandlerProvider() = default;
void SetHandlerControlOnMainThread(DWORD aPid,
mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
void GetAndSerializePayload(const MutexAutoLock&,
NotNull<mscom::IInterceptor*> aInterceptor);
@@ -90,16 +92,18 @@ private:
IAccessibleHyperlink*** aHyperlinks,
long* aNHyperlinks,
IA2TextSegment** aAttribRuns,
long* aNAttribRuns,
HRESULT* result);
void GetRelationsInfoMainThread(IARelationData** aRelations,
long* aNRelations,
HRESULT* result);
+ void GetAllChildrenMainThread(AccChildData** aChildren, ULONG* aNChildren,
+ HRESULT* result);
Atomic<uint32_t> mRefCnt;
Mutex mMutex; // Protects mSerializer
const IID mTargetUnkIid;
mscom::InterceptorTargetPtr<IUnknown> mTargetUnk; // Constant, main thread only
UniquePtr<mscom::StructToStream> mSerializer;
RefPtr<IUnknown> mFastMarshalUnk;
};
--- a/accessible/ipc/win/handler/HandlerData.idl
+++ b/accessible/ipc/win/handler/HandlerData.idl
@@ -146,31 +146,47 @@ interface IHandlerControl : IUnknown
}
typedef struct _IARelationData
{
BSTR mType;
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)]
interface IGeckoBackChannel : IUnknown
{
[propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl);
HRESULT Refresh([out] DynamicIA2Data* aOutData);
[propget] HRESULT AllTextInfo([out] BSTR* aText,
[out, size_is(,*aNHyperlinks)] IAccessibleHyperlink*** aHyperlinks,
[out] long* aNHyperlinks,
[out, size_is(,*aNAttribRuns)] IA2TextSegment** aAttribRuns,
[out] long* aNAttribRuns);
[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)]
library AccessibleHandlerTypeLib
{
/**
* This definition is required in order for the handler implementation to
* support IDispatch (aka Automation). This is used by interpreted language