Bug 1409928: Use e10s a11y handler cache for accessibles retrieved via IAccessibleHypertext. r?aklotz draft
authorJames Teh <jteh@mozilla.com>
Fri, 13 Oct 2017 17:01:20 +1000
changeset 688017 e7f96dfcd92da1700df1c018cde42f4328890f47
parent 687940 d58424c244c38f88357a26fb61c333d3c6e552d7
child 737776 67440c26cc3932fe469638da39224e05690d84ac
push id86644
push userbmo:jteh@mozilla.com
push dateSat, 28 Oct 2017 00:34:34 +0000
reviewersaklotz
bugs1409928
milestone58.0a1
Bug 1409928: Use e10s a11y handler cache for accessibles retrieved via IAccessibleHypertext. r?aklotz IAccessibleHypertext::hyperlink returns an IAccessibleHyperlink, not an IAccessible2. previously, the handler didn't know about this interface, so it wasn't used. Thus, any accessibles retrieved in this way did not benefit from the cache. This patch teaches the handler about IAccessibleHyperlink so the handler gets used in this case. MozReview-Commit-ID: 17CxxGyCLrE
accessible/ipc/win/HandlerProvider.cpp
accessible/ipc/win/handler/AccessibleHandler.cpp
accessible/ipc/win/handler/AccessibleHandler.h
--- a/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -182,20 +182,24 @@ private:
 void
 HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
 {
   MOZ_ASSERT(aOutIA2Data);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mTargetUnk);
   MOZ_ASSERT(IsTargetInterfaceCacheable());
 
-  RefPtr<NEWEST_IA2_INTERFACE>
-    target(static_cast<NEWEST_IA2_INTERFACE*>(mTargetUnk.get()));
+  RefPtr<NEWEST_IA2_INTERFACE> target;
+  HRESULT hr = mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID,
+    getter_AddRefs(target));
+  if (FAILED(hr)) {
+    return;
+  }
 
-  HRESULT hr = E_UNEXPECTED;
+  hr = E_UNEXPECTED;
 
   auto hasFailed = [&hr]() -> bool {
     return FAILED(hr);
   };
 
   auto cleanup = [this, aOutIA2Data]() -> void {
     ClearIA2Data(*aOutIA2Data);
   };
@@ -289,17 +293,18 @@ HandlerProvider::ClearIA2Data(IA2Data& a
 {
   ::VariantClear(&aData.mRole);
   ZeroMemory(&aData, sizeof(IA2Data));
 }
 
 bool
 HandlerProvider::IsTargetInterfaceCacheable()
 {
-  return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID;
+  return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID ||
+         mTargetUnkIid == IID_IAccessibleHyperlink;
 }
 
 HRESULT
 HandlerProvider::WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
                                      NotNull<IStream*> aStream)
 {
   MutexAutoLock lock(mMutex);
 
--- a/accessible/ipc/win/handler/AccessibleHandler.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -23,16 +23,17 @@
 #include <objbase.h>
 #include <uiautomation.h>
 #include <winreg.h>
 
 #include "AccessibleHypertext.h"
 #include "Accessible2_i.c"
 #include "Accessible2_2_i.c"
 #include "Accessible2_3_i.c"
+#include "AccessibleHyperlink_i.c"
 
 namespace mozilla {
 namespace a11y {
 
 static mscom::Factory<AccessibleHandler> sHandlerFactory;
 
 HRESULT
 AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface)
@@ -55,16 +56,17 @@ AccessibleHandler::Create(IUnknown* aOut
   return handler->InternalQueryInterface(aIid, aOutInterface);
 }
 
 AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
   : mscom::Handler(aOuter, aResult)
   , mDispatch(nullptr)
   , mIA2PassThru(nullptr)
   , mServProvPassThru(nullptr)
+  , mIAHyperlinkPassThru(nullptr)
   , mCachedData()
   , mCacheGen(0)
 {
   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
   MOZ_ASSERT(ctl);
   if (!ctl) {
     if (aResult) {
       *aResult = E_UNEXPECTED;
@@ -100,16 +102,39 @@ AccessibleHandler::ResolveIA2()
     // mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
     mIA2PassThru->Release();
   }
 
   return hr;
 }
 
 HRESULT
+AccessibleHandler::ResolveIAHyperlink()
+{
+  if (mIAHyperlinkPassThru) {
+    return S_OK;
+  }
+
+  RefPtr<IUnknown> proxy(GetProxy());
+  if (!proxy) {
+    return E_UNEXPECTED;
+  }
+
+  HRESULT hr = proxy->QueryInterface(IID_IAccessibleHyperlink,
+                                     reinterpret_cast<void**>(&mIAHyperlinkPassThru));
+  if (SUCCEEDED(hr)) {
+    // mIAHyperlinkPassThru is a weak reference
+    // (see comments in AccesssibleHandler.h)
+    mIAHyperlinkPassThru->Release();
+  }
+
+  return hr;
+}
+
+HRESULT
 AccessibleHandler::MaybeUpdateCachedData()
 {
   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
   if (!ctl) {
     return E_OUTOFMEMORY;
   }
 
   uint32_t gen = ctl->GetCacheGen();
@@ -387,16 +412,18 @@ CopyBSTR(BSTR aSrc)
     assignTo = mCachedData.mData.member; \
   }
 
 #define GET_BSTR(member, assignTo) \
   { \
     assignTo = CopyBSTR(mCachedData.mData.member); \
   }
 
+/*** IAccessible ***/
+
 HRESULT
 AccessibleHandler::get_accParent(IDispatch **ppdispParent)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
   return mIA2PassThru->get_accParent(ppdispParent);
@@ -704,16 +731,18 @@ AccessibleHandler::put_accName(VARIANT v
 
 HRESULT
 AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue)
 {
   // This matches AccessibleWrap
   return E_NOTIMPL;
 }
 
+/*** IAccessible2 ***/
+
 HRESULT
 AccessibleHandler::get_nRelations(long* nRelations)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
   return mIA2PassThru->get_nRelations(nRelations);
@@ -961,16 +990,18 @@ AccessibleHandler::get_attributes(BSTR* 
     return mIA2PassThru->get_attributes(attributes);
   }
 
   BEGIN_CACHE_ACCESS;
   GET_BSTR(mAttributes, *attributes);
   return S_OK;
 }
 
+/*** IAccessible2_2 ***/
+
 HRESULT
 AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute)
 {
   // Not yet implemented by ia2Accessible.
   // Once ia2Accessible implements this, we could either pass it through
   // or we could extract these individually from cached mAttributes.
   // The latter should be considered if traffic warrants it.
   return E_NOTIMPL;
@@ -995,16 +1026,18 @@ AccessibleHandler::get_relationTargetsOf
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
   return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
                                                  nTargets);
 }
 
