accessible/src/windows/msaa/AccessibleWrap.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "AccessibleWrap.h"
     8 #include "Accessible-inl.h"
    10 #include "Compatibility.h"
    11 #include "DocAccessible-inl.h"
    12 #include "EnumVariant.h"
    13 #include "nsAccUtils.h"
    14 #include "nsCoreUtils.h"
    15 #include "nsIAccessibleEvent.h"
    16 #include "nsIAccessibleRelation.h"
    17 #include "nsWinUtils.h"
    18 #include "ServiceProvider.h"
    19 #include "Relation.h"
    20 #include "Role.h"
    21 #include "RootAccessible.h"
    22 #include "sdnAccessible.h"
    23 #include "States.h"
    25 #ifdef A11Y_LOG
    26 #include "Logging.h"
    27 #endif
    29 #include "nsIMutableArray.h"
    30 #include "nsIFrame.h"
    31 #include "nsIScrollableFrame.h"
    32 #include "nsINodeInfo.h"
    33 #include "nsIServiceManager.h"
    34 #include "nsNameSpaceManager.h"
    35 #include "nsTextFormatter.h"
    36 #include "nsView.h"
    37 #include "nsViewManager.h"
    38 #include "nsEventMap.h"
    39 #include "nsArrayUtils.h"
    40 #include "mozilla/Preferences.h"
    42 #include "oleacc.h"
    44 using namespace mozilla;
    45 using namespace mozilla::a11y;
    47 const uint32_t USE_ROLE_STRING = 0;
    49 /* For documentation of the accessibility architecture,
    50  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
    51  */
    53 //#define DEBUG_LEAKS
    55 #ifdef DEBUG_LEAKS
    56 static gAccessibles = 0;
    57 #endif
    59 static const int32_t kIEnumVariantDisconnected = -1;
    61 ////////////////////////////////////////////////////////////////////////////////
    62 // AccessibleWrap
    63 ////////////////////////////////////////////////////////////////////////////////
    65 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
    67 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
    69 //-----------------------------------------------------
    70 // IUnknown interface methods - see iunknown.h for documentation
    71 //-----------------------------------------------------
    73 // Microsoft COM QueryInterface
    74 STDMETHODIMP
    75 AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
    76 {
    77   A11Y_TRYBLOCK_BEGIN
    79   if (!ppv)
    80     return E_INVALIDARG;
    82   *ppv = nullptr;
    84   if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
    85     *ppv = static_cast<IAccessible*>(this);
    86   else if (IID_IEnumVARIANT == iid) {
    87     // Don't support this interface for leaf elements.
    88     if (!HasChildren() || nsAccUtils::MustPrune(this))
    89       return E_NOINTERFACE;
    91     *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
    92   } else if (IID_IServiceProvider == iid)
    93     *ppv = new ServiceProvider(this);
    94   else if (IID_ISimpleDOMNode == iid) {
    95     if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
    96       return E_NOINTERFACE;
    98     *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode()));
    99   }
   101   if (nullptr == *ppv) {
   102     HRESULT hr = ia2Accessible::QueryInterface(iid, ppv);
   103     if (SUCCEEDED(hr))
   104       return hr;
   105   }
   107   if (nullptr == *ppv) {
   108     HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv);
   109     if (SUCCEEDED(hr))
   110       return hr;
   111   }
   113   if (nullptr == *ppv) {
   114     HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
   115     if (SUCCEEDED(hr))
   116       return hr;
   117   }
   119   if (nullptr == *ppv) {
   120     HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv);
   121     if (SUCCEEDED(hr))
   122       return hr;
   123   }
   125   if (nullptr == *ppv)
   126     return E_NOINTERFACE;
   128   (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
   129   return S_OK;
   131   A11Y_TRYBLOCK_END
   132 }
   134 //-----------------------------------------------------
   135 // IAccessible methods
   136 //-----------------------------------------------------
   138 STDMETHODIMP
   139 AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
   140 {
   141   A11Y_TRYBLOCK_BEGIN
   143   if (!ppdispParent)
   144     return E_INVALIDARG;
   146   *ppdispParent = nullptr;
   148   if (IsDefunct())
   149     return CO_E_OBJNOTCONNECTED;
   151   DocAccessible* doc = AsDoc();
   152   if (doc) {
   153     // Return window system accessible object for root document and tab document
   154     // accessibles.
   155     if (!doc->ParentDocument() ||
   156         (nsWinUtils::IsWindowEmulationStarted() &&
   157          nsCoreUtils::IsTabDocument(doc->DocumentNode()))) {
   158       HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
   159       if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
   160                                                          IID_IAccessible,
   161                                                          (void**)ppdispParent))) {
   162         return S_OK;
   163       }
   164     }
   165   }
   167   Accessible* xpParentAcc = Parent();
   168   if (!xpParentAcc)
   169     return S_FALSE;
   171   *ppdispParent = NativeAccessible(xpParentAcc);
   172   return S_OK;
   174   A11Y_TRYBLOCK_END
   175 }
   177 STDMETHODIMP
   178 AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
   179 {
   180   A11Y_TRYBLOCK_BEGIN
   182   if (!pcountChildren)
   183     return E_INVALIDARG;
   185   *pcountChildren = 0;
   187   if (IsDefunct())
   188     return CO_E_OBJNOTCONNECTED;
   190   if (nsAccUtils::MustPrune(this))
   191     return S_OK;
   193   *pcountChildren = ChildCount();
   194   return S_OK;
   196   A11Y_TRYBLOCK_END
   197 }
   199 STDMETHODIMP
   200 AccessibleWrap::get_accChild(
   201       /* [in] */ VARIANT varChild,
   202       /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
   203 {
   204   A11Y_TRYBLOCK_BEGIN
   206   if (!ppdispChild)
   207     return E_INVALIDARG;
   209   *ppdispChild = nullptr;
   210   if (IsDefunct())
   211     return CO_E_OBJNOTCONNECTED;
   213   // IAccessible::accChild is used to return this accessible or child accessible
   214   // at the given index or to get an accessible by child ID in the case of
   215   // document accessible (it's handled by overriden GetXPAccessibleFor method
   216   // on the document accessible). The getting an accessible by child ID is used
   217   // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event.
   218   Accessible* child = GetXPAccessibleFor(varChild);
   219   if (!child)
   220     return E_INVALIDARG;
   222   if (child->IsDefunct())
   223     return CO_E_OBJNOTCONNECTED;
   225   *ppdispChild = NativeAccessible(child);
   226   return S_OK;
   228   A11Y_TRYBLOCK_END
   229 }
   231 STDMETHODIMP
   232 AccessibleWrap::get_accName(
   233       /* [optional][in] */ VARIANT varChild,
   234       /* [retval][out] */ BSTR __RPC_FAR *pszName)
   235 {
   236   A11Y_TRYBLOCK_BEGIN
   238   if (!pszName)
   239     return E_INVALIDARG;
   241   *pszName = nullptr;
   243   if (IsDefunct())
   244     return CO_E_OBJNOTCONNECTED;
   246   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   247   if (!xpAccessible)
   248     return E_INVALIDARG;
   250   if (xpAccessible->IsDefunct())
   251     return CO_E_OBJNOTCONNECTED;
   253   nsAutoString name;
   254   xpAccessible->Name(name);
   256   // The name was not provided, e.g. no alt attribute for an image. A screen
   257   // reader may choose to invent its own accessible name, e.g. from an image src
   258   // attribute. Refer to eNoNameOnPurpose return value.
   259   if (name.IsVoid())
   260     return S_FALSE;
   262   *pszName = ::SysAllocStringLen(name.get(), name.Length());
   263   if (!*pszName)
   264     return E_OUTOFMEMORY;
   265   return S_OK;
   267   A11Y_TRYBLOCK_END
   268 }
   271 STDMETHODIMP
   272 AccessibleWrap::get_accValue(
   273       /* [optional][in] */ VARIANT varChild,
   274       /* [retval][out] */ BSTR __RPC_FAR *pszValue)
   275 {
   276   A11Y_TRYBLOCK_BEGIN
   278   if (!pszValue)
   279     return E_INVALIDARG;
   281   *pszValue = nullptr;
   283   if (IsDefunct())
   284     return CO_E_OBJNOTCONNECTED;
   286   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   287   if (!xpAccessible)
   288     return E_INVALIDARG;
   290   if (xpAccessible->IsDefunct())
   291     return CO_E_OBJNOTCONNECTED;
   293   if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT)
   294     return E_ACCESSDENIED;
   296   nsAutoString value;
   297   xpAccessible->Value(value);
   299   // See bug 438784: need to expose URL on doc's value attribute. For this,
   300   // reverting part of fix for bug 425693 to make this MSAA method behave
   301   // IAccessible2-style.
   302   if (value.IsEmpty())
   303     return S_FALSE;
   305   *pszValue = ::SysAllocStringLen(value.get(), value.Length());
   306   if (!*pszValue)
   307     return E_OUTOFMEMORY;
   308   return S_OK;
   310   A11Y_TRYBLOCK_END
   311 }
   313 STDMETHODIMP
   314 AccessibleWrap::get_accDescription(VARIANT varChild,
   315                                    BSTR __RPC_FAR *pszDescription)
   316 {
   317   A11Y_TRYBLOCK_BEGIN
   319   if (!pszDescription)
   320     return E_INVALIDARG;
   322   *pszDescription = nullptr;
   324   if (IsDefunct())
   325     return CO_E_OBJNOTCONNECTED;
   327   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   328   if (!xpAccessible)
   329     return E_INVALIDARG;
   331   if (xpAccessible->IsDefunct())
   332     return CO_E_OBJNOTCONNECTED;
   334   nsAutoString description;
   335   xpAccessible->Description(description);
   337   *pszDescription = ::SysAllocStringLen(description.get(),
   338                                         description.Length());
   339   return *pszDescription ? S_OK : E_OUTOFMEMORY;
   341   A11Y_TRYBLOCK_END
   342 }
   344 STDMETHODIMP
   345 AccessibleWrap::get_accRole(
   346       /* [optional][in] */ VARIANT varChild,
   347       /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
   348 {
   349   A11Y_TRYBLOCK_BEGIN
   351   if (!pvarRole)
   352     return E_INVALIDARG;
   354   VariantInit(pvarRole);
   356   if (IsDefunct())
   357     return CO_E_OBJNOTCONNECTED;
   359   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   360   if (!xpAccessible)
   361     return E_INVALIDARG;
   363   if (xpAccessible->IsDefunct())
   364     return CO_E_OBJNOTCONNECTED;
   366 #ifdef DEBUG
   367   NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
   368                "Does not support nsIAccessibleText when it should");
   369 #endif
   371   a11y::role geckoRole = xpAccessible->Role();
   372   uint32_t msaaRole = 0;
   374 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
   375              _msaaRole, ia2Role, nameRule) \
   376   case roles::_geckoRole: \
   377     msaaRole = _msaaRole; \
   378     break;
   380   switch (geckoRole) {
   381 #include "RoleMap.h"
   382     default:
   383       MOZ_CRASH("Unknown role.");
   384   };
   386 #undef ROLE
   388   // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
   389   // a ROLE_OUTLINEITEM for consistency and compatibility.
   390   // We need this because ARIA has a role of "row" for both grid and treegrid
   391   if (geckoRole == roles::ROW) {
   392     Accessible* xpParent = Parent();
   393     if (xpParent && xpParent->Role() == roles::TREE_TABLE)
   394       msaaRole = ROLE_SYSTEM_OUTLINEITEM;
   395   }
   397   // -- Try enumerated role
   398   if (msaaRole != USE_ROLE_STRING) {
   399     pvarRole->vt = VT_I4;
   400     pvarRole->lVal = msaaRole;  // Normal enumerated role
   401     return S_OK;
   402   }
   404   // -- Try BSTR role
   405   // Could not map to known enumerated MSAA role like ROLE_BUTTON
   406   // Use BSTR role to expose role attribute or tag name + namespace
   407   nsIContent *content = xpAccessible->GetContent();
   408   if (!content)
   409     return E_FAIL;
   411   if (content->IsElement()) {
   412     nsAutoString roleString;
   413     if (msaaRole != ROLE_SYSTEM_CLIENT &&
   414         !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) {
   415       nsIDocument * document = content->GetCurrentDoc();
   416       if (!document)
   417         return E_FAIL;
   419       nsINodeInfo *nodeInfo = content->NodeInfo();
   420       nodeInfo->GetName(roleString);
   422       // Only append name space if different from that of current document.
   423       if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) {
   424         nsAutoString nameSpaceURI;
   425         nodeInfo->GetNamespaceURI(nameSpaceURI);
   426         roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
   427       }
   428     }
   430     if (!roleString.IsEmpty()) {
   431       pvarRole->vt = VT_BSTR;
   432       pvarRole->bstrVal = ::SysAllocString(roleString.get());
   433       return S_OK;
   434     }
   435   }
   437   return E_FAIL;
   439   A11Y_TRYBLOCK_END
   440 }
   442 STDMETHODIMP
   443 AccessibleWrap::get_accState(
   444       /* [optional][in] */ VARIANT varChild,
   445       /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
   446 {
   447   A11Y_TRYBLOCK_BEGIN
   449   if (!pvarState)
   450     return E_INVALIDARG;
   452   VariantInit(pvarState);
   453   pvarState->vt = VT_I4;
   454   pvarState->lVal = 0;
   456   if (IsDefunct())
   457     return CO_E_OBJNOTCONNECTED;
   459   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   460   if (!xpAccessible)
   461     return E_INVALIDARG;
   463   if (xpAccessible->IsDefunct())
   464     return CO_E_OBJNOTCONNECTED;
   466   // MSAA only has 31 states and the lowest 31 bits of our state bit mask
   467   // are the same states as MSAA.
   468   // Note: we map the following Gecko states to different MSAA states:
   469   //   REQUIRED -> ALERT_LOW
   470   //   ALERT -> ALERT_MEDIUM
   471   //   INVALID -> ALERT_HIGH
   472   //   CHECKABLE -> MARQUEED
   474   uint32_t msaaState = 0;
   475   nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr);
   476   pvarState->lVal = msaaState;
   477   return S_OK;
   479   A11Y_TRYBLOCK_END
   480 }
   483 STDMETHODIMP
   484 AccessibleWrap::get_accHelp(
   485       /* [optional][in] */ VARIANT varChild,
   486       /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
   487 {
   488   A11Y_TRYBLOCK_BEGIN
   490   if (!pszHelp)
   491     return E_INVALIDARG;
   493   *pszHelp = nullptr;
   494   return S_FALSE;
   496   A11Y_TRYBLOCK_END
   497 }
   499 STDMETHODIMP
   500 AccessibleWrap::get_accHelpTopic(
   501       /* [out] */ BSTR __RPC_FAR *pszHelpFile,
   502       /* [optional][in] */ VARIANT varChild,
   503       /* [retval][out] */ long __RPC_FAR *pidTopic)
   504 {
   505   A11Y_TRYBLOCK_BEGIN
   507   if (!pszHelpFile || !pidTopic)
   508     return E_INVALIDARG;
   510   *pszHelpFile = nullptr;
   511   *pidTopic = 0;
   512   return S_FALSE;
   514   A11Y_TRYBLOCK_END
   515 }
   517 STDMETHODIMP
   518 AccessibleWrap::get_accKeyboardShortcut(
   519       /* [optional][in] */ VARIANT varChild,
   520       /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
   521 {
   522   A11Y_TRYBLOCK_BEGIN
   524   if (!pszKeyboardShortcut)
   525     return E_INVALIDARG;
   526   *pszKeyboardShortcut = nullptr;
   528   if (IsDefunct())
   529     return CO_E_OBJNOTCONNECTED;
   531   Accessible* acc = GetXPAccessibleFor(varChild);
   532   if (!acc)
   533     return E_INVALIDARG;
   535   if (acc->IsDefunct())
   536     return CO_E_OBJNOTCONNECTED;
   538   KeyBinding keyBinding = acc->AccessKey();
   539   if (keyBinding.IsEmpty())
   540     keyBinding = acc->KeyboardShortcut();
   542   nsAutoString shortcut;
   543   keyBinding.ToString(shortcut);
   545   *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
   546                                              shortcut.Length());
   547   return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
   549   A11Y_TRYBLOCK_END
   550 }
   552 STDMETHODIMP
   553 AccessibleWrap::get_accFocus(
   554       /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
   555 {
   556   A11Y_TRYBLOCK_BEGIN
   558   if (!pvarChild)
   559     return E_INVALIDARG;
   561   VariantInit(pvarChild);
   563   // VT_EMPTY:    None. This object does not have the keyboard focus itself
   564   //              and does not contain a child that has the keyboard focus.
   565   // VT_I4:       lVal is CHILDID_SELF. The object itself has the keyboard focus.
   566   // VT_I4:       lVal contains the child ID of the child element with the keyboard focus.
   567   // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
   568   //              for the child object with the keyboard focus.
   569   if (IsDefunct())
   570     return CO_E_OBJNOTCONNECTED;
   572   // Return the current IAccessible child that has focus
   573   Accessible* focusedAccessible = FocusedChild();
   574   if (focusedAccessible == this) {
   575     pvarChild->vt = VT_I4;
   576     pvarChild->lVal = CHILDID_SELF;
   577   }
   578   else if (focusedAccessible) {
   579     pvarChild->vt = VT_DISPATCH;
   580     pvarChild->pdispVal = NativeAccessible(focusedAccessible);
   581   }
   582   else {
   583     pvarChild->vt = VT_EMPTY;   // No focus or focus is not a child
   584   }
   586   return S_OK;
   588   A11Y_TRYBLOCK_END
   589 }
   591 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects.
   593 class AccessibleEnumerator MOZ_FINAL : public IEnumVARIANT
   594 {
   595 public:
   596   AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { }
   597   AccessibleEnumerator(const AccessibleEnumerator& toCopy) :
   598     mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { }
   599   ~AccessibleEnumerator() { }
   601   // IUnknown
   602   DECL_IUNKNOWN
   604   // IEnumVARIANT
   605   STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched);
   606   STDMETHODIMP Skip(unsigned long celt);
   607   STDMETHODIMP Reset()
   608   {
   609     mCurIndex = 0;
   610     return S_OK;
   611   }
   612   STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum);
   614 private:
   615   nsCOMPtr<nsIArray> mArray;
   616   uint32_t mCurIndex;
   617 };
   619 STDMETHODIMP
   620 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
   621 {
   622   A11Y_TRYBLOCK_BEGIN
   624   if (iid == IID_IEnumVARIANT) {
   625     *ppvObject = static_cast<IEnumVARIANT*>(this);
   626     AddRef();
   627     return S_OK;
   628   }
   629   if (iid == IID_IUnknown) {
   630     *ppvObject = static_cast<IUnknown*>(this);
   631     AddRef();
   632     return S_OK;
   633   }
   635   *ppvObject = nullptr;
   636   return E_NOINTERFACE;
   638   A11Y_TRYBLOCK_END
   639 }
   641 STDMETHODIMP
   642 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
   643 {
   644   A11Y_TRYBLOCK_BEGIN
   646   uint32_t length = 0;
   647   mArray->GetLength(&length);
   649   HRESULT hr = S_OK;
   651   // Can't get more elements than there are...
   652   if (celt > length - mCurIndex) {
   653     hr = S_FALSE;
   654     celt = length - mCurIndex;
   655   }
   657   for (uint32_t i = 0; i < celt; ++i, ++mCurIndex) {
   658     // Copy the elements of the array into rgvar
   659     nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex));
   660     NS_ASSERTION(accel, "Invalid pointer in mArray");
   662     if (accel) {
   663       rgvar[i].vt = VT_DISPATCH;
   664       rgvar[i].pdispVal = AccessibleWrap::NativeAccessible(accel);
   665     }
   666   }
   668   if (pceltFetched)
   669     *pceltFetched = celt;
   671   return hr;
   673   A11Y_TRYBLOCK_END
   674 }
   676 STDMETHODIMP
   677 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
   678 {
   679   A11Y_TRYBLOCK_BEGIN
   681   *ppenum = new AccessibleEnumerator(*this);
   682   if (!*ppenum)
   683     return E_OUTOFMEMORY;
   684   NS_ADDREF(*ppenum);
   685   return S_OK;
   687   A11Y_TRYBLOCK_END
   688 }
   690 STDMETHODIMP
   691 AccessibleEnumerator::Skip(unsigned long celt)
   692 {
   693   A11Y_TRYBLOCK_BEGIN
   695   uint32_t length = 0;
   696   mArray->GetLength(&length);
   697   // Check if we can skip the requested number of elements
   698   if (celt > length - mCurIndex) {
   699     mCurIndex = length;
   700     return S_FALSE;
   701   }
   702   mCurIndex += celt;
   703   return S_OK;
   705   A11Y_TRYBLOCK_END
   706 }
   708 /**
   709   * This method is called when a client wants to know which children of a node
   710   *  are selected. Note that this method can only find selected children for
   711   *  nsIAccessible object which implement SelectAccessible.
   712   *
   713   * The VARIANT return value arguement is expected to either contain a single IAccessible
   714   *  or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
   715   *  of children selected, unless there are none selected in which case we return an empty
   716   *  VARIANT.
   717   *
   718   * We get the selected options from the select's accessible object and wrap
   719   *  those in an AccessibleEnumerator which we then put in the return VARIANT.
   720   *
   721   * returns a VT_EMPTY VARIANT if:
   722   *  - there are no selected children for this object
   723   *  - the object is not the type that can have children selected
   724   */
   725 STDMETHODIMP
   726 AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
   727 {
   728   A11Y_TRYBLOCK_BEGIN
   730   if (!pvarChildren)
   731     return E_INVALIDARG;
   733   VariantInit(pvarChildren);
   734   pvarChildren->vt = VT_EMPTY;
   736   if (IsDefunct())
   737     return CO_E_OBJNOTCONNECTED;
   739   if (IsSelect()) {
   740     nsCOMPtr<nsIArray> selectedItems = SelectedItems();
   741     if (selectedItems) {
   742       // 1) Create and initialize the enumeration
   743       nsRefPtr<AccessibleEnumerator> pEnum =
   744         new AccessibleEnumerator(selectedItems);
   746       // 2) Put the enumerator in the VARIANT
   747       if (!pEnum)
   748         return E_OUTOFMEMORY;
   749       pvarChildren->vt = VT_UNKNOWN;    // this must be VT_UNKNOWN for an IEnumVARIANT
   750       NS_ADDREF(pvarChildren->punkVal = pEnum);
   751     }
   752   }
   753   return S_OK;
   755   A11Y_TRYBLOCK_END
   756 }
   758 STDMETHODIMP
   759 AccessibleWrap::get_accDefaultAction(
   760       /* [optional][in] */ VARIANT varChild,
   761       /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
   762 {
   763   A11Y_TRYBLOCK_BEGIN
   765   if (!pszDefaultAction)
   766     return E_INVALIDARG;
   768   *pszDefaultAction = nullptr;
   770   if (IsDefunct())
   771     return CO_E_OBJNOTCONNECTED;
   773   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   774   if (!xpAccessible)
   775     return E_INVALIDARG;
   777   if (xpAccessible->IsDefunct())
   778     return CO_E_OBJNOTCONNECTED;
   780   nsAutoString defaultAction;
   781   if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
   782     return E_FAIL;
   784   *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
   785                                           defaultAction.Length());
   786   return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
   788   A11Y_TRYBLOCK_END
   789 }
   791 STDMETHODIMP
   792 AccessibleWrap::accSelect(
   793       /* [in] */ long flagsSelect,
   794       /* [optional][in] */ VARIANT varChild)
   795 {
   796   A11Y_TRYBLOCK_BEGIN
   798   if (IsDefunct())
   799     return CO_E_OBJNOTCONNECTED;
   801   // currently only handle focus and selection
   802   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   803   if (!xpAccessible)
   804     return E_INVALIDARG;
   806   if (xpAccessible->IsDefunct())
   807     return CO_E_OBJNOTCONNECTED;
   809   if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
   810   {
   811     if (flagsSelect & SELFLAG_TAKEFOCUS)
   812       xpAccessible->TakeFocus();
   814     if (flagsSelect & SELFLAG_TAKESELECTION)
   815       xpAccessible->TakeSelection();
   817     if (flagsSelect & SELFLAG_ADDSELECTION)
   818       xpAccessible->SetSelected(true);
   820     if (flagsSelect & SELFLAG_REMOVESELECTION)
   821       xpAccessible->SetSelected(false);
   823     if (flagsSelect & SELFLAG_EXTENDSELECTION)
   824       xpAccessible->ExtendSelection();
   826     return S_OK;
   827   }
   829   return E_FAIL;
   831   A11Y_TRYBLOCK_END
   832 }
   834 STDMETHODIMP
   835 AccessibleWrap::accLocation(
   836       /* [out] */ long __RPC_FAR *pxLeft,
   837       /* [out] */ long __RPC_FAR *pyTop,
   838       /* [out] */ long __RPC_FAR *pcxWidth,
   839       /* [out] */ long __RPC_FAR *pcyHeight,
   840       /* [optional][in] */ VARIANT varChild)
   841 {
   842   A11Y_TRYBLOCK_BEGIN
   844   if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)
   845     return E_INVALIDARG;
   847   *pxLeft = 0;
   848   *pyTop = 0;
   849   *pcxWidth = 0;
   850   *pcyHeight = 0;
   852   if (IsDefunct())
   853     return CO_E_OBJNOTCONNECTED;
   855   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
   856   if (!xpAccessible)
   857     return E_INVALIDARG;
   859   if (xpAccessible->IsDefunct())
   860     return CO_E_OBJNOTCONNECTED;
   862   int32_t x, y, width, height;
   863   if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
   864     return E_FAIL;
   866   *pxLeft = x;
   867   *pyTop = y;
   868   *pcxWidth = width;
   869   *pcyHeight = height;
   870   return S_OK;
   872   A11Y_TRYBLOCK_END
   873 }
   875 STDMETHODIMP
   876 AccessibleWrap::accNavigate(
   877       /* [in] */ long navDir,
   878       /* [optional][in] */ VARIANT varStart,
   879       /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
   880 {
   881   A11Y_TRYBLOCK_BEGIN
   883   if (!pvarEndUpAt)
   884     return E_INVALIDARG;
   886   VariantInit(pvarEndUpAt);
   888   if (IsDefunct())
   889     return CO_E_OBJNOTCONNECTED;
   891   Accessible* accessible = GetXPAccessibleFor(varStart);
   892   if (!accessible)
   893     return E_INVALIDARG;
   895   if (accessible->IsDefunct())
   896     return CO_E_OBJNOTCONNECTED;
   898   Accessible* navAccessible = nullptr;
   899   Maybe<RelationType> xpRelation;
   901 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
   902   case msaaType: \
   903     xpRelation.construct(RelationType::geckoType); \
   904     break;
   906   switch(navDir) {
   907     case NAVDIR_FIRSTCHILD:
   908       if (!nsAccUtils::MustPrune(accessible))
   909         navAccessible = accessible->FirstChild();
   910       break;
   911     case NAVDIR_LASTCHILD:
   912       if (!nsAccUtils::MustPrune(accessible))
   913         navAccessible = accessible->LastChild();
   914       break;
   915     case NAVDIR_NEXT:
   916       navAccessible = accessible->NextSibling();
   917       break;
   918     case NAVDIR_PREVIOUS:
   919       navAccessible = accessible->PrevSibling();
   920       break;
   921     case NAVDIR_DOWN:
   922     case NAVDIR_LEFT:
   923     case NAVDIR_RIGHT:
   924     case NAVDIR_UP:
   925       return E_NOTIMPL;
   927     // MSAA relationship extensions to accNavigate
   928 #include "RelationTypeMap.h"
   930     default:
   931       return E_INVALIDARG;
   932   }
   934 #undef RELATIONTYPE
   936   pvarEndUpAt->vt = VT_EMPTY;
   938   if (!xpRelation.empty()) {
   939     Relation rel = RelationByType(xpRelation.ref());
   940     navAccessible = rel.Next();
   941   }
   943   if (!navAccessible)
   944     return E_FAIL;
   946   pvarEndUpAt->pdispVal = NativeAccessible(navAccessible);
   947   pvarEndUpAt->vt = VT_DISPATCH;
   948   return S_OK;
   950   A11Y_TRYBLOCK_END
   951 }
   953 STDMETHODIMP
   954 AccessibleWrap::accHitTest(
   955       /* [in] */ long xLeft,
   956       /* [in] */ long yTop,
   957       /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
   958 {
   959   A11Y_TRYBLOCK_BEGIN
   961   if (!pvarChild)
   962     return E_INVALIDARG;
   964   VariantInit(pvarChild);
   966   if (IsDefunct())
   967     return CO_E_OBJNOTCONNECTED;
   969   Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
   971   // if we got a child
   972   if (accessible) {
   973     // if the child is us
   974     if (accessible == this) {
   975       pvarChild->vt = VT_I4;
   976       pvarChild->lVal = CHILDID_SELF;
   977     } else { // its not create an Accessible for it.
   978       pvarChild->vt = VT_DISPATCH;
   979       pvarChild->pdispVal = NativeAccessible(accessible);
   980     }
   981   } else {
   982     // no child at that point
   983     pvarChild->vt = VT_EMPTY;
   984     return S_FALSE;
   985   }
   986   return S_OK;
   988   A11Y_TRYBLOCK_END
   989 }
   991 STDMETHODIMP
   992 AccessibleWrap::accDoDefaultAction(
   993       /* [optional][in] */ VARIANT varChild)
   994 {
   995   A11Y_TRYBLOCK_BEGIN
   997   if (IsDefunct())
   998     return CO_E_OBJNOTCONNECTED;
  1000   Accessible* xpAccessible = GetXPAccessibleFor(varChild);
  1001   if (!xpAccessible)
  1002     return E_INVALIDARG;
  1004   if (xpAccessible->IsDefunct())
  1005     return CO_E_OBJNOTCONNECTED;
  1007   return GetHRESULT(xpAccessible->DoAction(0));
  1009   A11Y_TRYBLOCK_END
  1012 STDMETHODIMP
  1013 AccessibleWrap::put_accName(
  1014       /* [optional][in] */ VARIANT varChild,
  1015       /* [in] */ BSTR szName)
  1017   return E_NOTIMPL;
  1020 STDMETHODIMP
  1021 AccessibleWrap::put_accValue(
  1022       /* [optional][in] */ VARIANT varChild,
  1023       /* [in] */ BSTR szValue)
  1025   return E_NOTIMPL;
  1028 ////////////////////////////////////////////////////////////////////////////////
  1029 // IDispatch
  1031 STDMETHODIMP
  1032 AccessibleWrap::GetTypeInfoCount(UINT *pctinfo)
  1034   if (!pctinfo)
  1035     return E_INVALIDARG;
  1037   *pctinfo = 1;
  1038   return S_OK;
  1041 STDMETHODIMP
  1042 AccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
  1044   if (!ppTInfo)
  1045     return E_INVALIDARG;
  1047   *ppTInfo = nullptr;
  1049   if (iTInfo != 0)
  1050     return DISP_E_BADINDEX;
  1052   ITypeInfo * typeInfo = GetTI(lcid);
  1053   if (!typeInfo)
  1054     return E_FAIL;
  1056   typeInfo->AddRef();
  1057   *ppTInfo = typeInfo;
  1059   return S_OK;
  1062 STDMETHODIMP
  1063 AccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
  1064                               UINT cNames, LCID lcid, DISPID *rgDispId)
  1066   ITypeInfo *typeInfo = GetTI(lcid);
  1067   if (!typeInfo)
  1068     return E_FAIL;
  1070   HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId);
  1071   return hr;
  1074 STDMETHODIMP
  1075 AccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
  1076                        LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
  1077                        VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
  1078                        UINT *puArgErr)
  1080   ITypeInfo *typeInfo = GetTI(lcid);
  1081   if (!typeInfo)
  1082     return E_FAIL;
  1084   return typeInfo->Invoke(static_cast<IAccessible*>(this), dispIdMember,
  1085                           wFlags, pDispParams, pVarResult, pExcepInfo,
  1086                           puArgErr);
  1090 // nsIAccessible method
  1091 NS_IMETHODIMP
  1092 AccessibleWrap::GetNativeInterface(void **aOutAccessible)
  1094   *aOutAccessible = static_cast<IAccessible*>(this);
  1095   NS_ADDREF_THIS();
  1096   return NS_OK;
  1099 ////////////////////////////////////////////////////////////////////////////////
  1100 // Accessible
  1102 nsresult
  1103 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
  1105   nsresult rv = Accessible::HandleAccEvent(aEvent);
  1106   NS_ENSURE_SUCCESS(rv, rv);
  1108   // Don't fire native MSAA events or mess with the system caret
  1109   // when running in metro mode. This confuses input focus tracking
  1110   // in metro's UIA implementation.
  1111   if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
  1112     return NS_OK;
  1115   uint32_t eventType = aEvent->GetEventType();
  1117   static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
  1118                 "MSAA event map skewed");
  1120   NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE);
  1122   uint32_t winEvent = gWinEventMap[eventType];
  1123   if (!winEvent)
  1124     return NS_OK;
  1126   // Means we're not active.
  1127   NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
  1129   Accessible* accessible = aEvent->GetAccessible();
  1130   if (!accessible)
  1131     return NS_OK;
  1133   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
  1134       eventType == nsIAccessibleEvent::EVENT_FOCUS) {
  1135     UpdateSystemCaretFor(accessible);
  1138   int32_t childID = GetChildIDFor(accessible); // get the id for the accessible
  1139   if (!childID)
  1140     return NS_OK; // Can't fire an event without a child ID
  1142   HWND hWnd = GetHWNDFor(accessible);
  1143   NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
  1145   nsAutoString tag;
  1146   nsAutoCString id;
  1147   nsIContent* cnt = accessible->GetContent();
  1148   if (cnt) {
  1149     cnt->Tag()->ToString(tag);
  1150     nsIAtom* aid = cnt->GetID();
  1151     if (aid)
  1152       aid->ToUTF8String(id);
  1155 #ifdef A11Y_LOG
  1156   if (logging::IsEnabled(logging::ePlatforms)) {
  1157     printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
  1158            eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
  1159            childID, hWnd);
  1161 #endif
  1163   // Fire MSAA event for client area window.
  1164   ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
  1166   // JAWS announces collapsed combobox navigation based on focus events.
  1167   if (Compatibility::IsJAWS()) {
  1168     if (eventType == nsIAccessibleEvent::EVENT_SELECTION &&
  1169       accessible->Role() == roles::COMBOBOX_OPTION) {
  1170       ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
  1174   return NS_OK;
  1177 ////////////////////////////////////////////////////////////////////////////////
  1178 // AccessibleWrap
  1180 //------- Helper methods ---------
  1182 int32_t
  1183 AccessibleWrap::GetChildIDFor(Accessible* aAccessible)
  1185   // A child ID of the window is required, when we use NotifyWinEvent,
  1186   // so that the 3rd party application can call back and get the IAccessible
  1187   // the event occurred on.
  1189   // Yes, this means we're only compatibible with 32 bit
  1190   // MSAA is only available for 32 bit windows, so it's okay
  1191   // XXX: bug 606080
  1192   return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0;
  1195 HWND
  1196 AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
  1198   if (aAccessible) {
  1199     DocAccessible* document = aAccessible->Document();
  1200     if(!document)
  1201       return nullptr;
  1203     // Popup lives in own windows, use its HWND until the popup window is
  1204     // hidden to make old JAWS versions work with collapsed comboboxes (see
  1205     // discussion in bug 379678).
  1206     nsIFrame* frame = aAccessible->GetFrame();
  1207     if (frame) {
  1208       nsIWidget* widget = frame->GetNearestWidget();
  1209       if (widget && widget->IsVisible()) {
  1210         nsIPresShell* shell = document->PresShell();
  1211         nsViewManager* vm = shell->GetViewManager();
  1212         if (vm) {
  1213           nsCOMPtr<nsIWidget> rootWidget;
  1214           vm->GetRootWidget(getter_AddRefs(rootWidget));
  1215           // Make sure the accessible belongs to popup. If not then use
  1216           // document HWND (which might be different from root widget in the
  1217           // case of window emulation).
  1218           if (rootWidget != widget)
  1219             return static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
  1224     return static_cast<HWND>(document->GetNativeWindow());
  1226   return nullptr;
  1229 IDispatch*
  1230 AccessibleWrap::NativeAccessible(nsIAccessible* aAccessible)
  1232   if (!aAccessible) {
  1233    NS_WARNING("Not passing in an aAccessible");
  1234    return nullptr;
  1237   IAccessible* msaaAccessible = nullptr;
  1238   aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible));
  1239   return static_cast<IDispatch*>(msaaAccessible);
  1242 Accessible*
  1243 AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
  1245   if (aVarChild.vt != VT_I4)
  1246     return nullptr;
  1248   // if its us real easy - this seems to always be the case
  1249   if (aVarChild.lVal == CHILDID_SELF)
  1250     return this;
  1252   if (nsAccUtils::MustPrune(this))
  1253     return nullptr;
  1255   // If lVal negative then it is treated as child ID and we should look for
  1256   // accessible through whole accessible subtree including subdocuments.
  1257   // Otherwise we treat lVal as index in parent.
  1259   if (aVarChild.lVal < 0) {
  1260     // Convert child ID to unique ID.
  1261     void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
  1263     DocAccessible* document = Document();
  1264     Accessible* child =
  1265       document->GetAccessibleByUniqueIDInSubtree(uniqueID);
  1267     // If it is a document then just return an accessible.
  1268     if (IsDoc())
  1269       return child;
  1271     // Otherwise check whether the accessible is a child (this path works for
  1272     // ARIA documents and popups).
  1273     Accessible* parent = child;
  1274     while (parent && parent != document) {
  1275       if (parent == this)
  1276         return child;
  1278       parent = parent->Parent();
  1281     return nullptr;
  1284   // Gecko child indices are 0-based in contrast to indices used in MSAA.
  1285   return GetChildAt(aVarChild.lVal - 1);
  1288 void
  1289 AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible)
  1291   // Move the system caret so that Windows Tablet Edition and tradional ATs with 
  1292   // off-screen model can follow the caret
  1293   ::DestroyCaret();
  1295   HyperTextAccessible* text = aAccessible->AsHyperText();
  1296   if (!text)
  1297     return;
  1299   nsIWidget* widget = nullptr;
  1300   nsIntRect caretRect = text->GetCaretRect(&widget);
  1301   HWND caretWnd;
  1302   if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
  1303     return;
  1306   // Create invisible bitmap for caret, otherwise its appearance interferes
  1307   // with Gecko caret
  1308   HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr);
  1309   if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) {  // Also destroys the last caret
  1310     ::ShowCaret(caretWnd);
  1311     RECT windowRect;
  1312     ::GetWindowRect(caretWnd, &windowRect);
  1313     ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
  1314     ::DeleteObject(caretBitMap);
  1318 ITypeInfo*
  1319 AccessibleWrap::GetTI(LCID lcid)
  1321   if (gTypeInfo)
  1322     return gTypeInfo;
  1324   ITypeLib *typeLib = nullptr;
  1325   HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib);
  1326   if (FAILED(hr))
  1327     return nullptr;
  1329   hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo);
  1330   typeLib->Release();
  1332   if (FAILED(hr))
  1333     return nullptr;
  1335   return gTypeInfo;

mercurial