Bug 1419362 part 3: Accessible HandlerProvider: Implement a method to retrieve all text, hyperlinks and attributes at once. r?MarcoZ, r?aklotz draft
authorJames Teh <jteh@mozilla.com>
Wed, 22 Nov 2017 16:08:11 +1000
changeset 703076 6ca36ba4e39e48f8a2cc48f7c02cc48f161c5096
parent 703075 1603b89a53d7089c8cbe009e5c306474becad266
child 703077 c55ed6628bcd7283ad296d95c9a829ec6909b7a7
push id90698
push userbmo:jteh@mozilla.com
push dateFri, 24 Nov 2017 09:13:42 +0000
reviewersMarcoZ, aklotz
bugs1419362
milestone59.0a1
Bug 1419362 part 3: Accessible HandlerProvider: Implement a method to retrieve all text, hyperlinks and attributes at once. r?MarcoZ, r?aklotz This allows the handler to fetch all text information in a single cross-process call when appropriate. Normally, it would be a minimum of 3 calls, plus one call for each additional attribute run. MozReview-Commit-ID: K5x9bAWiWWJ
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
@@ -17,21 +17,23 @@
 #include "HandlerData_i.c"
 #include "mozilla/Assertions.h"
 #include "mozilla/a11y/AccessibleWrap.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/Move.h"
 #include "mozilla/mscom/AgileReference.h"
 #include "mozilla/mscom/FastMarshaler.h"
 #include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/MainThreadHandoff.h"
 #include "mozilla/mscom/MainThreadInvoker.h"
 #include "mozilla/mscom/Ptr.h"
 #include "mozilla/mscom/StructStream.h"
 #include "mozilla/mscom/Utils.h"
 #include "nsThreadUtils.h"
+#include "nsTArray.h"
 
 #include <memory.h>
 
 namespace mozilla {
 namespace a11y {
 
 HandlerProvider::HandlerProvider(REFIID aIid,
                                mscom::InterceptorTargetPtr<IUnknown> aTarget)
@@ -562,11 +564,123 @@ HandlerProvider::Refresh(DynamicIA2Data*
                                  this, &HandlerProvider::BuildDynamicIA2Data,
                                  aOutData)) {
     return E_FAIL;
   }
 
   return S_OK;
 }
 