+/*** IAccessible2_3 ***/
+
 HRESULT
 AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges)
 {
   HRESULT hr = ResolveIA2();
   if (FAILED(hr)) {
     return hr;
   }
   return mIA2PassThru->get_selectionRanges(ranges, nRanges);
@@ -1018,16 +1051,18 @@ static const GUID kUnsupportedServices[]
   // Unknown, queried by Windows
   {0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }},
   // Unknown, queried by Windows
   {0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }},
   // Unknown, queried by Windows
   {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }}
 };
 
+/*** IServiceProvider ***/
+
 HRESULT
 AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid,
                                 void** aOutInterface)
 {
   static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
                 "You have modified NEWEST_IA2_IID. This code needs updating.");
   /* We're taking advantage of the fact that we are implementing IA2 as part
      of our own object to implement this just like a QI. */
@@ -1059,27 +1094,147 @@ AccessibleHandler::QueryService(REFGUID 
     // mServProvPassThru is a weak reference (see comments in
     // AccessibleHandler.h)
     mServProvPassThru->Release();
   }
 
   return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
 }
 
+/*** IProvideClassInfo ***/
+
 HRESULT
 AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo)
 {
   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
   if (!ctl) {
     return E_OUTOFMEMORY;
   }
 
   return ctl->GetHandlerTypeInfo(aOutTypeInfo);
 }
 
