# HG changeset patch # User James Teh # Date 1521641333 14400 # Wed Mar 21 10:08:53 2018 -0400 # Node ID fc4b465ae5c1d8e9910c3e5815e3f4cb2f083637 # Parent e1bd2a0806123389100e53c448c1c45cc44fa47b 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 diff --git a/accessible/ipc/win/HandlerProvider.cpp b/accessible/ipc/win/HandlerProvider.cpp --- 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 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 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 enumVar; + *hr = mTargetUnk.get()->QueryInterface(IID_IEnumVARIANT, + getter_AddRefs(enumVar)); + if (FAILED(*hr)) { + return; + } + + auto rawChildren = MakeUnique(count); + *hr = enumVar->Next((ULONG)count, rawChildren.get(), aNChildren); + if (FAILED(*hr)) { + *aChildren = nullptr; + *aNChildren = 0; + return; + } + + *aChildren = static_cast(::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 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 diff --git a/accessible/ipc/win/HandlerProvider.h b/accessible/ipc/win/HandlerProvider.h --- 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 aCtrl); void GetAndSerializePayload(const MutexAutoLock&, NotNull 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 mRefCnt; Mutex mMutex; // Protects mSerializer const IID mTargetUnkIid; mscom::InterceptorTargetPtr mTargetUnk; // Constant, main thread only UniquePtr mSerializer; RefPtr mFastMarshalUnk; }; diff --git a/accessible/ipc/win/handler/HandlerData.idl b/accessible/ipc/win/handler/HandlerData.idl --- 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