+template<typename Interface>
+HRESULT
+HandlerProvider::ToWrappedObject(Interface** aObj)
+{
+  mscom::STAUniquePtr<Interface> inObj(*aObj);
+  RefPtr<HandlerProvider> hprov = new HandlerProvider(__uuidof(Interface),
+    mscom::ToInterceptorTargetPtr(inObj));
+  HRESULT hr = mscom::MainThreadHandoff::WrapInterface(Move(inObj), hprov,
+                                                       aObj);
+  if (FAILED(hr)) {
+    *aObj = nullptr;
+  }
+  return hr;
+}
+
+void
+HandlerProvider::GetAllTextInfoMainThread(BSTR* aText,
+                                          IAccessibleHyperlink*** aHyperlinks,
+                                          long* aNHyperlinks,
+                                          IA2TextSegment** aAttribRuns,
+                                          long* aNAttribRuns, HRESULT* result)
+{
+  MOZ_ASSERT(aText);
+  MOZ_ASSERT(aHyperlinks);
+  MOZ_ASSERT(aNHyperlinks);
+  MOZ_ASSERT(aAttribRuns);
+  MOZ_ASSERT(aNAttribRuns);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mTargetUnk);
+
+  RefPtr<IAccessibleHypertext2> ht;
+  HRESULT hr = mTargetUnk->QueryInterface(IID_IAccessibleHypertext2,
+                                          getter_AddRefs(ht));
+  if (FAILED(hr)) {
+    *result = hr;
+    return;
+  }
+
+  hr = ht->get_text(0, IA2_TEXT_OFFSET_LENGTH, aText);
+  if (FAILED(hr)) {
+    *result = hr;
+    return;
+  }
+
+  if (hr == S_FALSE) {
+    // No text.
+    *aHyperlinks = nullptr;
+    *aNHyperlinks = 0;
+    *aAttribRuns = nullptr;
+    *aNAttribRuns = 0;
+    *result = S_FALSE;
+    return;
+  }
+
+  hr = ht->get_hyperlinks(aHyperlinks, aNHyperlinks);
+  if (FAILED(hr)) {
+    *aHyperlinks = nullptr;
+    // -1 signals to the handler that it should call hyperlinks itself.
+    *aNHyperlinks = -1;
+  }
+  // We must wrap these hyperlinks in an interceptor.
+  for (long index = 0; index < *aNHyperlinks; ++index) {
+    ToWrappedObject(&(*aHyperlinks)[index]);
+  }
+
+  // Fetch all attribute runs.
+  nsTArray<IA2TextSegment> attribRuns;
+  long end = 0;
+  long length = ::SysStringLen(*aText);
+  while (end < length) {
+    long start;
+    BSTR attribs;
+    // The (exclusive) end of the last run is the start of the next run.
+    hr = ht->get_attributes(end, &start, &end, &attribs);
+    if (FAILED(hr)) {
+      break;
+    }
+    attribRuns.AppendElement(IA2TextSegment({attribs, start, end}));
+  }
+
+  // Put the attribute runs in a COM array.
+  *aNAttribRuns = attribRuns.Length();
+  *aAttribRuns = static_cast<IA2TextSegment*>(::CoTaskMemAlloc(
+    sizeof(IA2TextSegment) * *aNAttribRuns));
+  for (long index = 0; index < *aNAttribRuns; ++index) {
+    (*aAttribRuns)[index] = attribRuns[index];
+  }
+
+  *result = S_OK;
+}
+
+HRESULT
+HandlerProvider::get_AllTextInfo(BSTR* aText,
+                                 IAccessibleHyperlink*** aHyperlinks,
+                                 long* aNHyperlinks,
+                                 IA2TextSegment** aAttribRuns,
+                                 long* aNAttribRuns)
+{
+  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+  HRESULT hr;
+  if (!mscom::InvokeOnMainThread("HandlerProvider::GetAllTextInfoMainThread",
+                                 this,
+                                 &HandlerProvider::GetAllTextInfoMainThread,
+                                 aText, aHyperlinks, aNHyperlinks,
+                                 aAttribRuns, aNAttribRuns, &hr)) {
+    return E_FAIL;
+  }
+
+  return hr;
+}
+
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -50,16 +50,21 @@ public:
                                                 ULONG aCallMethod) override;
   STDMETHODIMP NewInstance(REFIID aIid,
                            mscom::InterceptorTargetPtr<IUnknown> aTarget,
                            NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
 
   // IGeckoBackChannel
   STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
   STDMETHODIMP Refresh(DynamicIA2Data* aOutData) override;
+  STDMETHODIMP get_AllTextInfo(BSTR* aText,
+                               IAccessibleHyperlink*** aHyperlinks,
+                               long* aNHyperlinks,
+                               IA2TextSegment** aAttribRuns,
+                               long* aNAttribRuns) override;
 
 private:
   ~HandlerProvider() = default;
 
   void SetHandlerControlOnMainThread(DWORD aPid,
                                      mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
   void GetAndSerializePayload(const MutexAutoLock&,
                               NotNull<mscom::IInterceptor*> aInterceptor);
@@ -67,16 +72,28 @@ private:
                           StaticIA2Data* aOutData);
   void BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data);
   void BuildInitialIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
                            StaticIA2Data* aOutStaticData,
                            DynamicIA2Data* aOutDynamicData);
   static void CleanupStaticIA2Data(StaticIA2Data& aData);
   static void CleanupDynamicIA2Data(DynamicIA2Data& aData);
   bool IsTargetInterfaceCacheable();
+  // Replace a raw object from the main thread with a wrapped, intercepted
+  // object suitable for calling from the MTA.
+  // The reference to the original object is adopted; i.e. you should not
+  // separately release it.
+  // This is intended for objects returned from method calls on the main thread.
+  template<typename Interface> HRESULT ToWrappedObject(Interface** aObj);
+  void GetAllTextInfoMainThread(BSTR* aText,
+                                IAccessibleHyperlink*** aHyperlinks,
+                                long* aNHyperlinks,
+                                IA2TextSegment** aAttribRuns,
+                                long* aNAttribRuns,
+                                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
@@ -147,16 +147,21 @@ interface IHandlerControl : IUnknown
 
 [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);
 }
 
 [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