+/*** IAccessibleAction ***/
+
+HRESULT
+AccessibleHandler::nActions(long* nActions)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->nActions(nActions);
+}
+
+HRESULT
+AccessibleHandler::doAction(long actionIndex)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->doAction(actionIndex);
+}
+
+HRESULT
+AccessibleHandler::get_description(long actionIndex, BSTR* description)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_description(actionIndex, description);
+}
+
+HRESULT
+AccessibleHandler::get_keyBinding(long actionIndex,
+                                  long nMaxBindings,
+                                  BSTR** keyBindings,
+                                  long* nBindings)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_keyBinding(
+    actionIndex, nMaxBindings, keyBindings, nBindings);
+}
+
+HRESULT
+AccessibleHandler::get_name(long actionIndex, BSTR* name)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_name(actionIndex, name);
+}
+
+HRESULT
+AccessibleHandler::get_localizedName(long actionIndex, BSTR* localizedName)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_localizedName(actionIndex, localizedName);
+}
+
+/*** IAccessibleHyperlink ***/
+
+HRESULT
+AccessibleHandler::get_anchor(long index, VARIANT* anchor)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_anchor(index, anchor);
+}
+
+HRESULT
+AccessibleHandler::get_anchorTarget(long index, VARIANT* anchorTarget)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_anchorTarget(index, anchorTarget);
+}
+
+HRESULT
+AccessibleHandler::get_startIndex(long* index)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_startIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_endIndex(long* index)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_endIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_valid(boolean* valid)
+{
+  HRESULT hr = ResolveIAHyperlink();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIAHyperlinkPassThru->get_valid(valid);
+}
+
 } // namespace a11y
 } // namespace mozilla
 
 extern "C" HRESULT __stdcall
 ProxyDllCanUnloadNow();
 
 extern "C" HRESULT __stdcall
 DllCanUnloadNow()
--- a/accessible/ipc/win/handler/AccessibleHandler.h
+++ b/accessible/ipc/win/handler/AccessibleHandler.h
@@ -30,30 +30,32 @@ import NEWEST_IA2_IDL;
 
 #include "HandlerData.h"
 
 #include <windows.h>
 
 #if !defined(MOZILLA_INTERNAL_API)
 
 #include "Accessible2_3.h"
+#include "AccessibleHyperlink.h"
 #include "Handler.h"
 #include "mozilla/mscom/StructStream.h"
 #include "mozilla/UniquePtr.h"
 
 #include <ocidl.h>
 #include <servprov.h>
 
 namespace mozilla {
 namespace a11y {
 
 class AccessibleHandler final : public mscom::Handler
                               , public NEWEST_IA2_INTERFACE
                               , public IServiceProvider
                               , public IProvideClassInfo
+                              , public IAccessibleHyperlink
 {
 public:
   static HRESULT Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface);
 
   // mscom::Handler
   HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
                                 void** aOutInterface) override;
   HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) override;
@@ -147,22 +149,42 @@ public:
 
   // IServiceProvider
   STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid,
                             void** aOutInterface) override;
 
   // IProvideClassInfo
   STDMETHODIMP GetClassInfo(ITypeInfo** aOutTypeInfo) override;
 
+  // IAccessibleAction
+  STDMETHODIMP nActions(long* nActions) override;
+  STDMETHODIMP doAction(long actionIndex) override;
+  STDMETHODIMP get_description(long actionIndex, BSTR* description) override;
+  STDMETHODIMP get_keyBinding(long actionIndex,
+                              long nMaxBindings,
+                              BSTR** keyBindings,
+                              long* nBindings) override;
+  STDMETHODIMP get_name(long actionIndex, BSTR* name) override;
+  STDMETHODIMP get_localizedName(long actionIndex,
+                                 BSTR* localizedName) override;
+
+  // IAccessibleHyperlink
+  STDMETHODIMP get_anchor(long index, VARIANT* anchor) override;
+  STDMETHODIMP get_anchorTarget(long index, VARIANT* anchorTarget) override;
+  STDMETHODIMP get_startIndex(long* index) override;
+  STDMETHODIMP get_endIndex(long* index) override;
+  STDMETHODIMP get_valid(boolean* valid) override;
+
 private:
   AccessibleHandler(IUnknown* aOuter, HRESULT* aResult);
   virtual ~AccessibleHandler();
 
   HRESULT ResolveIA2();
   HRESULT ResolveIDispatch();
+  HRESULT ResolveIAHyperlink();
   HRESULT MaybeUpdateCachedData();
 
   RefPtr<IUnknown>                  mDispatchUnk;
   /**
    * Handlers aggregate their proxies. This means that their proxies delegate
    * their IUnknown implementation to us.
    *
    * mDispatchUnk and the result of Handler::GetProxy() are both strong
@@ -178,16 +200,17 @@ private:
    * must immediately Release() them to prevent these cycles.
    *
    * It is safe for us to use these raw pointers because the aggregated
    * objects's lifetimes are proper subsets of our own lifetime.
    */
   IDispatch*                        mDispatch;         // weak
   NEWEST_IA2_INTERFACE*             mIA2PassThru;      // weak
   IServiceProvider*                 mServProvPassThru; // weak
+  IAccessibleHyperlink*             mIAHyperlinkPassThru; // weak
   IA2Payload                        mCachedData;
   UniquePtr<mscom::StructToStream>  mSerializer;
   uint32_t                          mCacheGen;
 };
 
 } // namespace a11y
 } // namespace mozilla