bug 1124449 - teach IAccessible impl about proxy wrappers r=davidb
authorTrevor Saunders <trev.saunders@gmail.com>
Wed, 21 Jan 2015 18:31:27 -0500
changeset 256015 baf1a5507fd6655ccd84cac4d5d7e83f3a1bdf2e
parent 256014 2076dbcf6bcc3360050849a053cd39ed1d096ee9
child 256016 5186614bfe01e21077b1c850d946d029d442edc8
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdavidb
bugs1124449
milestone38.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 1124449 - teach IAccessible impl about proxy wrappers r=davidb
accessible/generic/Accessible.h
accessible/ipc/ProxyAccessible.cpp
accessible/ipc/ProxyAccessible.h
accessible/windows/msaa/AccessibleWrap.cpp
accessible/windows/msaa/AccessibleWrap.h
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -604,16 +604,21 @@ public:
 
   bool IsListControl() const { return HasGenericType(eListControl); }
 
   bool IsMenuButton() const { return HasGenericType(eMenuButton); }
 
   bool IsMenuPopup() const { return mType == eMenuPopupType; }
 
   bool IsProxy() const { return mType == eProxyType; }
+  ProxyAccessible* Proxy() const
+  {
+    MOZ_ASSERT(IsProxy());
+    return mBits.proxy;
+  }
 
   bool IsProgress() const { return mType == eProgressType; }
 
   bool IsRoot() const { return mType == eRootType; }
   a11y::RootAccessible* AsRoot();
 
   bool IsSelect() const { return HasGenericType(eSelect); }
 
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProxyAccessible.h"
 #include "DocAccessibleParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/a11y/Platform.h"
+#include "mozilla/a11y/Role.h"
 
 namespace mozilla {
 namespace a11y {
 
 void
 ProxyAccessible::Shutdown()
 {
   NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
@@ -44,16 +45,36 @@ ProxyAccessible::SetChildDoc(DocAccessib
     mOuterDoc = true;
   } else {
     MOZ_ASSERT(mChildren.Length() == 1);
     mChildren.Clear();
     mOuterDoc = false;
   }
 }
 
+bool
+ProxyAccessible::MustPruneChildren() const
+{
+  // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
+  // kept in sync with that.
+  if (mRole != roles::MENUITEM && mRole != roles::COMBOBOX_OPTION
+      && mRole != roles::OPTION && mRole != roles::ENTRY
+      && mRole != roles::FLAT_EQUATION && mRole != roles::PASSWORD_TEXT
+      && mRole != roles::PUSHBUTTON && mRole != roles::TOGGLE_BUTTON
+      && mRole != roles::GRAPHIC && mRole != roles::SLIDER
+      && mRole != roles::PROGRESSBAR && mRole != roles::SEPARATOR)
+    return false;
+
+  if (mChildren.Length() != 1)
+    return false;
+
+  return mChildren[0]->Role() == roles::TEXT_LEAF
+    || mChildren[0]->Role() == roles::STATICTEXT;
+}
+
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
   unused << mDoc->SendState(mID, &state);
   return state;
 }
 
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -34,16 +34,18 @@ public:
     MOZ_COUNT_DTOR(ProxyAccessible);
     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]; }
+  bool MustPruneChildren() const;
 
   void Shutdown();
 
   void SetChildDoc(DocAccessibleParent*);
 
   /**
    * Remove The given child.
    */
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -9,16 +9,17 @@
 
 #include "Compatibility.h"
 #include "DocAccessible-inl.h"
 #include "EnumVariant.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 #include "nsIAccessibleEvent.h"
 #include "nsWinUtils.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 #include "ServiceProvider.h"
 #include "Relation.h"
 #include "Role.h"
 #include "RootAccessible.h"
 #include "sdnAccessible.h"
 #include "States.h"
 
 #ifdef A11Y_LOG
