diff options
Diffstat (limited to 'accessible/windows/msaa')
42 files changed, 5199 insertions, 0 deletions
diff --git a/accessible/windows/msaa/ARIAGridAccessibleWrap.cpp b/accessible/windows/msaa/ARIAGridAccessibleWrap.cpp new file mode 100644 index 000000000..04f7c112f --- /dev/null +++ b/accessible/windows/msaa/ARIAGridAccessibleWrap.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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 "ARIAGridAccessibleWrap.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// ARIAGridAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridAccessibleWrap, + ARIAGridAccessible) + +IMPL_IUNKNOWN_INHERITED1(ARIAGridAccessibleWrap, + AccessibleWrap, + ia2AccessibleTable) + +void +ARIAGridAccessibleWrap::Shutdown() +{ + ia2AccessibleTable::mTable = nullptr; + ARIAGridAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// ARIAGridCellAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridCellAccessibleWrap, + ARIAGridCellAccessible) + +IMPL_IUNKNOWN_INHERITED1(ARIAGridCellAccessibleWrap, + HyperTextAccessibleWrap, + ia2AccessibleTableCell) + +void +ARIAGridCellAccessibleWrap::Shutdown() +{ + ia2AccessibleTableCell::mTableCell = nullptr; + ARIAGridCellAccessible::Shutdown(); +} diff --git a/accessible/windows/msaa/ARIAGridAccessibleWrap.h b/accessible/windows/msaa/ARIAGridAccessibleWrap.h new file mode 100644 index 000000000..b03cc349f --- /dev/null +++ b/accessible/windows/msaa/ARIAGridAccessibleWrap.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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/. */ + +#ifndef MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H +#define MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H + +#include "ARIAGridAccessible.h" +#include "ia2AccessibleTable.h" +#include "ia2AccessibleTableCell.h" + +namespace mozilla { +namespace a11y { + +/** + * IA2 wrapper class for ARIAGridAccessible implementing IAccessibleTable and + * IAccessibleTable2 interfaces. + */ +class ARIAGridAccessibleWrap : public ARIAGridAccessible, + public ia2AccessibleTable +{ + ~ARIAGridAccessibleWrap() {} + +public: + ARIAGridAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + ARIAGridAccessible(aContent, aDoc), ia2AccessibleTable(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +/** + * IA2 wrapper class for ARIAGridCellAccessible implementing + * IAccessibleTableCell interface. + */ +class ARIAGridCellAccessibleWrap : public ARIAGridCellAccessible, + public ia2AccessibleTableCell +{ + ~ARIAGridCellAccessibleWrap() {} + +public: + ARIAGridCellAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + ARIAGridCellAccessible(aContent, aDoc), ia2AccessibleTableCell(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp new file mode 100644 index 000000000..6112c370a --- /dev/null +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -0,0 +1,1686 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "AccessibleWrap.h" +#include "Accessible-inl.h" + +#include "Compatibility.h" +#include "DocAccessible-inl.h" +#include "mozilla/dom/TabChild.h" +#include "mozilla/a11y/DocAccessibleChild.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "EnumVariant.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "nsIAccessibleEvent.h" +#include "nsWinUtils.h" +#include "mozilla/a11y/ProxyAccessible.h" +#include "ProxyWrappers.h" +#include "ServiceProvider.h" +#include "Relation.h" +#include "Role.h" +#include "RootAccessible.h" +#include "sdnAccessible.h" +#include "States.h" + +#ifdef A11Y_LOG +#include "Logging.h" +#endif + +#include "nsIMutableArray.h" +#include "nsIFrame.h" +#include "nsIScrollableFrame.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIServiceManager.h" +#include "nsNameSpaceManager.h" +#include "nsTextFormatter.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsEventMap.h" +#include "nsArrayUtils.h" +#include "mozilla/Preferences.h" +#include "nsIXULRuntime.h" + +#include "oleacc.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +const uint32_t USE_ROLE_STRING = 0; + +/* For documentation of the accessibility architecture, + * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html + */ + +//#define DEBUG_LEAKS + +#ifdef DEBUG_LEAKS +static gAccessibles = 0; +#endif + +MsaaIdGenerator AccessibleWrap::sIDGen; + +static const VARIANT kVarChildIdSelf = {VT_I4}; + +static const int32_t kIEnumVariantDisconnected = -1; + +//////////////////////////////////////////////////////////////////////////////// +// AccessibleWrap +//////////////////////////////////////////////////////////////////////////////// +AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + Accessible(aContent, aDoc) + , mID(kNoID) +{ +} + +AccessibleWrap::~AccessibleWrap() +{ + if (mID != kNoID) { + sIDGen.ReleaseID(this); + } +} + +ITypeInfo* AccessibleWrap::gTypeInfo = nullptr; + +NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible) + +void +AccessibleWrap::Shutdown() +{ + if (mID != kNoID) { + auto doc = static_cast<DocAccessibleWrap*>(mDoc.get()); + MOZ_ASSERT(doc); + if (doc) { + doc->RemoveID(mID); + mID = kNoID; + } + } + + Accessible::Shutdown(); +} + +//----------------------------------------------------- +// IUnknown interface methods - see iunknown.h for documentation +//----------------------------------------------------- + +// Microsoft COM QueryInterface +STDMETHODIMP +AccessibleWrap::QueryInterface(REFIID iid, void** ppv) +{ + A11Y_TRYBLOCK_BEGIN + + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IUnknown == iid) + *ppv = static_cast<IAccessible*>(this); + else if (IID_IDispatch == iid || IID_IAccessible == iid) + *ppv = static_cast<IAccessible*>(this); + else if (IID_IEnumVARIANT == iid && !IsProxy()) { + // Don't support this interface for leaf elements. + if (!HasChildren() || nsAccUtils::MustPrune(this)) + return E_NOINTERFACE; + + *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this)); + } else if (IID_IServiceProvider == iid) + *ppv = new ServiceProvider(this); + else if (IID_ISimpleDOMNode == iid && !IsProxy()) { + if (IsDefunct() || (!HasOwnContent() && !IsDoc())) + return E_NOINTERFACE; + + *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode())); + } + + if (nullptr == *ppv) { + HRESULT hr = ia2Accessible::QueryInterface(iid, ppv); + if (SUCCEEDED(hr)) + return hr; + } + + if (nullptr == *ppv && !IsProxy()) { + HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv); + if (SUCCEEDED(hr)) + return hr; + } + + if (nullptr == *ppv) { + HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv); + if (SUCCEEDED(hr)) + return hr; + } + + if (nullptr == *ppv && !IsProxy()) { + HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv); + if (SUCCEEDED(hr)) + return hr; + } + + if (nullptr == *ppv) + return E_NOINTERFACE; + + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +//----------------------------------------------------- +// IAccessible methods +//----------------------------------------------------- + +STDMETHODIMP +AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) +{ + A11Y_TRYBLOCK_BEGIN + + if (!ppdispParent) + return E_INVALIDARG; + + *ppdispParent = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + 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()); + if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, + IID_IAccessible, + (void**)ppdispParent))) { + return S_OK; + } + } + } + + Accessible* xpParentAcc = Parent(); + if (!xpParentAcc) + return S_FALSE; + + *ppdispParent = NativeAccessible(xpParentAcc); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pcountChildren) + return E_INVALIDARG; + + *pcountChildren = 0; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (nsAccUtils::MustPrune(this)) + return S_OK; + + *pcountChildren = ChildCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accChild( + /* [in] */ VARIANT varChild, + /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) +{ + A11Y_TRYBLOCK_BEGIN + + if (!ppdispChild) + return E_INVALIDARG; + + *ppdispChild = nullptr; + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + // IAccessible::accChild is used to return this accessible or child accessible + // at the given index or to get an accessible by child ID in the case of + // document accessible. + // The getting an accessible by child ID is used by AccessibleObjectFromEvent() + // called by AT when AT handles our MSAA event. + bool isDefunct = false; + RefPtr<IAccessible> child = GetIAccessibleFor(varChild, &isDefunct); + if (!child) { + return E_INVALIDARG; + } + + if (isDefunct) { + return CO_E_OBJNOTCONNECTED; + } + + child.forget(ppdispChild); + return S_OK; + + A11Y_TRYBLOCK_END +} + +/** + * This function is a helper for implementing IAccessible methods that accept + * a Child ID as a parameter. If the child ID is CHILDID_SELF, the function + * returns S_OK but a null *aOutInterface. Otherwise, *aOutInterface points + * to the resolved IAccessible. + * + * The CHILDID_SELF case is special because in that case we actually execute + * the implementation of the IAccessible method, whereas in the non-self case, + * we delegate the method call to that object for execution. + * + * A sample invocation of this would look like: + * + * RefPtr<IAccessible> accessible; + * HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + * if (FAILED(hr)) { + * return hr; + * } + * + * if (accessible) { + * return accessible->get_accFoo(kVarChildIdSelf, pszName); + * } + * + * // Implementation for CHILDID_SELF case goes here + */ +HRESULT +AccessibleWrap::ResolveChild(const VARIANT& aVarChild, + IAccessible** aOutInterface) +{ + MOZ_ASSERT(aOutInterface); + *aOutInterface = nullptr; + + if (aVarChild.vt != VT_I4) { + return E_INVALIDARG; + } + + if (IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + if (aVarChild.lVal == CHILDID_SELF) { + return S_OK; + } + + bool isDefunct = false; + RefPtr<IAccessible> accessible = GetIAccessibleFor(aVarChild, &isDefunct); + if (!accessible) { + return E_INVALIDARG; + } + + if (isDefunct) { + return CO_E_OBJNOTCONNECTED; + } + + accessible.forget(aOutInterface); + return S_OK; +} + +STDMETHODIMP +AccessibleWrap::get_accName( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszName) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszName || varChild.vt != VT_I4) + return E_INVALIDARG; + + *pszName = nullptr; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accName(kVarChildIdSelf, pszName); + } + + nsAutoString name; + 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()); + if (!*pszName) + return E_OUTOFMEMORY; + return S_OK; + + A11Y_TRYBLOCK_END +} + + +STDMETHODIMP +AccessibleWrap::get_accValue( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszValue) + return E_INVALIDARG; + + *pszValue = nullptr; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accValue(kVarChildIdSelf, pszValue); + } + + nsAutoString value; + 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 + // IAccessible2-style. + if (value.IsEmpty()) + return S_FALSE; + + *pszValue = ::SysAllocStringLen(value.get(), value.Length()); + if (!*pszValue) + return E_OUTOFMEMORY; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accDescription(VARIANT varChild, + BSTR __RPC_FAR *pszDescription) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszDescription) + return E_INVALIDARG; + + *pszDescription = nullptr; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accDescription(kVarChildIdSelf, pszDescription); + } + + nsAutoString description; + Description(description); + + *pszDescription = ::SysAllocStringLen(description.get(), + description.Length()); + return *pszDescription ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accRole( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ VARIANT __RPC_FAR *pvarRole) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarRole) + return E_INVALIDARG; + + VariantInit(pvarRole); + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accRole(kVarChildIdSelf, pvarRole); + } + + a11y::role geckoRole; +#ifdef DEBUG + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(this), + "Does not support Text when it should"); +#endif + + geckoRole = Role(); + + uint32_t msaaRole = 0; + +#define ROLE(_geckoRole, stringRole, atkRole, macRole, \ + _msaaRole, ia2Role, nameRule) \ + case roles::_geckoRole: \ + msaaRole = _msaaRole; \ + break; + + 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 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; + } + + // -- Try enumerated role + if (msaaRole != USE_ROLE_STRING) { + pvarRole->vt = VT_I4; + pvarRole->lVal = msaaRole; // Normal enumerated role + return S_OK; + } + + // -- 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 = GetContent(); + if (!content) + return E_FAIL; + + if (content->IsElement()) { + nsAutoString roleString; + if (msaaRole != ROLE_SYSTEM_CLIENT && + !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) { + nsIDocument * document = content->GetUncomposedDoc(); + if (!document) + return E_FAIL; + + dom::NodeInfo *nodeInfo = content->NodeInfo(); + nodeInfo->GetName(roleString); + + // Only append name space if different from that of current document. + if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) { + nsAutoString nameSpaceURI; + nodeInfo->GetNamespaceURI(nameSpaceURI); + roleString += NS_LITERAL_STRING(", ") + nameSpaceURI; + } + } + + if (!roleString.IsEmpty()) { + pvarRole->vt = VT_BSTR; + pvarRole->bstrVal = ::SysAllocString(roleString.get()); + return S_OK; + } + } + + return E_FAIL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accState( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ VARIANT __RPC_FAR *pvarState) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarState) + return E_INVALIDARG; + + VariantInit(pvarState); + pvarState->vt = VT_I4; + pvarState->lVal = 0; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accState(kVarChildIdSelf, pvarState); + } + + // 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 = State(); + + uint32_t msaaState = 0; + nsAccUtils::To32States(state, &msaaState, nullptr); + pvarState->lVal = msaaState; + return S_OK; + + A11Y_TRYBLOCK_END +} + + +STDMETHODIMP +AccessibleWrap::get_accHelp( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszHelp) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszHelp) + return E_INVALIDARG; + + *pszHelp = nullptr; + return S_FALSE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accHelpTopic( + /* [out] */ BSTR __RPC_FAR *pszHelpFile, + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ long __RPC_FAR *pidTopic) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszHelpFile || !pidTopic) + return E_INVALIDARG; + + *pszHelpFile = nullptr; + *pidTopic = 0; + return S_FALSE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accKeyboardShortcut( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszKeyboardShortcut) + return E_INVALIDARG; + *pszKeyboardShortcut = nullptr; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accKeyboardShortcut(kVarChildIdSelf, + pszKeyboardShortcut); + } + + KeyBinding keyBinding = AccessKey(); + if (keyBinding.IsEmpty()) + keyBinding = KeyboardShortcut(); + + nsAutoString shortcut; + keyBinding.ToString(shortcut); + + *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(), + shortcut.Length()); + return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accFocus( + /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarChild) + return E_INVALIDARG; + + VariantInit(pvarChild); + + // VT_EMPTY: None. This object does not have the keyboard focus itself + // 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; + + // 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; + pvarChild->pdispVal = NativeAccessible(focusedAccessible); + } + else { + pvarChild->vt = VT_EMPTY; // No focus or focus is not a child + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +/** + * This helper class implements IEnumVARIANT for a nsTArray containing + * accessible objects. + */ +class AccessibleEnumerator final : public IEnumVARIANT +{ +public: + AccessibleEnumerator(const nsTArray<Accessible*>& aArray) : + mArray(aArray), mCurIndex(0) { } + AccessibleEnumerator(const AccessibleEnumerator& toCopy) : + mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { } + ~AccessibleEnumerator() { } + + // IUnknown + DECL_IUNKNOWN + + // IEnumVARIANT + STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched); + STDMETHODIMP Skip(unsigned long celt); + STDMETHODIMP Reset() + { + mCurIndex = 0; + return S_OK; + } + STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum); + +private: + nsTArray<Accessible*> mArray; + uint32_t mCurIndex; +}; + +STDMETHODIMP +AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject) +{ + A11Y_TRYBLOCK_BEGIN + + if (iid == IID_IEnumVARIANT) { + *ppvObject = static_cast<IEnumVARIANT*>(this); + AddRef(); + return S_OK; + } + if (iid == IID_IUnknown) { + *ppvObject = static_cast<IUnknown*>(this); + AddRef(); + return S_OK; + } + + *ppvObject = nullptr; + return E_NOINTERFACE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched) +{ + A11Y_TRYBLOCK_BEGIN + + uint32_t length = mArray.Length(); + HRESULT hr = S_OK; + + // Can't get more elements than there are... + if (celt > length - mCurIndex) { + hr = S_FALSE; + celt = length - mCurIndex; + } + + // Copy the elements of the array into rgvar. + for (uint32_t i = 0; i < celt; ++i, ++mCurIndex) { + rgvar[i].vt = VT_DISPATCH; + rgvar[i].pdispVal = AccessibleWrap::NativeAccessible(mArray[mCurIndex]); + } + + if (pceltFetched) + *pceltFetched = celt; + + return hr; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum) +{ + A11Y_TRYBLOCK_BEGIN + + *ppenum = new AccessibleEnumerator(*this); + if (!*ppenum) + return E_OUTOFMEMORY; + NS_ADDREF(*ppenum); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleEnumerator::Skip(unsigned long celt) +{ + A11Y_TRYBLOCK_BEGIN + + uint32_t length = mArray.Length(); + // Check if we can skip the requested number of elements + if (celt > length - mCurIndex) { + mCurIndex = length; + return S_FALSE; + } + mCurIndex += celt; + return S_OK; + + A11Y_TRYBLOCK_END +} + +/** + * This method is called when a client wants to know which children of a node + * are selected. Note that this method can only find selected children for + * accessible object which implement SelectAccessible. + * + * The VARIANT return value arguement is expected to either contain a single IAccessible + * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number + * of children selected, unless there are none selected in which case we return an empty + * VARIANT. + * + * We get the selected options from the select's accessible object and wrap + * those in an AccessibleEnumerator which we then put in the return VARIANT. + * + * returns a VT_EMPTY VARIANT if: + * - there are no selected children for this object + * - the object is not the type that can have children selected + */ +STDMETHODIMP +AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarChildren) + return E_INVALIDARG; + + VariantInit(pvarChildren); + pvarChildren->vt = VT_EMPTY; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (IsSelect()) { + AutoTArray<Accessible*, 10> selectedItems; + SelectedItems(&selectedItems); + + // 1) Create and initialize the enumeration + RefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems); + pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT + NS_ADDREF(pvarChildren->punkVal = pEnum); + } + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::get_accDefaultAction( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pszDefaultAction) + return E_INVALIDARG; + + *pszDefaultAction = nullptr; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->get_accDefaultAction(kVarChildIdSelf, pszDefaultAction); + } + + nsAutoString defaultAction; + ActionNameAt(0, defaultAction); + + *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(), + defaultAction.Length()); + return *pszDefaultAction ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::accSelect( + /* [in] */ long flagsSelect, + /* [optional][in] */ VARIANT varChild) +{ + A11Y_TRYBLOCK_BEGIN + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->accSelect(flagsSelect, kVarChildIdSelf); + } + + if (flagsSelect & SELFLAG_TAKEFOCUS) { + if (XRE_IsContentProcess()) { + // In this case we might have been invoked while the IPC MessageChannel is + // waiting on a sync reply. We cannot dispatch additional IPC while that + // is happening, so we dispatch TakeFocus from the main thread to + // guarantee that we are outside any IPC. + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewRunnableMethod(this, &Accessible::TakeFocus); + NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); + return S_OK; + } + TakeFocus(); + return S_OK; + } + + if (flagsSelect & SELFLAG_TAKESELECTION) { + TakeSelection(); + return S_OK; + } + + if (flagsSelect & SELFLAG_ADDSELECTION) { + SetSelected(true); + return S_OK; + } + + if (flagsSelect & SELFLAG_REMOVESELECTION) { + SetSelected(false); + return S_OK; + } + + return E_FAIL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::accLocation( + /* [out] */ long __RPC_FAR *pxLeft, + /* [out] */ long __RPC_FAR *pyTop, + /* [out] */ long __RPC_FAR *pcxWidth, + /* [out] */ long __RPC_FAR *pcyHeight, + /* [optional][in] */ VARIANT varChild) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) + return E_INVALIDARG; + + *pxLeft = 0; + *pyTop = 0; + *pcxWidth = 0; + *pcyHeight = 0; + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, + kVarChildIdSelf); + } + + nsIntRect rect = Bounds(); + + *pxLeft = rect.x; + *pyTop = rect.y; + *pcxWidth = rect.width; + *pcyHeight = rect.height; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::accNavigate( + /* [in] */ long navDir, + /* [optional][in] */ VARIANT varStart, + /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarEndUpAt) + return E_INVALIDARG; + + VariantInit(pvarEndUpAt); + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varStart, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->accNavigate(navDir, kVarChildIdSelf, pvarEndUpAt); + } + + Accessible* navAccessible = nullptr; + Maybe<RelationType> xpRelation; + +#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ + case msaaType: \ + xpRelation.emplace(RelationType::geckoType); \ + break; + + switch(navDir) { + case NAVDIR_FIRSTCHILD: + if (IsProxy()) { + if (!Proxy()->MustPruneChildren()) { + navAccessible = WrapperFor(Proxy()->FirstChild()); + } + } else { + if (!nsAccUtils::MustPrune(this)) + navAccessible = FirstChild(); + } + break; + case NAVDIR_LASTCHILD: + if (IsProxy()) { + if (!Proxy()->MustPruneChildren()) { + navAccessible = WrapperFor(Proxy()->LastChild()); + } + } else { + if (!nsAccUtils::MustPrune(this)) + navAccessible = LastChild(); + } + break; + case NAVDIR_NEXT: + navAccessible = IsProxy() + ? WrapperFor(Proxy()->NextSibling()) + : NextSibling(); + break; + case NAVDIR_PREVIOUS: + navAccessible = IsProxy() + ? WrapperFor(Proxy()->PrevSibling()) + : PrevSibling(); + break; + case NAVDIR_DOWN: + case NAVDIR_LEFT: + case NAVDIR_RIGHT: + case NAVDIR_UP: + return E_NOTIMPL; + + // MSAA relationship extensions to accNavigate +#include "RelationTypeMap.h" + + default: + return E_INVALIDARG; + } + +#undef RELATIONTYPE + + pvarEndUpAt->vt = VT_EMPTY; + + if (xpRelation) { + Relation rel = RelationByType(*xpRelation); + navAccessible = rel.Next(); + } + + if (!navAccessible) + return E_FAIL; + + pvarEndUpAt->pdispVal = NativeAccessible(navAccessible); + pvarEndUpAt->vt = VT_DISPATCH; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::accHitTest( + /* [in] */ long xLeft, + /* [in] */ long yTop, + /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) +{ + A11Y_TRYBLOCK_BEGIN + + if (!pvarChild) + return E_INVALIDARG; + + VariantInit(pvarChild); + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + 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; + } else { // its not create an Accessible for it. + pvarChild->vt = VT_DISPATCH; + pvarChild->pdispVal = NativeAccessible(accessible); + } + } else { + // no child at that point + pvarChild->vt = VT_EMPTY; + return S_FALSE; + } + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::accDoDefaultAction( + /* [optional][in] */ VARIANT varChild) +{ + A11Y_TRYBLOCK_BEGIN + + RefPtr<IAccessible> accessible; + HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible)); + if (FAILED(hr)) { + return hr; + } + + if (accessible) { + return accessible->accDoDefaultAction(kVarChildIdSelf); + } + + return DoAction(0) ? S_OK : E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +AccessibleWrap::put_accName( + /* [optional][in] */ VARIANT varChild, + /* [in] */ BSTR szName) +{ + return E_NOTIMPL; +} + +STDMETHODIMP +AccessibleWrap::put_accValue( + /* [optional][in] */ VARIANT varChild, + /* [in] */ BSTR szValue) +{ + return E_NOTIMPL; +} + +//////////////////////////////////////////////////////////////////////////////// +// IDispatch + +STDMETHODIMP +AccessibleWrap::GetTypeInfoCount(UINT *pctinfo) +{ + if (!pctinfo) + return E_INVALIDARG; + + *pctinfo = 1; + return S_OK; +} + +STDMETHODIMP +AccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + if (!ppTInfo) + return E_INVALIDARG; + + *ppTInfo = nullptr; + + if (iTInfo != 0) + return DISP_E_BADINDEX; + + ITypeInfo * typeInfo = GetTI(lcid); + if (!typeInfo) + return E_FAIL; + + typeInfo->AddRef(); + *ppTInfo = typeInfo; + + return S_OK; +} + +STDMETHODIMP +AccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ITypeInfo *typeInfo = GetTI(lcid); + if (!typeInfo) + return E_FAIL; + + HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId); + return hr; +} + +STDMETHODIMP +AccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + ITypeInfo *typeInfo = GetTI(lcid); + if (!typeInfo) + return E_FAIL; + + return typeInfo->Invoke(static_cast<IAccessible*>(this), dispIdMember, + wFlags, pDispParams, pVarResult, pExcepInfo, + puArgErr); +} + +void +AccessibleWrap::GetNativeInterface(void** aOutAccessible) +{ + *aOutAccessible = static_cast<IAccessible*>(this); + NS_ADDREF_THIS(); +} + +void +AccessibleWrap::SetID(uint32_t aID) +{ + MOZ_ASSERT(XRE_IsParentProcess() && IsProxy()); + mID = aID; +} + +void +AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, + "MSAA event map skewed"); + + NS_ASSERTION(aEventType > 0 && aEventType < ArrayLength(gWinEventMap), "invalid event type"); + + uint32_t winEvent = gWinEventMap[aEventType]; + if (!winEvent) + return; + + int32_t childID = GetChildIDFor(aTarget); + if (!childID) + return; // Can't fire an event without a child ID + + HWND hwnd = GetHWNDFor(aTarget); + if (!hwnd) { + return; + } + + // Fire MSAA event for client area window. + ::NotifyWinEvent(winEvent, hwnd, OBJID_CLIENT, childID); + + // JAWS announces collapsed combobox navigation based on focus events. + if (aEventType == nsIAccessibleEvent::EVENT_SELECTION && + Compatibility::IsJAWS()) { + roles::Role role = aTarget->IsProxy() ? aTarget->Proxy()->Role() : + aTarget->Role(); + if (role == roles::COMBOBOX_OPTION) { + ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, childID); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Accessible + +nsresult +AccessibleWrap::HandleAccEvent(AccEvent* aEvent) +{ + nsresult rv = Accessible::HandleAccEvent(aEvent); + NS_ENSURE_SUCCESS(rv, rv); + + if (IPCAccessibilityActive()) { + return NS_OK; + } + + uint32_t eventType = aEvent->GetEventType(); + + // Means we're not active. + NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE); + + Accessible* accessible = aEvent->GetAccessible(); + if (!accessible) + return NS_OK; + + if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED || + eventType == nsIAccessibleEvent::EVENT_FOCUS) { + UpdateSystemCaretFor(accessible); + } + + FireWinEvent(accessible, eventType); + + return NS_OK; +} + +DocProxyAccessibleWrap* +AccessibleWrap::DocProxyWrapper() const +{ + MOZ_ASSERT(IsProxy()); + + ProxyAccessible* proxy = Proxy(); + if (!proxy) { + return nullptr; + } + + AccessibleWrap* acc = WrapperFor(proxy->Document()); + MOZ_ASSERT(acc->IsDoc()); + + return static_cast<DocProxyAccessibleWrap*>(acc); +} + +//////////////////////////////////////////////////////////////////////////////// +// AccessibleWrap + +//------- Helper methods --------- + +int32_t +AccessibleWrap::GetChildIDFor(Accessible* aAccessible) +{ + // A child ID of the window is required, when we use NotifyWinEvent, + // so that the 3rd party application can call back and get the IAccessible + // the event occurred on. + + if (!aAccessible) { + return 0; + } + + // Chrome should use mID which has been generated by the content process. + if (aAccessible->IsProxy()) { + const uint32_t id = static_cast<AccessibleWrap*>(aAccessible)->mID; + MOZ_ASSERT(id != kNoID); + return id; + } + + if (!aAccessible->Document()) + return 0; + + uint32_t* id = & static_cast<AccessibleWrap*>(aAccessible)->mID; + if (*id != kNoID) + return *id; + + *id = sIDGen.GetID(); + + MOZ_ASSERT(!aAccessible->IsProxy()); + DocAccessibleWrap* doc = + static_cast<DocAccessibleWrap*>(aAccessible->Document()); + doc->AddID(*id, static_cast<AccessibleWrap*>(aAccessible)); + + return *id; +} + +HWND +AccessibleWrap::GetHWNDFor(Accessible* aAccessible) +{ + if (!aAccessible) { + return nullptr; + } + + if (XRE_IsContentProcess()) { + DocAccessible* doc = aAccessible->Document(); + if (!doc) { + return nullptr; + } + + DocAccessibleChild* ipcDoc = doc->IPCDoc(); + if (!ipcDoc) { + return nullptr; + } + + auto tab = static_cast<dom::TabChild*>(ipcDoc->Manager()); + MOZ_ASSERT(tab); + return reinterpret_cast<HWND>(tab->GetNativeWindowHandle()); + } + + // Accessibles in child processes are said to have the HWND of the window + // their tab is within. Popups are always in the parent process, and so + // never proxied, which means this is basically correct. + if (aAccessible->IsProxy()) { + ProxyAccessible* proxy = aAccessible->Proxy(); + if (!proxy) { + return nullptr; + } + + Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser(); + NS_ASSERTION(outerDoc, "no outer doc for accessible remote tab!"); + if (!outerDoc) { + return nullptr; + } + + return GetHWNDFor(outerDoc); + } + + DocAccessible* document = aAccessible->Document(); + if(!document) + return nullptr; + + // Popup lives in own windows, use its HWND until the popup window is + // hidden to make old JAWS versions work with collapsed comboboxes (see + // discussion in bug 379678). + nsIFrame* frame = aAccessible->GetFrame(); + if (frame) { + nsIWidget* widget = frame->GetNearestWidget(); + if (widget && widget->IsVisible()) { + nsIPresShell* shell = document->PresShell(); + nsViewManager* vm = shell->GetViewManager(); + if (vm) { + nsCOMPtr<nsIWidget> rootWidget; + vm->GetRootWidget(getter_AddRefs(rootWidget)); + // Make sure the accessible belongs to popup. If not then use + // document HWND (which might be different from root widget in the + // case of window emulation). + if (rootWidget != widget) + return static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW)); + } + } + } + + return static_cast<HWND>(document->GetNativeWindow()); +} + +IDispatch* +AccessibleWrap::NativeAccessible(Accessible* aAccessible) +{ + if (!aAccessible) { + NS_WARNING("Not passing in an aAccessible"); + return nullptr; + } + + IAccessible* msaaAccessible = nullptr; + aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible)); + return static_cast<IDispatch*>(msaaAccessible); +} + +static Accessible* +GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID) +{ + Accessible* child = static_cast<DocAccessibleWrap*>(aDoc)->GetAccessibleByID(aID); + if (child) + return child; + + uint32_t childDocCount = aDoc->ChildDocumentCount(); + for (uint32_t i = 0; i < childDocCount; i++) { + child = GetAccessibleInSubtree(aDoc->GetChildDocumentAt(i), aID); + if (child) + return child; + } + + return nullptr; + } + +static already_AddRefed<IDispatch> +GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, + const VARIANT& aVarChild) +{ + auto wrapper = static_cast<DocProxyAccessibleWrap*>(WrapperFor(aDoc)); + RefPtr<IAccessible> comProxy; + int32_t wrapperChildId = AccessibleWrap::GetChildIDFor(wrapper); + if (wrapperChildId == aVarChild.lVal) { + wrapper->GetNativeInterface(getter_AddRefs(comProxy)); + return comProxy.forget(); + } + + MOZ_ASSERT(aDoc->IsTopLevel()); + if (!aDoc->IsTopLevel()) { + return nullptr; + } + + wrapper->GetNativeInterface(getter_AddRefs(comProxy)); + MOZ_ASSERT(comProxy); + if (!comProxy) { + return nullptr; + } + + RefPtr<IDispatch> disp; + if (FAILED(comProxy->get_accChild(aVarChild, getter_AddRefs(disp)))) { + return nullptr; + } + + return disp.forget(); +} + +already_AddRefed<IAccessible> +AccessibleWrap::GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct) +{ + if (aVarChild.vt != VT_I4) + return nullptr; + + VARIANT varChild = aVarChild; + + MOZ_ASSERT(aIsDefunct); + *aIsDefunct = false; + + RefPtr<IAccessible> result; + + if (varChild.lVal == CHILDID_SELF) { + *aIsDefunct = IsDefunct(); + if (*aIsDefunct) { + return nullptr; + } + GetNativeInterface(getter_AddRefs(result)); + if (result) { + return result.forget(); + } + // If we're not a proxy, there's nothing more we can do to attempt to + // resolve the IAccessible, so we just fail. + if (!IsProxy()) { + return nullptr; + } + // Otherwise, since we're a proxy and we have a null native interface, this + // indicates that we need to obtain a COM proxy. To do this, we'll replace + // CHILDID_SELF with our real MSAA ID and continue the search from there. + varChild.lVal = GetExistingID(); + } + + if (IsProxy() ? Proxy()->MustPruneChildren() : nsAccUtils::MustPrune(this)) { + return nullptr; + } + + // If the MSAA ID is not a chrome id then we already know that we won't + // find it here and should look remotely instead. + if (XRE_IsParentProcess() && !sIDGen.IsChromeID(varChild.lVal)) { + return GetRemoteIAccessibleFor(varChild); + } + MOZ_ASSERT(XRE_IsParentProcess() || + sIDGen.IsIDForThisContentProcess(varChild.lVal)); + + if (varChild.lVal > 0) { + // Gecko child indices are 0-based in contrast to indices used in MSAA. + MOZ_ASSERT(!IsProxy()); + Accessible* xpAcc = GetChildAt(varChild.lVal - 1); + if (!xpAcc) { + return nullptr; + } + *aIsDefunct = xpAcc->IsDefunct(); + static_cast<AccessibleWrap*>(xpAcc)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); + } + + // 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. + // First handle the case that both this accessible and the id'd one are in + // this process. + if (!IsProxy()) { + DocAccessible* document = Document(); + Accessible* child = + GetAccessibleInSubtree(document, static_cast<uint32_t>(varChild.lVal)); + + // If it is a document then just return an accessible. + if (child && IsDoc()) { + *aIsDefunct = child->IsDefunct(); + static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); + } + + // Otherwise check whether the accessible is a child (this path works for + // ARIA documents and popups). + Accessible* parent = child; + while (parent && parent != document) { + if (parent == this) { + *aIsDefunct = child->IsDefunct(); + static_cast<AccessibleWrap*>(child)->GetNativeInterface(getter_AddRefs(result)); + return result.forget(); + } + + parent = parent->Parent(); + } + } + + // Now see about the case that both this accessible and the target one are + // proxied. + if (IsProxy()) { + DocAccessibleParent* proxyDoc = Proxy()->Document(); + RefPtr<IDispatch> disp = GetProxiedAccessibleInSubtree(proxyDoc, varChild); + if (!disp) { + return nullptr; + } + + MOZ_ASSERT(mscom::IsProxy(disp)); + DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible, + getter_AddRefs(result)); + MOZ_ASSERT(SUCCEEDED(hr)); + return result.forget(); + } + + return nullptr; +} + +already_AddRefed<IAccessible> +AccessibleWrap::GetRemoteIAccessibleFor(const VARIANT& aVarChild) +{ + DocAccessibleParent* proxyDoc = nullptr; + DocAccessible* doc = Document(); + const nsTArray<DocAccessibleParent*>* remoteDocs = + DocManager::TopLevelRemoteDocs(); + if (!remoteDocs) { + return nullptr; + } + + RefPtr<IAccessible> result; + + size_t docCount = remoteDocs->Length(); + for (size_t i = 0; i < docCount; i++) { + DocAccessibleParent* remoteDoc = remoteDocs->ElementAt(i); + + uint32_t remoteDocMsaaId = WrapperFor(remoteDoc)->GetExistingID(); + if (!sIDGen.IsSameContentProcessFor(aVarChild.lVal, remoteDocMsaaId)) { + continue; + } + + Accessible* outerDoc = remoteDoc->OuterDocOfRemoteBrowser(); + if (!outerDoc) { + continue; + } + + if (outerDoc->Document() != doc) { + continue; + } + + RefPtr<IDispatch> disp = + GetProxiedAccessibleInSubtree(remoteDoc, aVarChild); + if (!disp) { + continue; + } + + DebugOnly<HRESULT> hr = disp->QueryInterface(IID_IAccessible, + getter_AddRefs(result)); + MOZ_ASSERT(SUCCEEDED(hr)); + return result.forget(); + } + + return nullptr; +} + +void +AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) +{ + // Move the system caret so that Windows Tablet Edition and tradional ATs with + // off-screen model can follow the caret + ::DestroyCaret(); + + HyperTextAccessible* text = aAccessible->AsHyperText(); + if (!text) + return; + + nsIWidget* widget = nullptr; + LayoutDeviceIntRect caretRect = text->GetCaretRect(&widget); + HWND caretWnd; + if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { + return; + } + + // Create invisible bitmap for caret, otherwise its appearance interferes + // with Gecko caret + HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr); + if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret + ::ShowCaret(caretWnd); + RECT windowRect; + ::GetWindowRect(caretWnd, &windowRect); + ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top); + ::DeleteObject(caretBitMap); + } +} + +ITypeInfo* +AccessibleWrap::GetTI(LCID lcid) +{ + if (gTypeInfo) + return gTypeInfo; + + ITypeLib *typeLib = nullptr; + HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib); + if (FAILED(hr)) + return nullptr; + + hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo); + typeLib->Release(); + + if (FAILED(hr)) + return nullptr; + + return gTypeInfo; +} + +/* static */ +uint32_t +AccessibleWrap::GetContentProcessIdFor(dom::ContentParentId aIPCContentId) +{ + return sIDGen.GetContentProcessIDFor(aIPCContentId); +} + +/* static */ +void +AccessibleWrap::ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId) +{ + sIDGen.ReleaseContentProcessIDFor(aIPCContentId); +} diff --git a/accessible/windows/msaa/AccessibleWrap.h b/accessible/windows/msaa/AccessibleWrap.h new file mode 100644 index 000000000..eb97c2667 --- /dev/null +++ b/accessible/windows/msaa/AccessibleWrap.h @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_AccessibleWrap_h_ +#define mozilla_a11y_AccessibleWrap_h_ + +#include "nsCOMPtr.h" +#include "Accessible.h" +#include "Accessible2.h" +#include "ia2Accessible.h" +#include "ia2AccessibleComponent.h" +#include "ia2AccessibleHyperlink.h" +#include "ia2AccessibleValue.h" +#include "mozilla/a11y/MsaaIdGenerator.h" +#include "mozilla/a11y/ProxyAccessible.h" +#include "mozilla/Attributes.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 + +namespace mozilla { +namespace a11y { +class DocProxyAccessibleWrap; + +class AccessibleWrap : public Accessible, + public ia2Accessible, + public ia2AccessibleComponent, + public ia2AccessibleHyperlink, + public ia2AccessibleValue +{ +public: // construction, destruction + AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc); + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + public: // IUnknown methods - see iunknown.h for documentation + STDMETHODIMP QueryInterface(REFIID, void**) override; + + // Return the registered OLE class ID of this object's CfDataObj. + CLSID GetClassID() const; + + public: // COM interface IAccessible + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accParent( + /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accChildCount( + /* [retval][out] */ long __RPC_FAR *pcountChildren) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accChild( + /* [in] */ VARIANT varChild, + /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accName( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszName) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszValue) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accDescription( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszDescription) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accRole( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ VARIANT __RPC_FAR *pvarRole) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accState( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ VARIANT __RPC_FAR *pvarState) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accHelp( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszHelp) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accHelpTopic( + /* [out] */ BSTR __RPC_FAR *pszHelpFile, + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ long __RPC_FAR *pidTopic) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accKeyboardShortcut( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accFocus( + /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accSelection( + /* [retval][out] */ VARIANT __RPC_FAR *pvarChildren) override; + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accDefaultAction( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction) override; + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE accSelect( + /* [in] */ long flagsSelect, + /* [optional][in] */ VARIANT varChild) override; + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE accLocation( + /* [out] */ long __RPC_FAR *pxLeft, + /* [out] */ long __RPC_FAR *pyTop, + /* [out] */ long __RPC_FAR *pcxWidth, + /* [out] */ long __RPC_FAR *pcyHeight, + /* [optional][in] */ VARIANT varChild) override; + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE accNavigate( + /* [in] */ long navDir, + /* [optional][in] */ VARIANT varStart, + /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt) override; + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE accHitTest( + /* [in] */ long xLeft, + /* [in] */ long yTop, + /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) override; + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE accDoDefaultAction( + /* [optional][in] */ VARIANT varChild) override; + + virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_accName( + /* [optional][in] */ VARIANT varChild, + /* [in] */ BSTR szName) override; + + virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_accValue( + /* [optional][in] */ VARIANT varChild, + /* [in] */ BSTR szValue) override; + + // IDispatch (support of scripting languages like VB) + virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) override; + + virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) override; + + virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId) override; + + virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) override; + + // Accessible + virtual nsresult HandleAccEvent(AccEvent* aEvent) override; + virtual void Shutdown() override; + + // Helper methods + static int32_t GetChildIDFor(Accessible* aAccessible); + static HWND GetHWNDFor(Accessible* aAccessible); + + static void FireWinEvent(Accessible* aTarget, uint32_t aEventType); + + /** + * System caret support: update the Windows caret position. + * The system caret works more universally than the MSAA caret + * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it + * We will use an invisible system caret. + * Gecko is still responsible for drawing its own caret + */ + void UpdateSystemCaretFor(Accessible* aAccessible); + + /** + * Find an accessible by the given child ID in cached documents. + */ + MOZ_MUST_USE already_AddRefed<IAccessible> + GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct); + + virtual void GetNativeInterface(void **aOutAccessible) override; + + static IDispatch* NativeAccessible(Accessible* aAccessible); + + uint32_t GetExistingID() const { return mID; } + static const uint32_t kNoID = 0; + void SetID(uint32_t aID); + + static uint32_t GetContentProcessIdFor(dom::ContentParentId aIPCContentId); + static void ReleaseContentProcessIdFor(dom::ContentParentId aIPCContentId); + +protected: + virtual ~AccessibleWrap(); + + uint32_t mID; + + HRESULT + ResolveChild(const VARIANT& aVarChild, IAccessible** aOutInterface); + + /** + * Find a remote accessible by the given child ID. + */ + MOZ_MUST_USE already_AddRefed<IAccessible> + GetRemoteIAccessibleFor(const VARIANT& aVarChild); + + /** + * Return the wrapper for the document's proxy. + */ + DocProxyAccessibleWrap* DocProxyWrapper() const; + + /** + * Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it. + */ + static ITypeInfo* GetTI(LCID lcid); + + static ITypeInfo* gTypeInfo; + + static MsaaIdGenerator sIDGen; + + enum navRelations { + NAVRELATION_CONTROLLED_BY = 0x1000, + NAVRELATION_CONTROLLER_FOR = 0x1001, + NAVRELATION_LABEL_FOR = 0x1002, + NAVRELATION_LABELLED_BY = 0x1003, + NAVRELATION_MEMBER_OF = 0x1004, + NAVRELATION_NODE_CHILD_OF = 0x1005, + NAVRELATION_FLOWS_TO = 0x1006, + NAVRELATION_FLOWS_FROM = 0x1007, + NAVRELATION_SUBWINDOW_OF = 0x1008, + NAVRELATION_EMBEDS = 0x1009, + NAVRELATION_EMBEDDED_BY = 0x100a, + NAVRELATION_POPUP_FOR = 0x100b, + NAVRELATION_PARENT_WINDOW_OF = 0x100c, + NAVRELATION_DEFAULT_BUTTON = 0x100d, + NAVRELATION_DESCRIBED_BY = 0x100e, + NAVRELATION_DESCRIPTION_FOR = 0x100f, + NAVRELATION_NODE_PARENT_OF = 0x1010, + NAVRELATION_CONTAINING_DOCUMENT = 0x1011, + NAVRELATION_CONTAINING_TAB_PANE = 0x1012, + NAVRELATION_CONTAINING_APPLICATION = 0x1014, + NAVRELATION_DETAILS = 0x1015, + NAVRELATION_DETAILS_FOR = 0x1016, + NAVRELATION_ERROR = 0x1017, + NAVRELATION_ERROR_FOR = 0x1018 + }; +}; + +static inline AccessibleWrap* +WrapperFor(const 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 +#undef GetBinaryType +#undef RemoveDirectory +#endif + +#endif diff --git a/accessible/windows/msaa/ApplicationAccessibleWrap.cpp b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp new file mode 100644 index 000000000..b78a8dd55 --- /dev/null +++ b/accessible/windows/msaa/ApplicationAccessibleWrap.cpp @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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 "ApplicationAccessibleWrap.h" + +#include "AccessibleApplication_i.c" +#include "IUnknownImpl.h" + +#include "nsIGfxInfo.h" +#include "nsIPersistentProperties2.h" +#include "nsServiceManagerUtils.h" +#include "mozilla/Services.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// nsISupports +NS_IMPL_ISUPPORTS_INHERITED0(ApplicationAccessibleWrap, + ApplicationAccessible) + +already_AddRefed<nsIPersistentProperties> +ApplicationAccessibleWrap::NativeAttributes() +{ + nsCOMPtr<nsIPersistentProperties> attributes = + do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID); + + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + if (gfxInfo) { + bool isD2DEnabled = false; + gfxInfo->GetD2DEnabled(&isD2DEnabled); + nsAutoString unused; + attributes->SetStringProperty( + NS_LITERAL_CSTRING("D2D"), + isD2DEnabled ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"), + unused); + } + + return attributes.forget(); +} + +//////////////////////////////////////////////////////////////////////////////// +// IUnknown + +STDMETHODIMP +ApplicationAccessibleWrap::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleApplication == iid) { + *ppv = static_cast<IAccessibleApplication*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return AccessibleWrap::QueryInterface(iid, ppv); +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessibleApplication + +STDMETHODIMP +ApplicationAccessibleWrap::get_appName(BSTR* aName) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aName) + return E_INVALIDARG; + + *aName = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString name; + AppName(name); + if (name.IsEmpty()) + return S_FALSE; + + *aName = ::SysAllocStringLen(name.get(), name.Length()); + return *aName ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ApplicationAccessibleWrap::get_appVersion(BSTR* aVersion) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aVersion) + return E_INVALIDARG; + + *aVersion = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString version; + AppVersion(version); + if (version.IsEmpty()) + return S_FALSE; + + *aVersion = ::SysAllocStringLen(version.get(), version.Length()); + return *aVersion ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ApplicationAccessibleWrap::get_toolkitName(BSTR* aName) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aName) + return E_INVALIDARG; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString name; + PlatformName(name); + if (name.IsEmpty()) + return S_FALSE; + + *aName = ::SysAllocStringLen(name.get(), name.Length()); + return *aName ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ApplicationAccessibleWrap::get_toolkitVersion(BSTR* aVersion) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aVersion) + return E_INVALIDARG; + + *aVersion = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString version; + PlatformVersion(version); + if (version.IsEmpty()) + return S_FALSE; + + *aVersion = ::SysAllocStringLen(version.get(), version.Length()); + return *aVersion ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/msaa/ApplicationAccessibleWrap.h b/accessible/windows/msaa/ApplicationAccessibleWrap.h new file mode 100644 index 000000000..a427e98f5 --- /dev/null +++ b/accessible/windows/msaa/ApplicationAccessibleWrap.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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/. */ + +#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__ +#define mozilla_a11y_ApplicationAccessibleWrap_h__ + +#include "ApplicationAccessible.h" + +#include "AccessibleApplication.h" + +namespace mozilla { +namespace a11y { + +class ApplicationAccessibleWrap: public ApplicationAccessible, + public IAccessibleApplication +{ + ~ApplicationAccessibleWrap() {} + +public: + // nsISupporst + NS_DECL_ISUPPORTS_INHERITED + + // nsAccessible + virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override; + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleApplication + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_appName( + /* [retval][out] */ BSTR *name); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_appVersion( + /* [retval][out] */ BSTR *version); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitName( + /* [retval][out] */ BSTR *name); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_toolkitVersion( + /* [retval][out] */ BSTR *version); + +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/windows/msaa/Compatibility.cpp b/accessible/windows/msaa/Compatibility.cpp new file mode 100644 index 000000000..31026c586 --- /dev/null +++ b/accessible/windows/msaa/Compatibility.cpp @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "Compatibility.h" + +#include "nsWinUtils.h" +#include "Statistics.h" + +#include "mozilla/Preferences.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +/** + * Return true if module version is lesser than the given version. + */ +bool +IsModuleVersionLessThan(HMODULE aModuleHandle, DWORD aMajor, DWORD aMinor) +{ + wchar_t fileName[MAX_PATH]; + ::GetModuleFileNameW(aModuleHandle, fileName, MAX_PATH); + + DWORD dummy = 0; + DWORD length = ::GetFileVersionInfoSizeW(fileName, &dummy); + + LPBYTE versionInfo = new BYTE[length]; + ::GetFileVersionInfoW(fileName, 0, length, versionInfo); + + UINT uLen; + VS_FIXEDFILEINFO* fixedFileInfo = nullptr; + ::VerQueryValueW(versionInfo, L"\\", (LPVOID*)&fixedFileInfo, &uLen); + DWORD dwFileVersionMS = fixedFileInfo->dwFileVersionMS; + DWORD dwFileVersionLS = fixedFileInfo->dwFileVersionLS; + delete [] versionInfo; + + DWORD dwLeftMost = HIWORD(dwFileVersionMS); + DWORD dwSecondRight = HIWORD(dwFileVersionLS); + return (dwLeftMost < aMajor || + (dwLeftMost == aMajor && dwSecondRight < aMinor)); +} + + +//////////////////////////////////////////////////////////////////////////////// +// Compatibility +//////////////////////////////////////////////////////////////////////////////// + +uint32_t Compatibility::sConsumers = Compatibility::UNKNOWN; + +void +Compatibility::Init() +{ + // Note we collect some AT statistics/telemetry here for convenience. + + HMODULE jawsHandle = ::GetModuleHandleW(L"jhook"); + if (jawsHandle) + sConsumers |= (IsModuleVersionLessThan(jawsHandle, 8, 2173)) ? + OLDJAWS : JAWS; + + if (::GetModuleHandleW(L"gwm32inc")) + sConsumers |= WE; + + if (::GetModuleHandleW(L"dolwinhk")) + sConsumers |= DOLPHIN; + + if (::GetModuleHandleW(L"STSA32")) + sConsumers |= SEROTEK; + + if (::GetModuleHandleW(L"nvdaHelperRemote")) + sConsumers |= NVDA; + + if (::GetModuleHandleW(L"OsmHooks")) + sConsumers |= COBRA; + + if (::GetModuleHandleW(L"WebFinderRemote")) + sConsumers |= ZOOMTEXT; + + if (::GetModuleHandleW(L"Kazahook")) + sConsumers |= KAZAGURU; + + if (::GetModuleHandleW(L"TextExtractorImpl32") || + ::GetModuleHandleW(L"TextExtractorImpl64")) + sConsumers |= YOUDAO; + + if (::GetModuleHandleW(L"uiautomation") || + ::GetModuleHandleW(L"uiautomationcore")) + sConsumers |= UIAUTOMATION; + + // If we have a known consumer remove the unknown bit. + if (sConsumers != Compatibility::UNKNOWN) + sConsumers ^= Compatibility::UNKNOWN; + + // Gather telemetry + uint32_t temp = sConsumers; + for (int i = 0; temp; i++) { + if (temp & 0x1) + statistics::A11yConsumers(i); + + temp >>= 1; + } + + // Turn off new tab switching for Jaws and WE. + if (sConsumers & (JAWS | OLDJAWS | WE)) { + // Check to see if the pref for disallowing CtrlTab is already set. If so, + // bail out (respect the user settings). If not, set it. + if (!Preferences::HasUserValue("browser.ctrlTab.disallowForScreenReaders")) + Preferences::SetBool("browser.ctrlTab.disallowForScreenReaders", true); + } +} + diff --git a/accessible/windows/msaa/Compatibility.h b/accessible/windows/msaa/Compatibility.h new file mode 100644 index 000000000..dd9a82f7c --- /dev/null +++ b/accessible/windows/msaa/Compatibility.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef COMPATIBILITY_MANAGER_H +#define COMPATIBILITY_MANAGER_H + +#include <stdint.h> + +namespace mozilla { +namespace a11y { + +/** + * Used to get compatibility modes. Note, modes are computed at accessibility + * start up time and aren't changed during lifetime. + */ +class Compatibility +{ +public: + /** + * Return true if IAccessible2 disabled. + */ + static bool IsIA2Off() { return !!(sConsumers & OLDJAWS); } + + /** + * Return true if JAWS mode is enabled. + */ + static bool IsJAWS() { return !!(sConsumers & (JAWS | OLDJAWS)); } + + /** + * Return true if WE mode is enabled. + */ + static bool IsWE() { return !!(sConsumers & WE); } + + /** + * Return true if Dolphin mode is enabled. + */ + static bool IsDolphin() { return !!(sConsumers & DOLPHIN); } + +private: + Compatibility(); + Compatibility(const Compatibility&); + Compatibility& operator = (const Compatibility&); + + /** + * Initialize compatibility mode. Called by platform (see Platform.h) during + * accessibility initialization. + */ + static void Init(); + friend void PlatformInit(); + + /** + * List of detected consumers of a11y (used for statistics/telemetry and compat) + */ + enum { + NVDA = 1 << 0, + JAWS = 1 << 1, + OLDJAWS = 1 << 2, + WE = 1 << 3, + DOLPHIN = 1 << 4, + SEROTEK = 1 << 5, + COBRA = 1 << 6, + ZOOMTEXT = 1 << 7, + KAZAGURU = 1 << 8, + YOUDAO = 1 << 9, + UNKNOWN = 1 << 10, + UIAUTOMATION = 1 << 11 + }; + +private: + static uint32_t sConsumers; +}; + +} // a11y namespace +} // mozilla namespace + +#endif diff --git a/accessible/windows/msaa/DocAccessibleWrap.cpp b/accessible/windows/msaa/DocAccessibleWrap.cpp new file mode 100644 index 000000000..6fb89816d --- /dev/null +++ b/accessible/windows/msaa/DocAccessibleWrap.cpp @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "DocAccessibleWrap.h" + +#include "Compatibility.h" +#include "DocAccessibleChild.h" +#include "nsWinUtils.h" +#include "Role.h" +#include "RootAccessible.h" +#include "sdnDocAccessible.h" +#include "Statistics.h" + +#include "nsIDocShell.h" +#include "nsIInterfaceRequestorUtils.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// DocAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +DocAccessibleWrap:: + DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) : + DocAccessible(aDocument, aPresShell), mHWND(nullptr) +{ +} + +DocAccessibleWrap::~DocAccessibleWrap() +{ +} + +IMPL_IUNKNOWN_QUERY_HEAD(DocAccessibleWrap) + if (aIID == IID_ISimpleDOMDocument) { + statistics::ISimpleDOMUsed(); + *aInstancePtr = static_cast<ISimpleDOMDocument*>(new sdnDocAccessible(this)); + static_cast<IUnknown*>(*aInstancePtr)->AddRef(); + return S_OK; + } +IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(HyperTextAccessibleWrap) + +STDMETHODIMP +DocAccessibleWrap::get_accParent( + /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) +{ + // We might be a top-level document in a content process. + DocAccessibleChild* ipcDoc = IPCDoc(); + if (!ipcDoc) { + return DocAccessible::get_accParent(ppdispParent); + } + IAccessible* dispParent = ipcDoc->GetParentIAccessible(); + if (!dispParent) { + return S_FALSE; + } + + dispParent->AddRef(); + *ppdispParent = static_cast<IDispatch*>(dispParent); + return S_OK; +} + +STDMETHODIMP +DocAccessibleWrap::get_accValue(VARIANT aVarChild, BSTR __RPC_FAR* aValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aValue) + return E_INVALIDARG; + *aValue = nullptr; + + // For backwards-compat, we still support old MSAA hack to provide URL in accValue + // Check for real value first + HRESULT hr = AccessibleWrap::get_accValue(aVarChild, aValue); + if (FAILED(hr) || *aValue || aVarChild.lVal != CHILDID_SELF) + return hr; + + // If document is being used to create a widget, don't use the URL hack + roles::Role role = Role(); + if (role != roles::DOCUMENT && role != roles::APPLICATION && + role != roles::DIALOG && role != roles::ALERT) + return hr; + + nsAutoString url; + URL(url); + if (url.IsEmpty()) + return S_FALSE; + + *aValue = ::SysAllocStringLen(url.get(), url.Length()); + return *aValue ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +//////////////////////////////////////////////////////////////////////////////// +// Accessible + +void +DocAccessibleWrap::Shutdown() +{ + // Do window emulation specific shutdown if emulation was started. + if (nsWinUtils::IsWindowEmulationStarted()) { + // Destroy window created for root document. + if (mDocFlags & eTabDocument) { + HWND hWnd = static_cast<HWND>(mHWND); + ::RemovePropW(hWnd, kPropNameDocAcc); + ::DestroyWindow(hWnd); + } + + mHWND = nullptr; + } + + DocAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DocAccessible public + +void* +DocAccessibleWrap::GetNativeWindow() const +{ + return mHWND ? mHWND : DocAccessible::GetNativeWindow(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DocAccessible protected + +void +DocAccessibleWrap::DoInitialUpdate() +{ + DocAccessible::DoInitialUpdate(); + + if (nsWinUtils::IsWindowEmulationStarted()) { + // Create window for tab document. + if (mDocFlags & eTabDocument) { + a11y::RootAccessible* rootDocument = RootAccessible(); + bool isActive = true; + nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0); + if (Compatibility::IsDolphin()) { + rect = Bounds(); + nsIntRect rootRect = rootDocument->Bounds(); + rect.x = rootRect.x - rect.x; + rect.y -= rootRect.y; + + nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer(); + nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container); + docShell->GetIsActive(&isActive); + } + + HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow()); + mHWND = nsWinUtils::CreateNativeWindow(kClassNameTabContent, parentWnd, + rect.x, rect.y, + rect.width, rect.height, isActive); + + ::SetPropW(static_cast<HWND>(mHWND), kPropNameDocAcc, (HANDLE)this); + + } else { + DocAccessible* parentDocument = ParentDocument(); + if (parentDocument) + mHWND = parentDocument->GetNativeWindow(); + } + } +} diff --git a/accessible/windows/msaa/DocAccessibleWrap.h b/accessible/windows/msaa/DocAccessibleWrap.h new file mode 100644 index 000000000..effa23848 --- /dev/null +++ b/accessible/windows/msaa/DocAccessibleWrap.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_DocAccessibleWrap_h__ +#define mozilla_a11y_DocAccessibleWrap_h__ + +#include "DocAccessible.h" + +namespace mozilla { +namespace a11y { + +class DocAccessibleWrap : public DocAccessible +{ +public: + DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell); + virtual ~DocAccessibleWrap(); + + DECL_IUNKNOWN_INHERITED + + // IAccessible + + // Override get_accParent for e10s + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accParent( + /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) override; + + // Override get_accValue to provide URL when no other value is available + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue( + /* [optional][in] */ VARIANT varChild, + /* [retval][out] */ BSTR __RPC_FAR *pszValue) override; + + // Accessible + virtual void Shutdown(); + + // DocAccessible + virtual void* GetNativeWindow() const; + + /** + * Manage the mapping from id to Accessible. + */ + void AddID(uint32_t aID, AccessibleWrap* aAcc) + { mIDToAccessibleMap.Put(aID, aAcc); } + void RemoveID(uint32_t aID) { mIDToAccessibleMap.Remove(aID); } + AccessibleWrap* GetAccessibleByID(uint32_t aID) const + { return mIDToAccessibleMap.Get(aID); } + +protected: + // DocAccessible + virtual void DoInitialUpdate(); + +protected: + void* mHWND; + + /* + * This provides a mapping from 32 bit id to accessible objects. + */ + nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/EnumVariant.cpp b/accessible/windows/msaa/EnumVariant.cpp new file mode 100644 index 000000000..86c81a105 --- /dev/null +++ b/accessible/windows/msaa/EnumVariant.cpp @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "EnumVariant.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// ChildrenEnumVariant +//////////////////////////////////////////////////////////////////////////////// + +IMPL_IUNKNOWN_QUERY_HEAD(ChildrenEnumVariant) +IMPL_IUNKNOWN_QUERY_IFACE(IEnumVARIANT) +IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAnchorAcc) + +STDMETHODIMP +ChildrenEnumVariant::Next(ULONG aCount, VARIANT FAR* aItems, + ULONG FAR* aCountFetched) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aItems || !aCountFetched) + return E_INVALIDARG; + + *aCountFetched = 0; + + if (mAnchorAcc->IsDefunct() || mAnchorAcc->GetChildAt(mCurIndex) != mCurAcc) + return CO_E_OBJNOTCONNECTED; + + ULONG countFetched = 0; + while (mCurAcc && countFetched < aCount) { + VariantInit(aItems + countFetched); + + IDispatch* accNative = AccessibleWrap::NativeAccessible(mCurAcc); + + ++mCurIndex; + mCurAcc = mAnchorAcc->GetChildAt(mCurIndex); + + // Don't output the accessible and count it as having been fetched unless + // it is non-null + MOZ_ASSERT(accNative); + if (!accNative) { + continue; + } + + aItems[countFetched].pdispVal = accNative; + aItems[countFetched].vt = VT_DISPATCH; + ++countFetched; + } + + (*aCountFetched) = countFetched; + + return countFetched < aCount ? S_FALSE : S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ChildrenEnumVariant::Skip(ULONG aCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (mAnchorAcc->IsDefunct() || mAnchorAcc->GetChildAt(mCurIndex) != mCurAcc) + return CO_E_OBJNOTCONNECTED; + + mCurIndex += aCount; + mCurAcc = mAnchorAcc->GetChildAt(mCurIndex); + + return mCurAcc ? S_OK : S_FALSE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ChildrenEnumVariant::Reset() +{ + A11Y_TRYBLOCK_BEGIN + + if (mAnchorAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + mCurIndex = 0; + mCurAcc = mAnchorAcc->GetChildAt(0); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ChildrenEnumVariant::Clone(IEnumVARIANT** aEnumVariant) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aEnumVariant) + return E_INVALIDARG; + + *aEnumVariant = new ChildrenEnumVariant(*this); + (*aEnumVariant)->AddRef(); + + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/msaa/EnumVariant.h b/accessible/windows/msaa/EnumVariant.h new file mode 100644 index 000000000..39e342dd5 --- /dev/null +++ b/accessible/windows/msaa/EnumVariant.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_EnumVariant_h__ +#define mozilla_a11y_EnumVariant_h__ + +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" + +namespace mozilla { +namespace a11y { + +/** + * Used to fetch accessible children. + */ +class ChildrenEnumVariant final : public IEnumVARIANT +{ +public: + ChildrenEnumVariant(AccessibleWrap* aAnchor) : mAnchorAcc(aAnchor), + mCurAcc(mAnchorAcc->GetChildAt(0)), mCurIndex(0) { } + + // IUnknown + DECL_IUNKNOWN + + // IEnumVariant + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG aCount, + /* [length_is][size_is][out] */ VARIANT* aItems, + /* [out] */ ULONG* aCountFetched); + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG aCount); + + virtual HRESULT STDMETHODCALLTYPE Reset(); + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumVARIANT** aEnumVaraint); + +private: + ChildrenEnumVariant() = delete; + ChildrenEnumVariant& operator =(const ChildrenEnumVariant&) = delete; + + ChildrenEnumVariant(const ChildrenEnumVariant& aEnumVariant) : + mAnchorAcc(aEnumVariant.mAnchorAcc), mCurAcc(aEnumVariant.mCurAcc), + mCurIndex(aEnumVariant.mCurIndex) { } + virtual ~ChildrenEnumVariant() { } + +protected: + RefPtr<AccessibleWrap> mAnchorAcc; + Accessible* mCurAcc; + uint32_t mCurIndex; +}; + +} // a11y namespace +} // mozilla namespace + +#endif diff --git a/accessible/windows/msaa/HTMLTableAccessibleWrap.cpp b/accessible/windows/msaa/HTMLTableAccessibleWrap.cpp new file mode 100644 index 000000000..13cea8853 --- /dev/null +++ b/accessible/windows/msaa/HTMLTableAccessibleWrap.cpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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 "HTMLTableAccessibleWrap.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// HTMLTableAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableAccessibleWrap, + HTMLTableAccessible) + +IMPL_IUNKNOWN_INHERITED1(HTMLTableAccessibleWrap, + AccessibleWrap, + ia2AccessibleTable) + +void +HTMLTableAccessibleWrap::Shutdown() +{ + ia2AccessibleTable::mTable = nullptr; + HTMLTableAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLTableCellAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableCellAccessibleWrap, + HTMLTableCellAccessible) + +IMPL_IUNKNOWN_INHERITED1(HTMLTableCellAccessibleWrap, + HyperTextAccessibleWrap, + ia2AccessibleTableCell) + +void +HTMLTableCellAccessibleWrap::Shutdown() +{ + ia2AccessibleTableCell::mTableCell = nullptr; + HTMLTableCellAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLTableCellAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableHeaderCellAccessibleWrap, + HTMLTableHeaderCellAccessible) + +IMPL_IUNKNOWN_INHERITED1(HTMLTableHeaderCellAccessibleWrap, + HyperTextAccessibleWrap, + ia2AccessibleTableCell) + +void +HTMLTableHeaderCellAccessibleWrap::Shutdown() +{ + ia2AccessibleTableCell::mTableCell = nullptr; + HTMLTableHeaderCellAccessible::Shutdown(); +} diff --git a/accessible/windows/msaa/HTMLTableAccessibleWrap.h b/accessible/windows/msaa/HTMLTableAccessibleWrap.h new file mode 100644 index 000000000..71d149a3c --- /dev/null +++ b/accessible/windows/msaa/HTMLTableAccessibleWrap.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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/. */ + +#ifndef mozilla_a11y_HTMLTableAccessibleWrap_h__ +#define mozilla_a11y_HTMLTableAccessibleWrap_h__ + +#include "HTMLTableAccessible.h" + +#include "ia2AccessibleTable.h" +#include "ia2AccessibleTableCell.h" + +namespace mozilla { +namespace a11y { + +/** + * IA2 wrapper class for HTMLTableAccessible implementing IAccessibleTable + * and IAccessibleTable2 interfaces. + */ +class HTMLTableAccessibleWrap : public HTMLTableAccessible, + public ia2AccessibleTable +{ + ~HTMLTableAccessibleWrap() {} + +public: + HTMLTableAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + HTMLTableAccessible(aContent, aDoc), ia2AccessibleTable(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + + +/** + * IA2 wrapper class for HTMLTableCellAccessible implementing + * IAccessibleTableCell interface. + */ +class HTMLTableCellAccessibleWrap : public HTMLTableCellAccessible, + public ia2AccessibleTableCell +{ + ~HTMLTableCellAccessibleWrap() {} + +public: + HTMLTableCellAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + HTMLTableCellAccessible(aContent, aDoc), ia2AccessibleTableCell(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + + +/** + * IA2 wrapper class for HTMLTableHeaderCellAccessible implementing + * IAccessibleTableCell interface. + */ +class HTMLTableHeaderCellAccessibleWrap : public HTMLTableHeaderCellAccessible, + public ia2AccessibleTableCell +{ + ~HTMLTableHeaderCellAccessibleWrap() {} + +public: + HTMLTableHeaderCellAccessibleWrap(nsIContent* aContent, + DocAccessible* aDoc) : + HTMLTableHeaderCellAccessible(aContent, aDoc), ia2AccessibleTableCell(this) + {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp new file mode 100644 index 000000000..3644db68d --- /dev/null +++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.cpp @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "HTMLWin32ObjectAccessible.h" + +#include "Role.h" +#include "States.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// HTMLWin32ObjectOwnerAccessible +//////////////////////////////////////////////////////////////////////////////// + +HTMLWin32ObjectOwnerAccessible:: + HTMLWin32ObjectOwnerAccessible(nsIContent* aContent, + DocAccessible* aDoc, void* aHwnd) : + AccessibleWrap(aContent, aDoc), mHwnd(aHwnd) +{ + mStateFlags |= eNoKidsFromDOM; + + // Our only child is a HTMLWin32ObjectAccessible object. + if (mHwnd) { + mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd, aDoc); + AppendChild(mNativeAccessible); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLWin32ObjectOwnerAccessible: Accessible implementation + +void +HTMLWin32ObjectOwnerAccessible::Shutdown() +{ + AccessibleWrap::Shutdown(); + mNativeAccessible = nullptr; +} + +role +HTMLWin32ObjectOwnerAccessible::NativeRole() +{ + return roles::EMBEDDED_OBJECT; +} + +bool +HTMLWin32ObjectOwnerAccessible::NativelyUnavailable() const +{ + // XXX: No HWND means this is windowless plugin which is not accessible in + // the meantime. + return !mHwnd; +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLWin32ObjectAccessible +//////////////////////////////////////////////////////////////////////////////// + +HTMLWin32ObjectAccessible::HTMLWin32ObjectAccessible(void* aHwnd, + DocAccessible* aDoc) : + DummyAccessible(aDoc) +{ + mHwnd = aHwnd; + if (mHwnd) { +#if defined(MOZ_CONTENT_SANDBOX) + if (XRE_IsContentProcess()) { + DocAccessibleChild* ipcDoc = aDoc->IPCDoc(); + MOZ_ASSERT(ipcDoc); + if (!ipcDoc) { + return; + } + + IAccessibleHolder proxyHolder; + if (!ipcDoc->SendGetWindowedPluginIAccessible( + reinterpret_cast<uintptr_t>(mHwnd), &proxyHolder)) { + return; + } + + mCOMProxy.reset(proxyHolder.Release()); + return; + } +#endif + + // The plugin is not windowless. In this situation we use + // use its inner child owned by the plugin so that we don't get + // in an infinite loop, where the WM_GETOBJECT's get forwarded + // back to us and create another HTMLWin32ObjectAccessible + mHwnd = ::GetWindow((HWND)aHwnd, GW_CHILD); + } +} + +void +HTMLWin32ObjectAccessible::GetNativeInterface(void** aNativeAccessible) +{ +#if defined(MOZ_CONTENT_SANDBOX) + if (XRE_IsContentProcess()) { + RefPtr<IAccessible> addRefed = mCOMProxy.get(); + addRefed.forget(aNativeAccessible); + return; + } +#endif + + if (mHwnd) { + ::AccessibleObjectFromWindow(static_cast<HWND>(mHwnd), + OBJID_WINDOW, IID_IAccessible, + aNativeAccessible); + } +} + diff --git a/accessible/windows/msaa/HTMLWin32ObjectAccessible.h b/accessible/windows/msaa/HTMLWin32ObjectAccessible.h new file mode 100644 index 000000000..786d52191 --- /dev/null +++ b/accessible/windows/msaa/HTMLWin32ObjectAccessible.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_a11y_HTMLWin32ObjectAccessible_h_ +#define mozilla_a11y_HTMLWin32ObjectAccessible_h_ + +#include "BaseAccessibles.h" + +#if defined(MOZ_CONTENT_SANDBOX) +#include "mozilla/mscom/Ptr.h" +#endif + +struct IAccessible; + +namespace mozilla { +namespace a11y { + +class HTMLWin32ObjectOwnerAccessible : public AccessibleWrap +{ +public: + // This will own the HTMLWin32ObjectAccessible. We create this where the + // <object> or <embed> exists in the tree, so that get_accNextSibling() etc. + // will still point to Gecko accessible sibling content. This is necessary + // because the native plugin accessible doesn't know where it exists in the + // Mozilla tree, and returns null for previous and next sibling. This would + // have the effect of cutting off all content after the plugin. + HTMLWin32ObjectOwnerAccessible(nsIContent* aContent, + DocAccessible* aDoc, void* aHwnd); + virtual ~HTMLWin32ObjectOwnerAccessible() {} + + // Accessible + virtual void Shutdown(); + virtual mozilla::a11y::role NativeRole(); + virtual bool NativelyUnavailable() const; + +protected: + void* mHwnd; + RefPtr<Accessible> mNativeAccessible; +}; + +/** + * This class is used only internally, we never! send out an IAccessible linked + * back to this object. This class is used to represent a plugin object when + * referenced as a child or sibling of another Accessible node. We need only + * a limited portion of the Accessible interface implemented here. The + * in depth accessible information will be returned by the actual IAccessible + * object returned by us in Accessible::NewAccessible() that gets the IAccessible + * from the windows system from the window handle. + */ +class HTMLWin32ObjectAccessible : public DummyAccessible +{ +public: + HTMLWin32ObjectAccessible(void* aHwnd, DocAccessible* aDoc); + virtual ~HTMLWin32ObjectAccessible() {} + + virtual void GetNativeInterface(void** aNativeAccessible) override; + +protected: + void* mHwnd; +#if defined(MOZ_CONTENT_SANDBOX) + mscom::ProxyUniquePtr<IAccessible> mCOMProxy; +#endif +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/HyperTextAccessibleWrap.cpp b/accessible/windows/msaa/HyperTextAccessibleWrap.cpp new file mode 100644 index 000000000..b5fd716d9 --- /dev/null +++ b/accessible/windows/msaa/HyperTextAccessibleWrap.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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 "HyperTextAccessibleWrap.h" +#include "Accessible-inl.h" + +#include "nsEventShell.h" + +#include "mozilla/StaticPtr.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAccessibleWrap, + HyperTextAccessible) + +STDMETHODIMP +HyperTextAccessibleWrap::QueryInterface(REFIID aIID, void** aInstancePtr) +{ + if (!aInstancePtr) + return E_FAIL; + + *aInstancePtr = nullptr; + + if (IsTextRole()) { + if (aIID == IID_IAccessibleText) + *aInstancePtr = + static_cast<IAccessibleText*>(static_cast<ia2AccessibleText*>(this)); + else if (aIID == IID_IAccessibleHypertext) + *aInstancePtr = static_cast<IAccessibleHypertext*>(this); + else if (aIID == IID_IAccessibleEditableText) + *aInstancePtr = static_cast<IAccessibleEditableText*>(this); + + if (*aInstancePtr) { + AddRef(); + return S_OK; + } + } + + return AccessibleWrap::QueryInterface(aIID, aInstancePtr); +} + +nsresult +HyperTextAccessibleWrap::HandleAccEvent(AccEvent* aEvent) +{ + uint32_t eventType = aEvent->GetEventType(); + + if (eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || + eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) { + Accessible* accessible = aEvent->GetAccessible(); + if (accessible && accessible->IsHyperText()) { + AccTextChangeEvent* event = downcast_accEvent(aEvent); + HyperTextAccessibleWrap* text = + static_cast<HyperTextAccessibleWrap*>(accessible->AsHyperText()); + ia2AccessibleText::UpdateTextChangeData(text, event->IsTextInserted(), + event->ModifiedText(), + event->GetStartOffset(), + event->GetLength()); + } + } + + return HyperTextAccessible::HandleAccEvent(aEvent); +} diff --git a/accessible/windows/msaa/HyperTextAccessibleWrap.h b/accessible/windows/msaa/HyperTextAccessibleWrap.h new file mode 100644 index 000000000..c0f853da2 --- /dev/null +++ b/accessible/windows/msaa/HyperTextAccessibleWrap.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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/. */ + +#ifndef mozilla_a11y_HyperTextAccessibleWrap_h__ +#define mozilla_a11y_HyperTextAccessibleWrap_h__ + +#include "HyperTextAccessible.h" +#include "ia2AccessibleEditableText.h" +#include "ia2AccessibleHypertext.h" +#include "IUnknownImpl.h" + +namespace mozilla { +template<class T> class StaticAutoPtr; +template<class T> class StaticRefPtr; + +namespace a11y { + +class HyperTextAccessibleWrap : public HyperTextAccessible, + public ia2AccessibleHypertext, + public ia2AccessibleEditableText +{ +public: + HyperTextAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + HyperTextAccessible(aContent, aDoc) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + // Accessible + virtual nsresult HandleAccEvent(AccEvent* aEvent); + +protected: + ~HyperTextAccessibleWrap() {} +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/IDSet.h b/accessible/windows/msaa/IDSet.h new file mode 100644 index 000000000..3c3ed74c3 --- /dev/null +++ b/accessible/windows/msaa/IDSet.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +/** + * A class to generate unique IDs in the range [ - 2^31, 0 ) + */ + +#ifndef MOZILLA_A11Y_IDSet_h_ +#define MOZILLA_A11Y_IDSet_h_ + +#include "mozilla/Attributes.h" +#include "mozilla/MathAlgorithms.h" +#include "mozilla/SplayTree.h" + +namespace mozilla { +namespace a11y { + +/** + * On windows an accessible's id must be a negative 32 bit integer. It is + * important to support recycling arbitrary IDs because accessibles can be + * created and destroyed at any time in the life of a page. IDSet provides 2 + * operations: generate an ID in the range (0, mMaxId], and release an ID so + * it can be allocated again. Allocated ID are tracked by a sparse bitmap + * implemented with a splay tree. Nodes in the tree are keyed by the upper N + * bits of the ID, and the node contains a bitmap tracking the allocation of + * 2^(ceil(log2(mMaxId)) - N) IDs. + * + * Note that negation is handled by MsaaIdGenerator as it performs additional + * decoration on the ID generated by IDSet. + * @see mozilla::a11y::MsaaIdGenerator + */ +class IDSet +{ +public: + constexpr explicit IDSet(const uint32_t aMaxIdBits) + : mBitSet() + , mIdx(0) + , mMaxId((1UL << aMaxIdBits) - 1UL) + , mMaxIdx(mMaxId / bitsPerElt) + { + } + + /** + * Return a new unique id. + */ + uint32_t GetID() + { + uint32_t idx = mIdx; + while (true) { + BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx)); + if (elt->mBitvec[0] != UINT64_MAX) { + uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]); + + elt->mBitvec[0] |= (1ull << i); + mIdx = idx; + return (elt->mIdx * bitsPerElt + i); + } + + if (elt->mBitvec[1] != UINT64_MAX) { + uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]); + + elt->mBitvec[1] |= (1ull << i); + mIdx = idx; + return (elt->mIdx * bitsPerElt + bitsPerWord + i); + } + + idx++; + if (idx > mMaxIdx) { + idx = 0; + } + + if (idx == mIdx) { + MOZ_CRASH("used up all the available ids"); + } + } + } + + /** + * Free a no longer required id so it may be allocated again. + */ + void ReleaseID(uint32_t aID) + { + MOZ_ASSERT(aID < mMaxId); + + uint32_t idx = aID / bitsPerElt; + mIdx = idx; + BitSetElt* elt = mBitSet.find(BitSetElt(idx)); + MOZ_ASSERT(elt); + + uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord; + elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord)); + if (elt->mBitvec[0] == 0 && elt->mBitvec[1] == 0) { + delete mBitSet.remove(*elt); + } + } + +private: + static const unsigned int wordsPerElt = 2; + static const unsigned int bitsPerWord = 64; + static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord; + + struct BitSetElt : mozilla::SplayTreeNode<BitSetElt> + { + explicit BitSetElt(uint32_t aIdx) : + mIdx(aIdx) + { mBitvec[0] = mBitvec[1] = 0; } + + uint64_t mBitvec[wordsPerElt]; + uint32_t mIdx; + + static int compare(const BitSetElt& a, const BitSetElt& b) + { + if (a.mIdx == b.mIdx) { + return 0; + } + + if (a.mIdx < b.mIdx) { + return -1; + } + return 1; + } + }; + + SplayTree<BitSetElt, BitSetElt> mBitSet; + uint32_t mIdx; + const uint32_t mMaxId; + const uint32_t mMaxIdx; +}; + +} +} + +#endif diff --git a/accessible/windows/msaa/IUnknownImpl.cpp b/accessible/windows/msaa/IUnknownImpl.cpp new file mode 100644 index 000000000..4a9fa5383 --- /dev/null +++ b/accessible/windows/msaa/IUnknownImpl.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "IUnknownImpl.h" + +#include "nsDebug.h" + +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif + +namespace mozilla { +namespace a11y { + +HRESULT +GetHRESULT(nsresult aResult) +{ + switch (aResult) { + case NS_OK: + return S_OK; + + case NS_ERROR_INVALID_ARG: + return E_INVALIDARG; + + case NS_ERROR_OUT_OF_MEMORY: + return E_OUTOFMEMORY; + + case NS_ERROR_NOT_IMPLEMENTED: + return E_NOTIMPL; + + default: + return E_FAIL; + } +} + +int +FilterExceptions(unsigned int aCode, EXCEPTION_POINTERS* aExceptionInfo) +{ + if (aCode == EXCEPTION_ACCESS_VIOLATION) { +#ifdef MOZ_CRASHREPORTER + // MSAA swallows crashes (because it is COM-based) but we still need to + // learn about those crashes so we can fix them. Make sure to pass them to + // the crash reporter. + CrashReporter::WriteMinidumpForException(aExceptionInfo); +#endif + } else { + NS_NOTREACHED("We should only be catching crash exceptions"); + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/windows/msaa/IUnknownImpl.h b/accessible/windows/msaa/IUnknownImpl.h new file mode 100644 index 000000000..dbf6c1374 --- /dev/null +++ b/accessible/windows/msaa/IUnknownImpl.h @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. * + */ + +#ifndef mozilla_a11y_IUnknownImpl_h_ +#define mozilla_a11y_IUnknownImpl_h_ + +#include <windows.h> +#undef CreateEvent // thank you windows you're such a helper +#include "nsError.h" + +// Avoid warning C4509 like "nonstandard extension used: +// 'AccessibleWrap::[acc_getName]' uses SEH and 'name' has destructor. +// At this point we're catching a crash which is of much greater +// importance than the missing dereference for the nsCOMPtr<> +#ifdef _MSC_VER +#pragma warning( disable : 4509 ) +#endif + +#ifdef __GNUC__ +#define ATTRIBUTE_UNUSED __attribute__((unused)) +#else +#define ATTRIBUTE_UNUSED +#endif + +namespace mozilla { +namespace a11y { + +class AutoRefCnt +{ +public: + AutoRefCnt() : mValue(0) {} + + ULONG operator++() { return ++mValue; } + ULONG operator--() { return --mValue; } + ULONG operator++(int) { return ++mValue; } + ULONG operator--(int) { return --mValue; } + + operator ULONG() const { return mValue; } + +private: + ULONG mValue; +}; + +} // namespace a11y +} // namespace mozilla + +#define DECL_IUNKNOWN \ +public: \ + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**); \ + virtual ULONG STDMETHODCALLTYPE AddRef() final \ + { \ + MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \ + ++mRefCnt; \ + return mRefCnt; \ + } \ + virtual ULONG STDMETHODCALLTYPE Release() final \ + { \ + MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \ + --mRefCnt; \ + if (mRefCnt) \ + return mRefCnt; \ + \ + delete this; \ + return 0; \ + } \ +private: \ + mozilla::a11y::AutoRefCnt mRefCnt; \ +public: + +#define DECL_IUNKNOWN_INHERITED \ +public: \ +virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**); \ + +#define IMPL_IUNKNOWN_QUERY_HEAD(Class) \ +STDMETHODIMP \ +Class::QueryInterface(REFIID aIID, void** aInstancePtr) \ +{ \ + A11Y_TRYBLOCK_BEGIN \ + if (!aInstancePtr) \ + return E_INVALIDARG; \ + *aInstancePtr = nullptr; \ + \ + HRESULT hr ATTRIBUTE_UNUSED = E_NOINTERFACE; + +#define IMPL_IUNKNOWN_QUERY_TAIL \ + return hr; \ + A11Y_TRYBLOCK_END \ +} + +#define IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(Member) \ + return Member->QueryInterface(aIID, aInstancePtr); \ + A11Y_TRYBLOCK_END \ +} + +#define IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(BaseClass) \ + return BaseClass::QueryInterface(aIID, aInstancePtr); \ + A11Y_TRYBLOCK_END \ +} + +#define IMPL_IUNKNOWN_QUERY_IFACE(Iface) \ + if (aIID == IID_##Iface) { \ + *aInstancePtr = static_cast<Iface*>(this); \ + AddRef(); \ + return S_OK; \ + } + +#define IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(Iface, aResolveIface) \ + if (aIID == IID_##Iface) { \ + *aInstancePtr = static_cast<Iface*>(static_cast<aResolveIface*>(this)); \ + AddRef(); \ + return S_OK; \ + } + +#define IMPL_IUNKNOWN_QUERY_CLASS(Class) \ + hr = Class::QueryInterface(aIID, aInstancePtr); \ + if (SUCCEEDED(hr)) \ + return hr; + +#define IMPL_IUNKNOWN_QUERY_CLASS_COND(Class, Cond) \ + if (Cond) { \ + hr = Class::QueryInterface(aIID, aInstancePtr); \ + if (SUCCEEDED(hr)) \ + return hr; \ + } + +#define IMPL_IUNKNOWN_QUERY_AGGR_COND(Member, Cond) \ + if (Cond) { \ + hr = Member->QueryInterface(aIID, aInstancePtr); \ + if (SUCCEEDED(hr)) \ + return hr; \ + } + +#define IMPL_IUNKNOWN1(Class, I1) \ + IMPL_IUNKNOWN_QUERY_HEAD(Class) \ + IMPL_IUNKNOWN_QUERY_IFACE(I1); \ + IMPL_IUNKNOWN_QUERY_IFACE(IUnknown); \ + IMPL_IUNKNOWN_QUERY_TAIL \ + +#define IMPL_IUNKNOWN2(Class, I1, I2) \ + IMPL_IUNKNOWN_QUERY_HEAD(Class) \ + IMPL_IUNKNOWN_QUERY_IFACE(I1); \ + IMPL_IUNKNOWN_QUERY_IFACE(I2); \ + IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(IUnknown, I1); \ + IMPL_IUNKNOWN_QUERY_TAIL \ + +#define IMPL_IUNKNOWN_INHERITED1(Class, Super0, Super1) \ + IMPL_IUNKNOWN_QUERY_HEAD(Class) \ + IMPL_IUNKNOWN_QUERY_CLASS(Super1); \ + IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(Super0) + +#define IMPL_IUNKNOWN_INHERITED2(Class, Super0, Super1, Super2) \ + IMPL_IUNKNOWN_QUERY_HEAD(Class) \ + IMPL_IUNKNOWN_QUERY_CLASS(Super1); \ + IMPL_IUNKNOWN_QUERY_CLASS(Super2); \ + IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(Super0) + + +/** + * Wrap every method body by these macroses to pass exception to the crash + * reporter. + */ +#define A11Y_TRYBLOCK_BEGIN \ + MOZ_SEH_TRY { + +#define A11Y_TRYBLOCK_END \ + } MOZ_SEH_EXCEPT(mozilla::a11y::FilterExceptions(::GetExceptionCode(), \ + GetExceptionInformation())) \ + { } \ + return E_FAIL; + + +namespace mozilla { +namespace a11y { + +/** + * Converts nsresult to HRESULT. + */ +HRESULT GetHRESULT(nsresult aResult); + +/** + * Used to pass an exception to the crash reporter. + */ +int FilterExceptions(unsigned int aCode, EXCEPTION_POINTERS* aExceptionInfo); + +} // namespace a11y; +} //namespace mozilla; + +#endif diff --git a/accessible/windows/msaa/ImageAccessibleWrap.cpp b/accessible/windows/msaa/ImageAccessibleWrap.cpp new file mode 100644 index 000000000..7ff2e8a47 --- /dev/null +++ b/accessible/windows/msaa/ImageAccessibleWrap.cpp @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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 "ImageAccessibleWrap.h" +#include "nsIURI.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +NS_IMPL_ISUPPORTS_INHERITED0(ImageAccessibleWrap, + ImageAccessible) + +IMPL_IUNKNOWN_INHERITED1(ImageAccessibleWrap, + AccessibleWrap, + ia2AccessibleImage) + diff --git a/accessible/windows/msaa/ImageAccessibleWrap.h b/accessible/windows/msaa/ImageAccessibleWrap.h new file mode 100644 index 000000000..e6a916ebe --- /dev/null +++ b/accessible/windows/msaa/ImageAccessibleWrap.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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/. */ + +#ifndef mozilla_a11y_ImageAccessibleWrap_h__ +#define mozilla_a11y_ImageAccessibleWrap_h__ + +#include "ImageAccessible.h" +#include "ia2AccessibleImage.h" + +namespace mozilla { +namespace a11y { + +class ImageAccessibleWrap : public ImageAccessible, + public ia2AccessibleImage +{ +public: + ImageAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + ImageAccessible(aContent, aDoc) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + +protected: + ~ImageAccessibleWrap() {} +}; + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/windows/msaa/MsaaIdGenerator.cpp b/accessible/windows/msaa/MsaaIdGenerator.cpp new file mode 100644 index 000000000..5f4b333fa --- /dev/null +++ b/accessible/windows/msaa/MsaaIdGenerator.cpp @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "MsaaIdGenerator.h" + +#include "mozilla/a11y/AccessibleWrap.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Unused.h" +#include "nsDataHashtable.h" +#include "nsIXULRuntime.h" + +// These constants may be adjusted to modify the proportion of the Child ID +// allocated to the content ID vs proportion allocated to the unique ID. They +// must always sum to 31, ie. the width of a 32-bit integer less the sign bit. + +// NB: kNumContentProcessIDBits must be large enough to successfully hold the +// maximum permitted number of e10s content processes. If the e10s maximum +// number of content processes changes, then kNumContentProcessIDBits must also +// be updated if necessary to accommodate that new value! +static const uint32_t kNumContentProcessIDBits = 7UL; +static const uint32_t kNumUniqueIDBits = (31UL - kNumContentProcessIDBits); + +static_assert(kNumContentProcessIDBits + kNumUniqueIDBits == 31, + "Allocation of Content ID bits and Unique ID bits must sum to 31"); + +namespace mozilla { +namespace a11y { +namespace detail { + +typedef nsDataHashtable<nsUint64HashKey, uint32_t> ContentParentIdMap; + +#pragma pack(push, 1) +union MsaaID +{ + int32_t mInt32; + uint32_t mUInt32; + struct + { + uint32_t mUniqueID:kNumUniqueIDBits; + uint32_t mContentProcessID:kNumContentProcessIDBits; + uint32_t mSignBit:1; + } + mCracked; +}; +#pragma pack(pop) + +static uint32_t +BuildMsaaID(const uint32_t aID, const uint32_t aContentProcessID) +{ + MsaaID id; + id.mCracked.mSignBit = 0; + id.mCracked.mUniqueID = aID; + id.mCracked.mContentProcessID = aContentProcessID; + return ~id.mUInt32; +} + +class MsaaIDCracker +{ +public: + explicit MsaaIDCracker(const uint32_t aMsaaID) + { + mID.mUInt32 = ~aMsaaID; + } + + uint32_t GetContentProcessId() + { + return mID.mCracked.mContentProcessID; + } + + uint32_t GetUniqueId() + { + return mID.mCracked.mUniqueID; + } + +private: + MsaaID mID; +}; + +} // namespace detail + +constexpr MsaaIdGenerator::MsaaIdGenerator() + : mIDSet(kNumUniqueIDBits) +{} + +uint32_t +MsaaIdGenerator::GetID() +{ + uint32_t id = mIDSet.GetID(); + MOZ_ASSERT(id <= ((1UL << kNumUniqueIDBits) - 1UL)); + return detail::BuildMsaaID(id, ResolveContentProcessID()); +} + +void +MsaaIdGenerator::ReleaseID(AccessibleWrap* aAccWrap) +{ + MOZ_ASSERT(aAccWrap); + uint32_t id = aAccWrap->GetExistingID(); + MOZ_ASSERT(id != AccessibleWrap::kNoID); + detail::MsaaIDCracker cracked(id); + if (cracked.GetContentProcessId() != ResolveContentProcessID()) { + // This may happen if chrome holds a proxy whose ID was originally generated + // by a content process. Since ReleaseID only has meaning in the process + // that originally generated that ID, we ignore ReleaseID calls for any ID + // that did not come from the current process. + MOZ_ASSERT(aAccWrap->IsProxy()); + return; + } + mIDSet.ReleaseID(cracked.GetUniqueId()); +} + +bool +MsaaIdGenerator::IsChromeID(uint32_t aID) +{ + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == 0; +} + +bool +MsaaIdGenerator::IsIDForThisContentProcess(uint32_t aID) +{ + MOZ_ASSERT(XRE_IsContentProcess()); + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == ResolveContentProcessID(); +} + +bool +MsaaIdGenerator::IsIDForContentProcess(uint32_t aID, + dom::ContentParentId aIPCContentProcessId) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + detail::MsaaIDCracker cracked(aID); + return cracked.GetContentProcessId() == + GetContentProcessIDFor(aIPCContentProcessId); +} + +bool +MsaaIdGenerator::IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID) +{ + detail::MsaaIDCracker firstCracked(aFirstID); + detail::MsaaIDCracker secondCracked(aSecondID); + return firstCracked.GetContentProcessId() == + secondCracked.GetContentProcessId(); +} + +uint32_t +MsaaIdGenerator::ResolveContentProcessID() +{ + if (XRE_IsParentProcess()) { + return 0; + } + + dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); + uint32_t result = contentChild->GetMsaaID(); + + MOZ_ASSERT(result); + return result; +} + +/** + * Each dom::ContentParent has a 64-bit ID. This ID is monotonically increasing + * with each new content process, so those IDs are effectively single-use. OTOH, + * MSAA requires 32-bit IDs. Since we only allocate kNumContentProcessIDBits for + * the content process ID component, the MSAA content process ID value must be + * reusable. sContentParentIdMap holds the current associations between + * dom::ContentParent IDs and the MSAA content parent IDs that have been + * allocated to them. + */ +static StaticAutoPtr<detail::ContentParentIdMap> sContentParentIdMap; + +static const uint32_t kBitsPerByte = 8UL; +// Set sContentProcessIdBitmap[0] to 1 to reserve the Chrome process's id +static uint64_t sContentProcessIdBitmap[(1UL << kNumContentProcessIDBits) / + (sizeof(uint64_t) * kBitsPerByte)] = {1ULL}; +static const uint32_t kBitsPerElement = sizeof(sContentProcessIdBitmap[0]) * + kBitsPerByte; + +uint32_t +MsaaIdGenerator::GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID) +{ + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); + if (!sContentParentIdMap) { + sContentParentIdMap = new detail::ContentParentIdMap(); + ClearOnShutdown(&sContentParentIdMap); + } + + uint32_t value = 0; + if (sContentParentIdMap->Get(aIPCContentProcessID, &value)) { + return value; + } + + uint32_t index = 0; + for (; index < ArrayLength(sContentProcessIdBitmap); ++index) { + if (sContentProcessIdBitmap[index] == UINT64_MAX) { + continue; + } + uint32_t bitIndex = CountTrailingZeroes64(~sContentProcessIdBitmap[index]); + MOZ_ASSERT(!(sContentProcessIdBitmap[index] & (1ULL << bitIndex))); + MOZ_ASSERT(bitIndex != 0 || index != 0); + sContentProcessIdBitmap[index] |= (1ULL << bitIndex); + value = index * kBitsPerElement + bitIndex; + break; + } + + // If we run out of content process IDs, we're in trouble + MOZ_RELEASE_ASSERT(index < ArrayLength(sContentProcessIdBitmap)); + + sContentParentIdMap->Put(aIPCContentProcessID, value); + return value; +} + +void +MsaaIdGenerator::ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID) +{ + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); + if (!sContentParentIdMap) { + // Since Content IDs are generated lazily, ContentParent might attempt + // to release an ID that was never allocated to begin with. + return; + } + + Maybe<uint32_t> mapping = sContentParentIdMap->GetAndRemove(aIPCContentProcessID); + if (!mapping) { + // Since Content IDs are generated lazily, ContentParent might attempt + // to release an ID that was never allocated to begin with. + return; + } + + uint32_t index = mapping.ref() / kBitsPerElement; + MOZ_ASSERT(index < ArrayLength(sContentProcessIdBitmap)); + + uint64_t mask = 1ULL << (mapping.ref() % kBitsPerElement); + MOZ_ASSERT(sContentProcessIdBitmap[index] & mask); + + sContentProcessIdBitmap[index] &= ~mask; +} + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/windows/msaa/MsaaIdGenerator.h b/accessible/windows/msaa/MsaaIdGenerator.h new file mode 100644 index 000000000..b845e8473 --- /dev/null +++ b/accessible/windows/msaa/MsaaIdGenerator.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_MsaaIdGenerator_h +#define mozilla_a11y_MsaaIdGenerator_h + +#include "mozilla/a11y/IDSet.h" + +#include "mozilla/dom/ipc/IdType.h" + +namespace mozilla { +namespace a11y { + +class AccessibleWrap; + +/** + * This class is responsible for generating child IDs used by our MSAA + * implementation. Since e10s requires us to differentiate IDs based on the + * originating process of the accessible, a portion of the ID's bits are + * allocated to storing that information. The remaining bits represent the + * unique ID of the accessible, within that content process. + * + * The constants kNumContentProcessIDBits and kNumUniqueIDBits in the + * implementation are responsible for determining the proportion of bits that + * are allocated for each purpose. + */ +class MsaaIdGenerator +{ +public: + constexpr MsaaIdGenerator(); + + uint32_t GetID(); + void ReleaseID(AccessibleWrap* aAccWrap); + bool IsChromeID(uint32_t aID); + bool IsIDForThisContentProcess(uint32_t aID); + bool IsIDForContentProcess(uint32_t aID, + dom::ContentParentId aIPCContentProcessId); + bool IsSameContentProcessFor(uint32_t aFirstID, uint32_t aSecondID); + + uint32_t GetContentProcessIDFor(dom::ContentParentId aIPCContentProcessID); + void ReleaseContentProcessIDFor(dom::ContentParentId aIPCContentProcessID); + +private: + uint32_t ResolveContentProcessID(); + +private: + IDSet mIDSet; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_MsaaIdGenerator_h diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp new file mode 100644 index 000000000..dc6acd3ad --- /dev/null +++ b/accessible/windows/msaa/Platform.cpp @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "Platform.h" + +#include "AccEvent.h" +#include "Compatibility.h" +#include "HyperTextAccessibleWrap.h" +#include "ia2AccessibleText.h" +#include "nsIXULRuntime.h" +#include "nsWinUtils.h" +#include "mozilla/a11y/ProxyAccessible.h" +#include "mozilla/mscom/InterceptorLog.h" +#include "mozilla/mscom/Registration.h" +#include "mozilla/StaticPtr.h" +#include "ProxyWrappers.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::mscom; + +static StaticAutoPtr<RegisteredProxy> gRegProxy; +static StaticAutoPtr<RegisteredProxy> gRegAccTlb; +static StaticAutoPtr<RegisteredProxy> gRegMiscTlb; + +void +a11y::PlatformInit() +{ + Compatibility::Init(); + + nsWinUtils::MaybeStartWindowEmulation(); + ia2AccessibleText::InitTextChangeData(); + if (BrowserTabsRemoteAutostart()) { + mscom::InterceptorLog::Init(); + UniquePtr<RegisteredProxy> regProxy( + mscom::RegisterProxy(L"ia2marshal.dll")); + gRegProxy = regProxy.release(); + UniquePtr<RegisteredProxy> regAccTlb( + mscom::RegisterTypelib(L"oleacc.dll", + RegistrationFlags::eUseSystemDirectory)); + gRegAccTlb = regAccTlb.release(); + UniquePtr<RegisteredProxy> regMiscTlb( + mscom::RegisterTypelib(L"Accessible.tlb")); + gRegMiscTlb = regMiscTlb.release(); + } +} + +void +a11y::PlatformShutdown() +{ + ::DestroyCaret(); + + nsWinUtils::ShutdownWindowEmulation(); + gRegProxy = nullptr; + gRegAccTlb = nullptr; + gRegMiscTlb = nullptr; +} + +void +a11y::ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces) +{ + AccessibleWrap* wrapper = nullptr; + if (aInterfaces & Interfaces::DOCUMENT) { + wrapper = new DocProxyAccessibleWrap(aProxy); + } else if (aInterfaces & Interfaces::HYPERTEXT) { + wrapper = new HyperTextProxyAccessibleWrap(aProxy); + } else { + wrapper = new ProxyAccessibleWrap(aProxy); + } + + wrapper->SetProxyInterfaces(aInterfaces); + wrapper->AddRef(); + aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper)); +} + +void +a11y::ProxyDestroyed(ProxyAccessible* aProxy) +{ + AccessibleWrap* wrapper = + reinterpret_cast<AccessibleWrap*>(aProxy->GetWrapper()); + MOZ_ASSERT(wrapper); + if (!wrapper) + return; + + wrapper->Shutdown(); + aProxy->SetWrapper(0); + wrapper->Release(); +} + +void +a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType) +{ + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), aEventType); +} + +void +a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t, bool) +{ + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), + nsIAccessibleEvent::EVENT_STATE_CHANGE); +} + +void +a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset) +{ + AccessibleWrap::FireWinEvent(WrapperFor(aTarget), + nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED); +} + +void +a11y::ProxyTextChangeEvent(ProxyAccessible* aText, const nsString& aStr, + int32_t aStart, uint32_t aLen, bool aInsert, bool) +{ + AccessibleWrap* wrapper = WrapperFor(aText); + MOZ_ASSERT(wrapper); + if (!wrapper) { + return; + } + + auto text = static_cast<HyperTextAccessibleWrap*>(wrapper->AsHyperText()); + if (text) { + ia2AccessibleText::UpdateTextChangeData(text, aInsert, aStr, aStart, aLen); + } + + uint32_t eventType = aInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED : + nsIAccessibleEvent::EVENT_TEXT_REMOVED; + AccessibleWrap::FireWinEvent(wrapper, eventType); +} + +void +a11y::ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible*, bool aInsert, bool) +{ + uint32_t event = aInsert ? nsIAccessibleEvent::EVENT_SHOW : + nsIAccessibleEvent::EVENT_HIDE; + AccessibleWrap* wrapper = WrapperFor(aTarget); + AccessibleWrap::FireWinEvent(wrapper, event); +} + +void +a11y::ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible*, uint32_t aType) +{ + AccessibleWrap* wrapper = WrapperFor(aTarget); + AccessibleWrap::FireWinEvent(wrapper, aType); +} diff --git a/accessible/windows/msaa/RootAccessibleWrap.cpp b/accessible/windows/msaa/RootAccessibleWrap.cpp new file mode 100644 index 000000000..30fe40c43 --- /dev/null +++ b/accessible/windows/msaa/RootAccessibleWrap.cpp @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "RootAccessibleWrap.h" + +#include "Compatibility.h" +#include "nsCoreUtils.h" +#include "nsWinUtils.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// Constructor/destructor + +RootAccessibleWrap:: + RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) : + RootAccessible(aDocument, aPresShell) +{ +} + +RootAccessibleWrap::~RootAccessibleWrap() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// RootAccessible + +void +RootAccessibleWrap::DocumentActivated(DocAccessible* aDocument) +{ + if (Compatibility::IsDolphin() && + nsCoreUtils::IsTabDocument(aDocument->DocumentNode())) { + uint32_t count = mChildDocuments.Length(); + for (uint32_t idx = 0; idx < count; idx++) { + DocAccessible* childDoc = mChildDocuments[idx]; + HWND childDocHWND = static_cast<HWND>(childDoc->GetNativeWindow()); + if (childDoc != aDocument) + nsWinUtils::HideNativeWindow(childDocHWND); + else + nsWinUtils::ShowNativeWindow(childDocHWND); + } + } +} diff --git a/accessible/windows/msaa/RootAccessibleWrap.h b/accessible/windows/msaa/RootAccessibleWrap.h new file mode 100644 index 000000000..6aa6fefe3 --- /dev/null +++ b/accessible/windows/msaa/RootAccessibleWrap.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_a11y_RootAccessibleWrap_h__ +#define mozilla_a11y_RootAccessibleWrap_h__ + +#include "RootAccessible.h" + +namespace mozilla { +namespace a11y { + +class RootAccessibleWrap : public RootAccessible +{ +public: + RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell); + virtual ~RootAccessibleWrap(); + + // RootAccessible + virtual void DocumentActivated(DocAccessible* aDocument); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/ServiceProvider.cpp b/accessible/windows/msaa/ServiceProvider.cpp new file mode 100644 index 000000000..82265d3c2 --- /dev/null +++ b/accessible/windows/msaa/ServiceProvider.cpp @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "ServiceProvider.h" + +#include "ApplicationAccessibleWrap.h" +#include "Compatibility.h" +#include "DocAccessible.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "Relation.h" +#include "uiaRawElmProvider.h" + +#include "mozilla/Preferences.h" +#include "nsIDocShell.h" + +#include "ISimpleDOMNode_i.c" + +namespace mozilla { +namespace a11y { + +IMPL_IUNKNOWN_QUERY_HEAD(ServiceProvider) + IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider) +IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible) + + +//////////////////////////////////////////////////////////////////////////////// +// IServiceProvider + +STDMETHODIMP +ServiceProvider::QueryService(REFGUID aGuidService, REFIID aIID, + void** aInstancePtr) +{ + if (!aInstancePtr) + return E_INVALIDARG; + + *aInstancePtr = nullptr; + + // UIA IAccessibleEx + if (aGuidService == IID_IAccessibleEx && + Preferences::GetBool("accessibility.uia.enable")) { + uiaRawElmProvider* accEx = new uiaRawElmProvider(mAccessible); + HRESULT hr = accEx->QueryInterface(aIID, aInstancePtr); + if (FAILED(hr)) + delete accEx; + + return hr; + } + + // Provide a special service ID for getting the accessible for the browser tab + // document that contains this accessible object. If this accessible object + // is not inside a browser tab then the service fails with E_NOINTERFACE. + // A use case for this is for screen readers that need to switch context or + // 'virtual buffer' when focus moves from one browser tab area to another. + static const GUID SID_IAccessibleContentDocument = + { 0xa5d8e1f3,0x3571,0x4d8f,{0x95,0x21,0x07,0xed,0x28,0xfb,0x07,0x2e} }; + if (aGuidService == SID_IAccessibleContentDocument) { + if (aIID != IID_IAccessible) + return E_NOINTERFACE; + + Relation rel = mAccessible->RelationByType(RelationType::CONTAINING_TAB_PANE); + AccessibleWrap* tabDoc = static_cast<AccessibleWrap*>(rel.Next()); + if (!tabDoc) + return E_NOINTERFACE; + + *aInstancePtr = static_cast<IAccessible*>(tabDoc); + (reinterpret_cast<IUnknown*>(*aInstancePtr))->AddRef(); + return S_OK; + } + + // Can get to IAccessibleApplication from any node via QS + if (aGuidService == IID_IAccessibleApplication || + (Compatibility::IsJAWS() && aIID == IID_IAccessibleApplication)) { + ApplicationAccessibleWrap* applicationAcc = + static_cast<ApplicationAccessibleWrap*>(ApplicationAcc()); + if (!applicationAcc) + return E_NOINTERFACE; + + return applicationAcc->QueryInterface(aIID, aInstancePtr); + } + + static const GUID IID_SimpleDOMDeprecated = + { 0x0c539790,0x12e4,0x11cf,{0xb6,0x61,0x00,0xaa,0x00,0x4c,0xd6,0xd8} }; + if (aGuidService == IID_ISimpleDOMNode || + aGuidService == IID_SimpleDOMDeprecated || + aGuidService == IID_IAccessible || aGuidService == IID_IAccessible2) + return mAccessible->QueryInterface(aIID, aInstancePtr); + + return E_INVALIDARG; +} + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/windows/msaa/ServiceProvider.h b/accessible/windows/msaa/ServiceProvider.h new file mode 100644 index 000000000..b0fc812c5 --- /dev/null +++ b/accessible/windows/msaa/ServiceProvider.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_ServiceProvider_h_ +#define mozilla_a11y_ServiceProvider_h_ + +#include <servprov.h> + +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" + +namespace mozilla { +namespace a11y { + +class ServiceProvider final : public IServiceProvider +{ +public: + ServiceProvider(AccessibleWrap* aAcc) : mAccessible(aAcc) {} + ~ServiceProvider() {} + + DECL_IUNKNOWN + + // IServiceProvider + virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID aGuidService, + REFIID aIID, + void** aInstancePtr); + +private: + RefPtr<AccessibleWrap> mAccessible; +}; + +} +} + +#endif diff --git a/accessible/windows/msaa/TextLeafAccessibleWrap.cpp b/accessible/windows/msaa/TextLeafAccessibleWrap.cpp new file mode 100644 index 000000000..6f1d193db --- /dev/null +++ b/accessible/windows/msaa/TextLeafAccessibleWrap.cpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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 "TextLeafAccessibleWrap.h" + +#include "sdnTextAccessible.h" +#include "Statistics.h" + +using namespace mozilla::a11y; + +IMPL_IUNKNOWN_QUERY_HEAD(TextLeafAccessibleWrap) + if (aIID == IID_ISimpleDOMText) { + statistics::ISimpleDOMUsed(); + *aInstancePtr = static_cast<ISimpleDOMText*>(new sdnTextAccessible(this)); + static_cast<IUnknown*>(*aInstancePtr)->AddRef(); + return S_OK; + } +IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(AccessibleWrap) diff --git a/accessible/windows/msaa/TextLeafAccessibleWrap.h b/accessible/windows/msaa/TextLeafAccessibleWrap.h new file mode 100644 index 000000000..612bed173 --- /dev/null +++ b/accessible/windows/msaa/TextLeafAccessibleWrap.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_a11y_TextLeafAccessibleWrap_h__ +#define mozilla_a11y_TextLeafAccessibleWrap_h__ + +#include "TextLeafAccessible.h" + +namespace mozilla { +namespace a11y { + +/** + * Wrap TextLeafAccessible to expose ISimpleDOMText as a native interface with + * a tear off. + */ +class TextLeafAccessibleWrap : public TextLeafAccessible +{ +public: + TextLeafAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + TextLeafAccessible(aContent, aDoc) { } + virtual ~TextLeafAccessibleWrap() {} + + // IUnknown + DECL_IUNKNOWN_INHERITED +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/XULListboxAccessibleWrap.cpp b/accessible/windows/msaa/XULListboxAccessibleWrap.cpp new file mode 100644 index 000000000..4bd0fb512 --- /dev/null +++ b/accessible/windows/msaa/XULListboxAccessibleWrap.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "XULListboxAccessibleWrap.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULListboxAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(XULListboxAccessibleWrap, + XULListboxAccessible) + +IMPL_IUNKNOWN_QUERY_HEAD(XULListboxAccessibleWrap) +IMPL_IUNKNOWN_QUERY_CLASS_COND(ia2AccessibleTable, + !IsDefunct() && IsMulticolumn()); +IMPL_IUNKNOWN_QUERY_CLASS(AccessibleWrap) +IMPL_IUNKNOWN_QUERY_TAIL + +void +XULListboxAccessibleWrap::Shutdown() +{ + ia2AccessibleTable::mTable = nullptr; + XULListboxAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULListCellAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(XULListCellAccessibleWrap, + XULListCellAccessible) + +IMPL_IUNKNOWN_INHERITED1(XULListCellAccessibleWrap, + HyperTextAccessibleWrap, + ia2AccessibleTableCell) + +void +XULListCellAccessibleWrap::Shutdown() +{ + ia2AccessibleTableCell::mTableCell = nullptr; + XULListCellAccessible::Shutdown(); +} diff --git a/accessible/windows/msaa/XULListboxAccessibleWrap.h b/accessible/windows/msaa/XULListboxAccessibleWrap.h new file mode 100644 index 000000000..37db2d70a --- /dev/null +++ b/accessible/windows/msaa/XULListboxAccessibleWrap.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_a11y_XULListboxAccessibleWrap_h__ +#define mozilla_a11y_XULListboxAccessibleWrap_h__ + +#include "XULListboxAccessible.h" + +#include "ia2AccessibleTable.h" +#include "ia2AccessibleTableCell.h" + +namespace mozilla { +namespace a11y { + +/** + * IA2 wrapper class for XULListboxAccessible class implementing + * IAccessibleTable and IAccessibleTable2 interfaces. + */ +class XULListboxAccessibleWrap : public XULListboxAccessible, + public ia2AccessibleTable +{ + ~XULListboxAccessibleWrap() {} + +public: + XULListboxAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + XULListboxAccessible(aContent, aDoc), ia2AccessibleTable(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +/** + * IA2 wrapper class for XULListCellAccessible class, implements + * IAccessibleTableCell interface. + */ +class XULListCellAccessibleWrap : public XULListCellAccessible, + public ia2AccessibleTableCell +{ + ~XULListCellAccessibleWrap() {} + +public: + XULListCellAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + XULListCellAccessible(aContent, aDoc), ia2AccessibleTableCell(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/XULMenuAccessibleWrap.cpp b/accessible/windows/msaa/XULMenuAccessibleWrap.cpp new file mode 100644 index 000000000..ba0075bac --- /dev/null +++ b/accessible/windows/msaa/XULMenuAccessibleWrap.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "XULMenuAccessibleWrap.h" +#include "nsNameSpaceManager.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULMenuAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +XULMenuitemAccessibleWrap:: + XULMenuitemAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : + XULMenuitemAccessible(aContent, aDoc) +{ +} + +ENameValueFlag +XULMenuitemAccessibleWrap::Name(nsString& aName) +{ + // XXX This should be done in MSAA's get_accName() so that Accessible::Name()] + // provides the same results on all platforms + XULMenuitemAccessible::Name(aName); + if (aName.IsEmpty()) + return eNameOK; + + nsAutoString accel; + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::acceltext, accel); + if (!accel.IsEmpty()) + aName += NS_LITERAL_STRING("\t") + accel; + + return eNameOK; +} diff --git a/accessible/windows/msaa/XULMenuAccessibleWrap.h b/accessible/windows/msaa/XULMenuAccessibleWrap.h new file mode 100644 index 000000000..a07182241 --- /dev/null +++ b/accessible/windows/msaa/XULMenuAccessibleWrap.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_a11y_XULMenuAccessibleWrap_h__ +#define mozilla_a11y_XULMenuAccessibleWrap_h__ + +#include "XULMenuAccessible.h" + +namespace mozilla { +namespace a11y { + +class XULMenuitemAccessibleWrap : public XULMenuitemAccessible +{ +public: + XULMenuitemAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc); + virtual ~XULMenuitemAccessibleWrap() {} + + // nsIAccessible + virtual mozilla::a11y::ENameValueFlag Name(nsString& aName); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/XULTreeGridAccessibleWrap.cpp b/accessible/windows/msaa/XULTreeGridAccessibleWrap.cpp new file mode 100644 index 000000000..e5cc4a09b --- /dev/null +++ b/accessible/windows/msaa/XULTreeGridAccessibleWrap.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "XULTreeGridAccessibleWrap.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(XULTreeGridAccessibleWrap, + XULTreeGridAccessible) + +IMPL_IUNKNOWN_INHERITED1(XULTreeGridAccessibleWrap, + AccessibleWrap, + ia2AccessibleTable) + +void +XULTreeGridAccessibleWrap::Shutdown() +{ + ia2AccessibleTable::mTable = nullptr; + XULTreeGridAccessible::Shutdown(); +} + +//////////////////////////////////////////////////////////////////////////////// +// XULTreeGridCellAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS_INHERITED0(XULTreeGridCellAccessibleWrap, + XULTreeGridCellAccessible) + +IMPL_IUNKNOWN_INHERITED1(XULTreeGridCellAccessibleWrap, + AccessibleWrap, + ia2AccessibleTableCell) + +void +XULTreeGridCellAccessibleWrap::Shutdown() +{ + ia2AccessibleTableCell::mTableCell = nullptr; + XULTreeGridCellAccessible::Shutdown(); +} diff --git a/accessible/windows/msaa/XULTreeGridAccessibleWrap.h b/accessible/windows/msaa/XULTreeGridAccessibleWrap.h new file mode 100644 index 000000000..3c6c62699 --- /dev/null +++ b/accessible/windows/msaa/XULTreeGridAccessibleWrap.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_a11y_XULTreeGridAccessibleWrap_h__ +#define mozilla_a11y_XULTreeGridAccessibleWrap_h__ + +#include "XULTreeGridAccessible.h" + +#include "ia2AccessibleTable.h" +#include "ia2AccessibleTableCell.h" + +namespace mozilla { +namespace a11y { + +/** + * IA2 wrapper class for XULTreeGridAccessible class implementing + * IAccessibleTable and IAccessibleTable2 interfaces. + */ +class XULTreeGridAccessibleWrap : public XULTreeGridAccessible, + public ia2AccessibleTable +{ + ~XULTreeGridAccessibleWrap() {} + +public: + XULTreeGridAccessibleWrap(nsIContent* aContent, DocAccessible* aDoc, + nsTreeBodyFrame* aTree) : + XULTreeGridAccessible(aContent, aDoc, aTree), ia2AccessibleTable(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +/** + * IA2 wrapper class for XULTreeGridCellAccessible class, implements + * IAccessibleTableCell interface. + */ +class XULTreeGridCellAccessibleWrap : public XULTreeGridCellAccessible, + public ia2AccessibleTableCell +{ + ~XULTreeGridCellAccessibleWrap() {} + +public: + XULTreeGridCellAccessibleWrap(nsIContent* aContent, + DocAccessible* aDoc, + XULTreeGridRowAccessible* aRowAcc, + nsITreeBoxObject* aTree, + nsITreeView* aTreeView, + int32_t aRow, nsITreeColumn* aColumn) : + XULTreeGridCellAccessible(aContent, aDoc, aRowAcc, aTree, aTreeView, aRow, + aColumn), ia2AccessibleTableCell(this) {} + + // IUnknown + DECL_IUNKNOWN_INHERITED + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + + virtual void Shutdown() override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/msaa/moz.build b/accessible/windows/msaa/moz.build new file mode 100644 index 000000000..54c8c6686 --- /dev/null +++ b/accessible/windows/msaa/moz.build @@ -0,0 +1,76 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS += [ + 'IUnknownImpl.h', +] + +EXPORTS.mozilla.a11y += [ + 'AccessibleWrap.h', + 'Compatibility.h', + 'HyperTextAccessibleWrap.h', + 'IDSet.h', + 'MsaaIdGenerator.h', +] + +UNIFIED_SOURCES += [ + 'AccessibleWrap.cpp', + 'ApplicationAccessibleWrap.cpp', + 'ARIAGridAccessibleWrap.cpp', + 'Compatibility.cpp', + 'DocAccessibleWrap.cpp', + 'EnumVariant.cpp', + 'HTMLTableAccessibleWrap.cpp', + 'HTMLWin32ObjectAccessible.cpp', + 'HyperTextAccessibleWrap.cpp', + 'ImageAccessibleWrap.cpp', + 'IUnknownImpl.cpp', + 'MsaaIdGenerator.cpp', + 'nsWinUtils.cpp', + 'Platform.cpp', + 'RootAccessibleWrap.cpp', + 'TextLeafAccessibleWrap.cpp', +] + +# This file cannot be built in unified mode because it includes ISimpleDOMNode_i.c. +SOURCES += [ + 'ServiceProvider.cpp', +] + +if CONFIG['MOZ_XUL']: + UNIFIED_SOURCES += [ + 'XULListboxAccessibleWrap.cpp', + 'XULMenuAccessibleWrap.cpp', + 'XULTreeGridAccessibleWrap.cpp', + ] + +LOCAL_INCLUDES += [ + '/accessible/base', + '/accessible/generic', + '/accessible/html', + '/accessible/ipc', + '/accessible/ipc/win', + '/accessible/windows', + '/accessible/windows/ia2', + '/accessible/windows/sdn', + '/accessible/windows/uia', + '/accessible/xpcom', + '/accessible/xul', + '/dom/base', + '/layout/style', +] + +# The Windows MIDL code generator creates things like: +# +# #endif !_MIDL_USE_GUIDDEF_ +# +# which clang-cl complains about. MSVC doesn't, so turn this warning off. +if CONFIG['CLANG_CL']: + CXXFLAGS += ['-Wno-extra-tokens'] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/accessible/windows/msaa/nsEventMap.h b/accessible/windows/msaa/nsEventMap.h new file mode 100644 index 000000000..8af992e1c --- /dev/null +++ b/accessible/windows/msaa/nsEventMap.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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 <winuser.h> +#include "AccessibleEventId.h" + +const uint32_t kEVENT_WIN_UNKNOWN = 0x00000000; + +static const uint32_t gWinEventMap[] = { + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent doesn't have 0 constant + EVENT_OBJECT_SHOW, // nsIAccessibleEvent::EVENT_SHOW + EVENT_OBJECT_HIDE, // nsIAccessibleEvent::EVENT_HIDE + EVENT_OBJECT_REORDER, // nsIAccessibleEvent::EVENT_REORDER + IA2_EVENT_ACTIVE_DECENDENT_CHANGED, // nsIAccessibleEvent::EVENT_ACTIVE_DECENDENT_CHANGED + EVENT_OBJECT_FOCUS, // nsIAccessibleEvent::EVENT_FOCUS + EVENT_OBJECT_STATECHANGE, // nsIAccessibleEvent::EVENT_STATE_CHANGE + EVENT_OBJECT_LOCATIONCHANGE, // nsIAccessibleEvent::EVENT_LOCATION_CHANGE + EVENT_OBJECT_NAMECHANGE, // nsIAccessibleEvent::EVENT_NAME_CHANGE + EVENT_OBJECT_DESCRIPTIONCHANGE, // nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE + EVENT_OBJECT_VALUECHANGE, // nsIAccessibleEvent::EVENT_VALUE_CHANGE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_HELP_CHANGE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_DEFACTION_CHANGE + IA2_EVENT_ACTION_CHANGED, // nsIAccessibleEvent::EVENT_ACTION_CHANGE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_ACCELERATOR_CHANGE + EVENT_OBJECT_SELECTION, // nsIAccessibleEvent::EVENT_SELECTION + EVENT_OBJECT_SELECTIONADD, // nsIAccessibleEvent::EVENT_SELECTION_ADD + EVENT_OBJECT_SELECTIONREMOVE, // nsIAccessibleEvent::EVENT_SELECTION_REMOVE + EVENT_OBJECT_SELECTIONWITHIN, // nsIAccessibleEvent::EVENT_SELECTION_WITHIN + EVENT_SYSTEM_ALERT, // nsIAccessibleEvent::EVENT_ALERT + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_FOREGROUND + EVENT_SYSTEM_MENUSTART, // nsIAccessibleEvent::EVENT_MENU_START + EVENT_SYSTEM_MENUEND, // nsIAccessibleEvent::EVENT_MENU_END + EVENT_SYSTEM_MENUPOPUPSTART, // nsIAccessibleEvent::EVENT_MENUPOPUP_START + EVENT_SYSTEM_MENUPOPUPEND, // nsIAccessibleEvent::EVENT_MENUPOPUP_END + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_CAPTURE_START + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_CAPTURE_END + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_MOVESIZE_START + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_MOVESIZE_END + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_CONTEXT_HELP_START + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_CONTEXT_HELP_END + EVENT_SYSTEM_DRAGDROPSTART, // nsIAccessibleEvent::EVENT_DRAGDROP_START + EVENT_SYSTEM_DRAGDROPEND, // nsIAccessibleEvent::EVENT_DRAGDROP_END + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_DIALOG_START + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_DIALOG_END + EVENT_SYSTEM_SCROLLINGSTART, // nsIAccessibleEvent::EVENT_SCROLLING_START + EVENT_SYSTEM_SCROLLINGEND, // nsIAccessibleEvent::EVENT_SCROLLING_END + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_MINIMIZE_START + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_MINIMIZE_END + IA2_EVENT_DOCUMENT_LOAD_COMPLETE, // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE + IA2_EVENT_DOCUMENT_RELOAD, // nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD + IA2_EVENT_DOCUMENT_LOAD_STOPPED, // nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED + IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED + IA2_EVENT_DOCUMENT_CONTENT_CHANGED, // nsIAccessibleEvent::EVENT_DOCUMENT_CONTENT_CHANGED + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_PROPERTY_CHANGED + IA2_EVENT_PAGE_CHANGED, // nsIAccessibleEvent::IA2_EVENT_PAGE_CHANGED + IA2_EVENT_TEXT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED + IA2_EVENT_TEXT_CARET_MOVED, // nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED + IA2_EVENT_TEXT_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_CHANGED + IA2_EVENT_TEXT_INSERTED, // nsIAccessibleEvent::EVENT_TEXT_INSERTED + IA2_EVENT_TEXT_REMOVED, // nsIAccessibleEvent::EVENT_TEXT_REMOVED + IA2_EVENT_TEXT_UPDATED, // nsIAccessibleEvent::EVENT_TEXT_UPDATED + IA2_EVENT_TEXT_SELECTION_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED + IA2_EVENT_VISIBLE_DATA_CHANGED, // nsIAccessibleEvent::EVENT_VISIBLE_DATA_CHANGED + IA2_EVENT_TEXT_COLUMN_CHANGED, // nsIAccessibleEvent::EVENT_TEXT_COLUMN_CHANGED + IA2_EVENT_SECTION_CHANGED, // nsIAccessibleEvent::EVENT_SECTION_CHANGED + IA2_EVENT_TABLE_CAPTION_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_CAPTION_CHANGED + IA2_EVENT_TABLE_MODEL_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_MODEL_CHANGED + IA2_EVENT_TABLE_SUMMARY_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_SUMMARY_CHANGED + IA2_EVENT_TABLE_ROW_DESCRIPTION_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_ROW_DESCRIPTION_CHANGED + IA2_EVENT_TABLE_ROW_HEADER_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_ROW_HEADER_CHANGED + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_ROW_INSERT + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_ROW_DELETE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_ROW_REORDER + IA2_EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED + IA2_EVENT_TABLE_COLUMN_HEADER_CHANGED, // nsIAccessibleEvent::EVENT_TABLE_COLUMN_HEADER_CHANGED + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_COLUMN_INSERT + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_COLUMN_DELETE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_TABLE_COLUMN_REORDER + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_CREATE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_DESTROY + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_RESIZE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_WINDOW_RESTORE + IA2_EVENT_HYPERLINK_END_INDEX_CHANGED, // nsIAccessibleEvent::EVENT_HYPERLINK_END_INDEX_CHANGED + IA2_EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED + IA2_EVENT_HYPERLINK_SELECTED_LINK_CHANGED, // nsIAccessibleEvent::EVENT_HYPERLINK_SELECTED_LINK_CHANGED + IA2_EVENT_HYPERTEXT_LINK_ACTIVATED, // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_ACTIVATED + IA2_EVENT_HYPERTEXT_LINK_SELECTED, // nsIAccessibleEvent::EVENT_HYPERTEXT_LINK_SELECTED + IA2_EVENT_HYPERLINK_START_INDEX_CHANGED, // nsIAccessibleEvent::EVENT_HYPERLINK_START_INDEX_CHANGED + IA2_EVENT_HYPERTEXT_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_CHANGED + IA2_EVENT_HYPERTEXT_NLINKS_CHANGED, // nsIAccessibleEvent::EVENT_HYPERTEXT_NLINKS_CHANGED + IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED, // nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED + EVENT_OBJECT_VALUECHANGE // nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE +}; + diff --git a/accessible/windows/msaa/nsWinUtils.cpp b/accessible/windows/msaa/nsWinUtils.cpp new file mode 100644 index 000000000..b49cd0263 --- /dev/null +++ b/accessible/windows/msaa/nsWinUtils.cpp @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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 "nsWinUtils.h" + +#include "Compatibility.h" +#include "DocAccessible.h" +#include "nsAccessibilityService.h" +#include "nsCoreUtils.h" + +#include "mozilla/Preferences.h" +#include "nsArrayUtils.h" +#include "nsIArray.h" +#include "nsICSSDeclaration.h" +#include "nsIDocument.h" +#include "nsIDocShellTreeItem.h" +#include "mozilla/dom/Element.h" +#include "nsXULAppAPI.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using mozilla::dom::Element; + +// Window property used by ipc related code in identifying accessible +// tab windows. +const wchar_t* kPropNameTabContent = L"AccessibleTabWindow"; + +/** + * WindowProc to process WM_GETOBJECT messages, used in windows emulation mode. + */ +static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam); + +bool nsWinUtils::sWindowEmulationStarted = false; + +already_AddRefed<nsIDOMCSSStyleDeclaration> +nsWinUtils::GetComputedStyleDeclaration(nsIContent* aContent) +{ + nsIContent* elm = nsCoreUtils::GetDOMElementFor(aContent); + if (!elm) + return nullptr; + + // Returns number of items in style declaration + nsCOMPtr<nsPIDOMWindowInner> window = elm->OwnerDoc()->GetInnerWindow(); + if (!window) + return nullptr; + + ErrorResult dummy; + nsCOMPtr<nsICSSDeclaration> cssDecl; + nsCOMPtr<Element> domElement(do_QueryInterface(elm)); + cssDecl = window->GetComputedStyle(*domElement, EmptyString(), dummy); + nsCOMPtr<nsIDOMCSSStyleDeclaration> domDecl = do_QueryInterface(cssDecl); + dummy.SuppressException(); + return domDecl.forget(); +} + +bool +nsWinUtils::MaybeStartWindowEmulation() +{ + // Register window class that'll be used for document accessibles associated + // with tabs. + if (IPCAccessibilityActive()) + return false; + + if (Compatibility::IsJAWS() || Compatibility::IsWE() || + Compatibility::IsDolphin() || + XRE_IsContentProcess()) { + RegisterNativeWindow(kClassNameTabContent); + sWindowEmulationStarted = true; + return true; + } + + return false; +} + +void +nsWinUtils::ShutdownWindowEmulation() +{ + // Unregister window call that's used for document accessibles associated + // with tabs. + if (IsWindowEmulationStarted()) { + ::UnregisterClassW(kClassNameTabContent, GetModuleHandle(nullptr)); + sWindowEmulationStarted = false; + } +} + +void +nsWinUtils::RegisterNativeWindow(LPCWSTR aWindowClass) +{ + WNDCLASSW wc; + wc.style = CS_GLOBALCLASS; + wc.lpfnWndProc = WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandle(nullptr); + wc.hIcon = nullptr; + wc.hCursor = nullptr; + wc.hbrBackground = nullptr; + wc.lpszMenuName = nullptr; + wc.lpszClassName = aWindowClass; + ::RegisterClassW(&wc); +} + +HWND +nsWinUtils::CreateNativeWindow(LPCWSTR aWindowClass, HWND aParentWnd, + int aX, int aY, int aWidth, int aHeight, + bool aIsActive) +{ + HWND hwnd = ::CreateWindowExW(WS_EX_TRANSPARENT, aWindowClass, + L"NetscapeDispatchWnd", + WS_CHILD | (aIsActive ? WS_VISIBLE : 0), + aX, aY, aWidth, aHeight, + aParentWnd, + nullptr, + GetModuleHandle(nullptr), + nullptr); + if (hwnd) { + // Mark this window so that ipc related code can identify it. + ::SetPropW(hwnd, kPropNameTabContent, (HANDLE)1); + } + return hwnd; +} + +void +nsWinUtils::ShowNativeWindow(HWND aWnd) +{ + ::ShowWindow(aWnd, SW_SHOW); +} + +void +nsWinUtils::HideNativeWindow(HWND aWnd) +{ + ::SetWindowPos(aWnd, nullptr, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | + SWP_NOZORDER | SWP_NOACTIVATE); +} + +LRESULT CALLBACK +WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Note, this window's message handling should not invoke any call that + // may result in a cross-process ipc call. Doing so may violate RPC + // message semantics. + + switch (msg) { + case WM_GETOBJECT: + { + // Do explicit casting to make it working on 64bit systems (see bug 649236 + // for details). + int32_t objId = static_cast<DWORD>(lParam); + if (objId == OBJID_CLIENT) { + DocAccessible* document = + reinterpret_cast<DocAccessible*>(::GetPropW(hWnd, kPropNameDocAcc)); + if (document) { + IAccessible* msaaAccessible = nullptr; + document->GetNativeInterface((void**)&msaaAccessible); // does an addref + if (msaaAccessible) { + LRESULT result = ::LresultFromObject(IID_IAccessible, wParam, + msaaAccessible); // does an addref + msaaAccessible->Release(); // release extra addref + return result; + } + } + } + return 0; + } + case WM_NCHITTEST: + { + LRESULT lRet = ::DefWindowProc(hWnd, msg, wParam, lParam); + if (HTCLIENT == lRet) + lRet = HTTRANSPARENT; + return lRet; + } + } + + return ::DefWindowProcW(hWnd, msg, wParam, lParam); +} diff --git a/accessible/windows/msaa/nsWinUtils.h b/accessible/windows/msaa/nsWinUtils.h new file mode 100644 index 000000000..55089e722 --- /dev/null +++ b/accessible/windows/msaa/nsWinUtils.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* 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/. */ + +#ifndef nsWinUtils_h_ +#define nsWinUtils_h_ + +#include <windows.h> + +#include "nsIDOMCSSStyleDeclaration.h" +#include "nsCOMPtr.h" + +class nsIContent; + +namespace mozilla { +namespace a11y { + +class DocAccessible; + +const LPCWSTR kClassNameRoot = L"MozillaUIWindowClass"; +const LPCWSTR kClassNameTabContent = L"MozillaContentWindowClass"; +const LPCWSTR kPropNameDocAcc = L"MozDocAccessible"; + +class nsWinUtils +{ +public: + /** + * Return computed styles declaration for the given node. + * + * @note Please use it carefully since it can shutdown the accessible tree + * you operate on. + */ + static already_AddRefed<nsIDOMCSSStyleDeclaration> + GetComputedStyleDeclaration(nsIContent* aContent); + + /** + * Start window emulation if presence of specific AT is detected. + */ + static bool MaybeStartWindowEmulation(); + + /** + * Free resources used for window emulation. + */ + static void ShutdownWindowEmulation(); + + /** + * Return true if window emulation is started. + */ + static bool IsWindowEmulationStarted() { return sWindowEmulationStarted; } + + /** + * Helper to register window class. + */ + static void RegisterNativeWindow(LPCWSTR aWindowClass); + + /** + * Helper to create a window. + */ + static HWND CreateNativeWindow(LPCWSTR aWindowClass, HWND aParentWnd, + int aX, int aY, int aWidth, int aHeight, + bool aIsActive); + + /** + * Helper to show window. + */ + static void ShowNativeWindow(HWND aWnd); + + /** + * Helper to hide window. + */ + static void HideNativeWindow(HWND aWnd); + +private: + /** + * Flag that indicates if window emulation is started. + */ + static bool sWindowEmulationStarted; +}; + +} // namespace a11y +} // namespace mozilla + +#endif |