Bug 1431256 part 1: Accessible HandlerProvider: Implement a method to optimally retrieve all children in a single call. r?MarcoZ draft
authorJames Teh <jteh@mozilla.com>
Wed, 21 Mar 2018 10:08:53 -0400
changeset 770833 1777f53a66414df1511da9f091e6096911ae9c98
parent 770611 7b55d395bb63165c20a9230f82b3b14da09bcd2d
child 770834 1116fddbd2a43a72f9c24843f98b40b68b6c5514
push id103514
push userbmo:jteh@mozilla.com
push dateWed, 21 Mar 2018 21:41:58 +0000
reviewersMarcoZ
bugs1431256
milestone61.0a1
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
accessible/ipc/win/HandlerProvider.cpp
accessible/ipc/win/HandlerProvider.h
accessible/ipc/win/handler/HandlerData.idl
--- 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