@@ -148,16 +149,26 @@ AccessibleWrap::get_accParent( IDispatch
   if (!ppdispParent)
     return E_INVALIDARG;
 
   *ppdispParent = nullptr;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (IsProxy()) {
+    ProxyAccessible* proxy = Proxy();
+    ProxyAccessible* parent = proxy->Parent();
+    if (!parent)
+      return S_FALSE;
+
+    *ppdispParent = NativeAccessible(WrapperFor(parent));
+    return S_OK;
+  }
+
   DocAccessible* doc = AsDoc();
   if (doc) {
     // Return window system accessible object for root document and tab document
     // accessibles.
     if (!doc->ParentDocument() ||
         (nsWinUtils::IsWindowEmulationStarted() &&
          nsCoreUtils::IsTabDocument(doc->DocumentNode()))) {
       HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
@@ -187,16 +198,25 @@ AccessibleWrap::get_accChildCount( long 
   if (!pcountChildren)
     return E_INVALIDARG;
 
   *pcountChildren = 0;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  if (IsProxy()) {
+    ProxyAccessible* proxy = Proxy();
+    if (proxy->MustPruneChildren())
+      return S_OK;
+
+    *pcountChildren = proxy->ChildrenCount();
+    return S_OK;
+  }
+
   if (nsAccUtils::MustPrune(this))
     return S_OK;
 
   *pcountChildren = ChildCount();
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
@@ -251,17 +271,20 @@ AccessibleWrap::get_accName(
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString name;
-  xpAccessible->Name(name);
+  if (xpAccessible->IsProxy())
+    xpAccessible->Proxy()->Name(name);
+  else
+    xpAccessible->Name(name);
 
   // The name was not provided, e.g. no alt attribute for an image. A screen
   // reader may choose to invent its own accessible name, e.g. from an image src
   // attribute. Refer to eNoNameOnPurpose return value.
   if (name.IsVoid())
     return S_FALSE;
 
   *pszName = ::SysAllocStringLen(name.get(), name.Length());
@@ -290,16 +313,20 @@ AccessibleWrap::get_accValue(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT)
     return E_ACCESSDENIED;
 
   nsAutoString value;
   xpAccessible->Value(value);
 
   // See bug 438784: need to expose URL on doc's value attribute. For this,
   // reverting part of fix for bug 425693 to make this MSAA method behave
@@ -332,17 +359,20 @@ AccessibleWrap::get_accDescription(VARIA
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   nsAutoString description;
-  xpAccessible->Description(description);
+  if (IsProxy())
+    xpAccessible->Proxy()->Description(description);
+  else
+    xpAccessible->Description(description);
 
   *pszDescription = ::SysAllocStringLen(description.get(),
                                         description.Length());
   return *pszDescription ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
 
@@ -363,22 +393,28 @@ AccessibleWrap::get_accRole(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  a11y::role geckoRole;
+  if (xpAccessible->IsProxy()) {
+    geckoRole = xpAccessible->Proxy()->Role();
+  } else {
 #ifdef DEBUG
-  NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
-               "Does not support Text when it should");
+    NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
+                 "Does not support Text when it should");
 #endif
 
-  a11y::role geckoRole = xpAccessible->Role();
+    geckoRole = xpAccessible->Role();
+  }
+
   uint32_t msaaRole = 0;
 
 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
              _msaaRole, ia2Role, nameRule) \
   case roles::_geckoRole: \
     msaaRole = _msaaRole; \
     break;
 
@@ -388,29 +424,39 @@ AccessibleWrap::get_accRole(
       MOZ_CRASH("Unknown role.");
   };
 
 #undef ROLE
 
   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
   // a ROLE_OUTLINEITEM for consistency and compatibility.
   // We need this because ARIA has a role of "row" for both grid and treegrid
-  if (geckoRole == roles::ROW) {
-    Accessible* xpParent = Parent();
-    if (xpParent && xpParent->Role() == roles::TREE_TABLE)
-      msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+  if (xpAccessible->IsProxy()) {
+      if (geckoRole == roles::ROW
+          && xpAccessible->Proxy()->Parent()->Role() == roles::TREE_TABLE)
+        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+  } else {
+    if (geckoRole == roles::ROW) {
+      Accessible* xpParent = Parent();
+      if (xpParent && xpParent->Role() == roles::TREE_TABLE)
+        msaaRole = ROLE_SYSTEM_OUTLINEITEM;
+    }
   }
   
   // -- Try enumerated role
   if (msaaRole != USE_ROLE_STRING) {
     pvarRole->vt = VT_I4;
     pvarRole->lVal = msaaRole;  // Normal enumerated role
     return S_OK;
   }
 
+  // XXX bug 798492 make this work with proxies?
+  if (IsProxy())
+  return E_FAIL;
+
   // -- Try BSTR role
   // Could not map to known enumerated MSAA role like ROLE_BUTTON
   // Use BSTR role to expose role attribute or tag name + namespace
   nsIContent *content = xpAccessible->GetContent();
   if (!content)
     return E_FAIL;
 
   if (content->IsElement()) {
@@ -471,18 +517,24 @@ AccessibleWrap::get_accState(
   // MSAA only has 31 states and the lowest 31 bits of our state bit mask
   // are the same states as MSAA.
   // Note: we map the following Gecko states to different MSAA states:
   //   REQUIRED -> ALERT_LOW
   //   ALERT -> ALERT_MEDIUM
   //   INVALID -> ALERT_HIGH
   //   CHECKABLE -> MARQUEED
 
+  uint64_t state;
+  if (xpAccessible->IsProxy())
+    state = xpAccessible->Proxy()->State();
+  else
+    state = State();
+
   uint32_t msaaState = 0;
-  nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr);
+  nsAccUtils::To32States(state, &msaaState, nullptr);
   pvarState->lVal = msaaState;
   return S_OK;
 
   A11Y_TRYBLOCK_END
 }
 
 
 STDMETHODIMP
@@ -535,16 +587,20 @@ AccessibleWrap::get_accKeyboardShortcut(
 
   Accessible* acc = GetXPAccessibleFor(varChild);
   if (!acc)
     return E_INVALIDARG;
 
   if (acc->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (acc->IsProxy())
+    return E_NOTIMPL;
+
   KeyBinding keyBinding = acc->AccessKey();
   if (keyBinding.IsEmpty())
     keyBinding = acc->KeyboardShortcut();
 
   nsAutoString shortcut;
   keyBinding.ToString(shortcut);
 
   *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
@@ -569,16 +625,20 @@ AccessibleWrap::get_accFocus(
   //              and does not contain a child that has the keyboard focus.
   // VT_I4:       lVal is CHILDID_SELF. The object itself has the keyboard focus.
   // VT_I4:       lVal contains the child ID of the child element with the keyboard focus.
   // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
   //              for the child object with the keyboard focus.
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   // Return the current IAccessible child that has focus
   Accessible* focusedAccessible = FocusedChild();
   if (focusedAccessible == this) {
     pvarChild->vt = VT_I4;
     pvarChild->lVal = CHILDID_SELF;
   }
   else if (focusedAccessible) {
     pvarChild->vt = VT_DISPATCH;
@@ -731,16 +791,20 @@ AccessibleWrap::get_accSelection(VARIANT
     return E_INVALIDARG;
 
   VariantInit(pvarChildren);
   pvarChildren->vt = VT_EMPTY;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   if (IsSelect()) {
     nsAutoTArray<Accessible*, 10> selectedItems;
     SelectedItems(&selectedItems);
 
     // 1) Create and initialize the enumeration
     nsRefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
     pvarChildren->vt = VT_UNKNOWN;    // this must be VT_UNKNOWN for an IEnumVARIANT
     NS_ADDREF(pvarChildren->punkVal = pEnum);
@@ -767,16 +831,20 @@ AccessibleWrap::get_accDefaultAction(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   nsAutoString defaultAction;
   xpAccessible->ActionNameAt(0, defaultAction);
   *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
                                           defaultAction.Length());
   return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
 
   A11Y_TRYBLOCK_END
 }
@@ -794,16 +862,20 @@ AccessibleWrap::accSelect(
   // currently only handle focus and selection
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
   {
     if (flagsSelect & SELFLAG_TAKEFOCUS)
       xpAccessible->TakeFocus();
 
     if (flagsSelect & SELFLAG_TAKESELECTION)
       xpAccessible->TakeSelection();
 
@@ -847,16 +919,20 @@ AccessibleWrap::accLocation(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   nsIntRect rect = xpAccessible->Bounds();
   *pxLeft = rect.x;
   *pyTop = rect.y;
   *pcxWidth = rect.width;
   *pcyHeight = rect.height;
   return S_OK;
 
   A11Y_TRYBLOCK_END
@@ -880,16 +956,20 @@ AccessibleWrap::accNavigate(
 
   Accessible* accessible = GetXPAccessibleFor(varStart);
   if (!accessible)
     return E_INVALIDARG;
 
   if (accessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   Accessible* navAccessible = nullptr;
   Maybe<RelationType> xpRelation;
 
 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
   case msaaType: \
     xpRelation.emplace(RelationType::geckoType); \
     break;
 
@@ -951,16 +1031,20 @@ AccessibleWrap::accHitTest(
   if (!pvarChild)
     return E_INVALIDARG;
 
   VariantInit(pvarChild);
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (IsProxy())
+    return E_NOTIMPL;
+
   Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
 
   // if we got a child
   if (accessible) {
     // if the child is us
     if (accessible == this) {
       pvarChild->vt = VT_I4;
       pvarChild->lVal = CHILDID_SELF;
@@ -989,16 +1073,20 @@ AccessibleWrap::accDoDefaultAction(
 
   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   if (!xpAccessible)
     return E_INVALIDARG;
 
   if (xpAccessible->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
+  // TODO make this work with proxies.
+  if (xpAccessible->IsProxy())
+    return E_NOTIMPL;
+
   return xpAccessible->DoAction(0) ? S_OK : E_INVALIDARG;
 
   A11Y_TRYBLOCK_END
 }
 
 STDMETHODIMP
 AccessibleWrap::put_accName(
       /* [optional][in] */ VARIANT varChild,
@@ -1231,16 +1319,28 @@ AccessibleWrap::GetXPAccessibleFor(const
 {
   if (aVarChild.vt != VT_I4)
     return nullptr;
 
   // if its us real easy - this seems to always be the case
   if (aVarChild.lVal == CHILDID_SELF)
     return this;
 
+  if (IsProxy()) {
+    if (Proxy()->MustPruneChildren())
+      return nullptr;
+
+    if (aVarChild.lVal > 0)
+      return WrapperFor(Proxy()->ChildAt(aVarChild.lVal - 1));
+
+    // XXX Don't implement negative child ids for now because annoying, and
+    // doesn't seem to be speced.
+    return nullptr;
+  }
+
   if (nsAccUtils::MustPrune(this))
     return nullptr;
 
   // If lVal negative then it is treated as child ID and we should look for
   // accessible through whole accessible subtree including subdocuments.
   // Otherwise we treat lVal as index in parent.
 
   if (aVarChild.lVal < 0) {
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -9,16 +9,17 @@
 
 #include "nsCOMPtr.h"
 #include "Accessible.h"
 #include "Accessible2.h"
 #include "ia2Accessible.h"
 #include "ia2AccessibleComponent.h"
 #include "ia2AccessibleHyperlink.h"
 #include "ia2AccessibleValue.h"
+#include "mozilla/a11y/ProxyAccessible.h"
 
 #ifdef __GNUC__
 // Inheriting from both XPCOM and MSCOM interfaces causes a lot of warnings
 // about virtual functions being hidden by each other. This is done by
 // design, so silence the warning.
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
 
@@ -204,16 +205,21 @@ protected:
     NAVRELATION_DESCRIPTION_FOR = 0x100f,
     NAVRELATION_NODE_PARENT_OF = 0x1010,
     NAVRELATION_CONTAINING_DOCUMENT = 0x1011,
     NAVRELATION_CONTAINING_TAB_PANE = 0x1012,
     NAVRELATION_CONTAINING_APPLICATION = 0x1014
   };
 };
 
+static inline AccessibleWrap*
+WrapperFor(ProxyAccessible* aProxy)
+{
+  return reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper());
+}
 } // namespace a11y
 } // namespace mozilla
 
 #ifdef XP_WIN
 // Undo the windows.h damage
 #undef GetMessage
 #undef CreateEvent
 #undef GetClassName