bug 1138436 - start on proxying IAccessible2 r=surkov, r=davidb
authorTrevor Saunders <trev.saunders@gmail.com>
Mon, 09 Feb 2015 11:57:23 -0500
changeset 233879 4e8054e7ff96b0e5aa7526a4f029f8a68600af60
parent 233878 df62dfbaa245bcb24f07be9a1d0a02f45871143c
child 233880 8015ad9aa698e88f6dcd6ca9d5061ed9f6e6de3f
push id56996
push usertrev.saunders@gmail.com
push dateTue, 17 Mar 2015 00:50:35 +0000
treeherdermozilla-inbound@4e8054e7ff96 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov, davidb
bugs1138436
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1138436 - start on proxying IAccessible2 r=surkov, r=davidb
accessible/ipc/ProxyAccessible.h
accessible/windows/ia2/ia2Accessible.cpp
accessible/windows/ia2/ia2AccessibleRelation.h
accessible/windows/ia2/moz.build
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -37,16 +37,19 @@ public:
     MOZ_ASSERT(!mWrapper);
   }
 
   void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
   { mChildren.InsertElementAt(aIdx, aChild); }
 
   uint32_t ChildrenCount() const { return mChildren.Length(); }
   ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
+
+  // XXX evaluate if this is fast enough.
+  size_t IndexInParent() const { return mParent->mChildren.IndexOf(this); }
   bool MustPruneChildren() const;
 
   void Shutdown();
 
   void SetChildDoc(DocAccessibleParent*);
 
   /**
    * Remove The given child.
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -11,24 +11,27 @@
 #include "AccessibleRole.h"
 #include "AccessibleStates.h"
 
 #include "Compatibility.h"
 #include "ia2AccessibleRelation.h"
 #include "IUnknownImpl.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleTypes.h"
+#include "mozilla/a11y/PDocAccessible.h"
 #include "Relation.h"
 
 #include "nsIPersistentProperties2.h"
 #include "nsISimpleEnumerator.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
+template<typename String> static void EscapeAttributeChars(String& aStr);
+
 ////////////////////////////////////////////////////////////////////////////////
 // ia2Accessible
 ////////////////////////////////////////////////////////////////////////////////
 
 STDMETHODIMP
 ia2Accessible::QueryInterface(REFIID iid, void** ppv)
 {
   if (!ppv)
@@ -60,16 +63,25 @@ ia2Accessible::get_nRelations(long* aNRe
   if (!aNRelations)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (acc->IsProxy()) {
+    // XXX evaluate performance of collecting all relation targets.
+    nsTArray<RelationType> types;
+    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
+    acc->Proxy()->Relations(&types, &targetSets);
+    *aNRelations = types.Length();
+    return S_OK;
+  }
+
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     Relation rel = acc->RelationByType(sRelationTypePairs[idx].first);
     if (rel.Next())
       (*aNRelations)++;
   }
@@ -79,24 +91,52 @@ ia2Accessible::get_nRelations(long* aNRe
 }
 
 STDMETHODIMP
 ia2Accessible::get_relation(long aRelationIndex,
                             IAccessibleRelation** aRelation)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (!aRelation)
+  if (!aRelation || aRelationIndex < 0)
     return E_INVALIDARG;
   *aRelation = nullptr;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (acc->IsProxy()) {
+    nsTArray<RelationType> types;
+    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
+    acc->Proxy()->Relations(&types, &targetSets);
+
+    size_t targetSetCount = targetSets.Length();
+    for (size_t i = 0; i < targetSetCount; i++) {
+      uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
+      MOZ_ASSERT(sRelationTypePairs[relTypeIdx].first == types[i]);
+      if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
+        continue;
+
+      if (static_cast<size_t>(aRelationIndex) == i) {
+        nsTArray<nsRefPtr<Accessible>> targets;
+        size_t targetCount = targetSets[i].Length();
+        for (size_t j = 0; j < targetCount; j++)
+          targets.AppendElement(WrapperFor(targetSets[i][j]));
+
+        nsRefPtr<ia2AccessibleRelation> rel =
+          new ia2AccessibleRelation(types[i], Move(targets));
+        rel.forget(aRelation);
+        return S_OK;
+      }
+    }
+
+    return E_INVALIDARG;
+  }
+
   long relIdx = 0;
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
     nsRefPtr<ia2AccessibleRelation> ia2Relation =
@@ -118,24 +158,52 @@ ia2Accessible::get_relation(long aRelati
 
 STDMETHODIMP
 ia2Accessible::get_relations(long aMaxRelations,
                              IAccessibleRelation** aRelation,
                              long *aNRelations)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (!aRelation || !aNRelations)
+  if (!aRelation || !aNRelations || aMaxRelations <= 0)
     return E_INVALIDARG;
   *aNRelations = 0;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (acc->IsProxy()) {
+    nsTArray<RelationType> types;
+    nsTArray<nsTArray<ProxyAccessible*>> targetSets;
+    acc->Proxy()->Relations(&types, &targetSets);
+
+    size_t count = std::min(targetSets.Length(),
+                            static_cast<size_t>(aMaxRelations));
+    size_t i = 0;
+    while (i < count) {
+      uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
+      if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
+        continue;
+
+      size_t targetCount = targetSets[i].Length();
+      nsTArray<nsRefPtr<Accessible>> targets(targetCount);
+      for (size_t j = 0; j < targetCount; j++)
+        targets.AppendElement(WrapperFor(targetSets[i][j]));
+
+      nsRefPtr<ia2AccessibleRelation> rel =
+        new ia2AccessibleRelation(types[i], Move(targets));
+      rel.forget(aRelation + i);
+      i++;
+    }
+
+    *aNRelations = i;
+    return S_OK;
+  }
+
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) &&
        *aNRelations < aMaxRelations; idx++) {
     if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
       continue;
 
     RelationType relationType = sRelationTypePairs[idx].first;
     Relation rel = acc->RelationByType(relationType);
     nsRefPtr<ia2AccessibleRelation> ia2Rel =
@@ -164,31 +232,41 @@ ia2Accessible::role(long* aRole)
     return CO_E_OBJNOTCONNECTED;
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     *aRole = ia2Role; \
     break;
 
-  a11y::role geckoRole = acc->Role();
+  a11y::role geckoRole;
+  if (acc->IsProxy())
+    geckoRole = acc->Proxy()->Role();
+  else
+    geckoRole = acc->Role();
   switch (geckoRole) {
 #include "RoleMap.h"
     default:
       MOZ_CRASH("Unknown role.");
   };
 
 #undef ROLE
 
   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call
   // the IA2 role a ROLE_OUTLINEITEM.
-  if (geckoRole == roles::ROW) {
-    Accessible* xpParent = acc->Parent();
-    if (xpParent && xpParent->Role() == roles::TREE_TABLE)
+  if (acc->IsProxy()) {
+    if (geckoRole == roles::ROW && acc->Proxy()->Parent() &&
+        acc->Proxy()->Parent()->Role() == roles::TREE_TABLE)
       *aRole = ROLE_SYSTEM_OUTLINEITEM;
+  } else {
+    if (geckoRole == roles::ROW) {
+      Accessible* xpParent = acc->Parent();
+      if (xpParent && xpParent->Role() == roles::TREE_TABLE)
+        *aRole = ROLE_SYSTEM_OUTLINEITEM;
+    }
   }
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
@@ -269,17 +347,26 @@ ia2Accessible::get_states(AccessibleStat
 
   if (!aStates)
     return E_INVALIDARG;
   *aStates = 0;
 
   // XXX: bug 344674 should come with better approach that we have here.
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
-  uint64_t state = acc->State();
+  if (acc->IsDefunct()) {
+    *aStates = IA2_STATE_DEFUNCT;
+    return CO_E_OBJNOTCONNECTED;
+  }
+
+  uint64_t state;
+  if (acc->IsProxy())
+    state = acc->Proxy()->State();
+  else
+    state = acc->State();
 
   if (state & states::INVALID)
     *aStates |= IA2_STATE_INVALID_ENTRY;
   if (state & states::REQUIRED)
     *aStates |= IA2_STATE_REQUIRED;
 
   // The following IA2 states are not supported by Gecko
   // IA2_STATE_ARMED
@@ -441,17 +528,21 @@ ia2Accessible::get_indexInParent(long* a
   if (!aIndexInParent)
     return E_INVALIDARG;
   *aIndexInParent = -1;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  *aIndexInParent = acc->IndexInParent();
+  if (acc->IsProxy())
+    *aIndexInParent = acc->Proxy()->IndexInParent();
+  else
+    *aIndexInParent = acc->IndexInParent();
+
   if (*aIndexInParent == -1)
     return S_FALSE;
 
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -516,18 +607,39 @@ ia2Accessible::get_attributes(BSTR* aAtt
   *aAttributes = nullptr;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   // The format is name:value;name:value; with \ for escaping these
   // characters ":;=,\".
-  nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
-  return ConvertToIA2Attributes(attributes, aAttributes);
+  if (!acc->IsProxy()) {
+    nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
+    return ConvertToIA2Attributes(attributes, aAttributes);
+  }
+
+  nsTArray<Attribute> attrs;
+  acc->Proxy()->Attributes(&attrs);
+  nsString attrStr;
+  size_t attrCount = attrs.Length();
+  for (size_t i = 0; i < attrCount; i++) {
+    EscapeAttributeChars(attrs[i].Name());
+    EscapeAttributeChars(attrs[i].Value());
+    AppendUTF8toUTF16(attrs[i].Name(), attrStr);
+    attrStr.Append(':');
+    attrStr.Append(attrs[i].Value());
+    attrStr.Append(';');
+  }
+
+  if (attrStr.IsEmpty())
+    return S_FALSE;
+
+  *aAttributes = ::SysAllocStringLen(attrStr.get(), attrStr.Length());
+  return *aAttributes ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // IAccessible2_2
 
 STDMETHODIMP
@@ -562,17 +674,17 @@ ia2Accessible::get_accessibleWithCaret(I
 STDMETHODIMP
 ia2Accessible::get_relationTargetsOfType(BSTR aType,
                                          long aMaxTargets,
                                          IUnknown*** aTargets,
                                          long* aNTargets)
 {
   A11Y_TRYBLOCK_BEGIN
 
-  if (!aTargets || !aNTargets)
+  if (!aTargets || !aNTargets || aMaxTargets < 0)
     return E_INVALIDARG;
   *aNTargets = 0;
 
   Maybe<RelationType> relationType;
   for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
     if (wcscmp(aType, sRelationTypePairs[idx].second) == 0) {
       relationType.emplace(sRelationTypePairs[idx].first);
       break;
@@ -580,23 +692,33 @@ ia2Accessible::get_relationTargetsOfType
   }
   if (!relationType)
     return E_INVALIDARG;
 
   AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  Relation rel = acc->RelationByType(*relationType);
+  nsTArray<Accessible*> targets;
+  if (acc->IsProxy()) {
+    nsTArray<ProxyAccessible*> targetProxies =
+      acc->Proxy()->RelationByType(*relationType);
 
-  nsTArray<Accessible*> targets;
-  Accessible* target = nullptr;
-  while ((target = rel.Next()) &&
-         static_cast<long>(targets.Length()) <= aMaxTargets)
-    targets.AppendElement(target);
+    size_t targetCount = aMaxTargets;
+    if (targetProxies.Length() < targetCount)
+      targetCount = targetProxies.Length();
+    for (size_t i = 0; i < targetCount; i++)
+      targets.AppendElement(WrapperFor(targetProxies[i]));
+  } else {
+    Relation rel = acc->RelationByType(*relationType);
+    Accessible* target = nullptr;
+    while ((target = rel.Next()) &&
+           static_cast<long>(targets.Length()) <= aMaxTargets)
+      targets.AppendElement(target);
+  }
 
   *aNTargets = targets.Length();
   *aTargets = static_cast<IUnknown**>(
     ::CoTaskMemAlloc(sizeof(IUnknown*) * *aNTargets));
   if (!*aTargets)
     return E_OUTOFMEMORY;
 
   for (int32_t i = 0; i < *aNTargets; i++) {
@@ -608,16 +730,28 @@ ia2Accessible::get_relationTargetsOfType
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Helpers
 
+template<typename String>
+static inline void
+EscapeAttributeChars(String& aStr)
+{
+  int32_t offset = 0;
+  static const char kCharsToEscape[] = ":;=,\\";
+  while ((offset = aStr.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
+    aStr.Insert('\\', offset);
+    offset += 2;
+  }
+}
+
 HRESULT
 ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
                                       BSTR* aIA2Attributes)
 {
   *aIA2Attributes = nullptr;
 
   // The format is name:value;name:value; with \ for escaping these
   // characters ":;=,\".
@@ -627,46 +761,36 @@ ia2Accessible::ConvertToIA2Attributes(ns
 
   nsCOMPtr<nsISimpleEnumerator> propEnum;
   aAttributes->Enumerate(getter_AddRefs(propEnum));
   if (!propEnum)
     return E_FAIL;
 
   nsAutoString strAttrs;
 
-  const char kCharsToEscape[] = ":;=,\\";
-
   bool hasMore = false;
   while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
     nsCOMPtr<nsISupports> propSupports;
     propEnum->GetNext(getter_AddRefs(propSupports));
 
     nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports));
     if (!propElem)
       return E_FAIL;
 
     nsAutoCString name;
     if (NS_FAILED(propElem->GetKey(name)))
       return E_FAIL;
 
-    int32_t offset = 0;
-    while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
-      name.Insert('\\', offset);
-      offset += 2;
-    }
+    EscapeAttributeChars(name);
 
     nsAutoString value;
     if (NS_FAILED(propElem->GetValue(value)))
       return E_FAIL;
 
-    offset = 0;
-    while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
-      value.Insert('\\', offset);
-      offset += 2;
-    }
+    EscapeAttributeChars(value);
 
     AppendUTF8toUTF16(name, strAttrs);
     strAttrs.Append(':');
     strAttrs.Append(value);
     strAttrs.Append(';');
   }
 
   if (strAttrs.IsEmpty())
--- a/accessible/windows/ia2/ia2AccessibleRelation.h
+++ b/accessible/windows/ia2/ia2AccessibleRelation.h
@@ -19,16 +19,20 @@
 namespace mozilla {
 namespace a11y {
 
 class ia2AccessibleRelation MOZ_FINAL : public IAccessibleRelation
 {
 public:
   ia2AccessibleRelation(RelationType aType, Relation* aRel);
 
+  ia2AccessibleRelation(RelationType aType,
+                        nsTArray<nsRefPtr<Accessible>>&& aTargets) :
+    mType(aType), mTargets(Move(aTargets)) {}
+
   // IUnknown
   DECL_IUNKNOWN
 
   // IAccessibleRelation
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationType(
       /* [retval][out] */ BSTR *relationType);
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedRelationType(
--- a/accessible/windows/ia2/moz.build
+++ b/accessible/windows/ia2/moz.build
@@ -47,8 +47,10 @@ LOCAL_INCLUDES += [
 FINAL_LIBRARY = 'xul'
 
 # The midl generated code include Windows headers which defines min and max
 # macros which conflicts with std::min/max.  Suppress the macros:
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
 
 FAIL_ON_WARNINGS = True
+
+include('/ipc/chromium/chromium-config.mozbuild')