From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- accessible/windows/msaa/AccessibleWrap.cpp | 1686 ++++++++++++++++++++++++++++ 1 file changed, 1686 insertions(+) create mode 100644 accessible/windows/msaa/AccessibleWrap.cpp (limited to 'accessible/windows/msaa/AccessibleWrap.cpp') 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(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(this); + else if (IID_IDispatch == iid || IID_IAccessible == iid) + *ppv = static_cast(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(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(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(*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(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 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 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 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 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 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 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 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 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 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& 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 mArray; + uint32_t mCurIndex; +}; + +STDMETHODIMP +AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject) +{ + A11Y_TRYBLOCK_BEGIN + + if (iid == IID_IEnumVARIANT) { + *ppvObject = static_cast(this); + AddRef(); + return S_OK; + } + if (iid == IID_IUnknown) { + *ppvObject = static_cast(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 selectedItems; + SelectedItems(&selectedItems); + + // 1) Create and initialize the enumeration + RefPtr 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 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 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 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 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 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 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 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(this), dispIdMember, + wFlags, pDispParams, pVarResult, pExcepInfo, + puArgErr); +} + +void +AccessibleWrap::GetNativeInterface(void** aOutAccessible) +{ + *aOutAccessible = static_cast(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(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(aAccessible)->mID; + MOZ_ASSERT(id != kNoID); + return id; + } + + if (!aAccessible->Document()) + return 0; + + uint32_t* id = & static_cast(aAccessible)->mID; + if (*id != kNoID) + return *id; + + *id = sIDGen.GetID(); + + MOZ_ASSERT(!aAccessible->IsProxy()); + DocAccessibleWrap* doc = + static_cast(aAccessible->Document()); + doc->AddID(*id, static_cast(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(ipcDoc->Manager()); + MOZ_ASSERT(tab); + return reinterpret_cast(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 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(widget->GetNativeData(NS_NATIVE_WINDOW)); + } + } + } + + return static_cast(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(&msaaAccessible)); + return static_cast(msaaAccessible); +} + +static Accessible* +GetAccessibleInSubtree(DocAccessible* aDoc, uint32_t aID) +{ + Accessible* child = static_cast(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 +GetProxiedAccessibleInSubtree(const DocAccessibleParent* aDoc, + const VARIANT& aVarChild) +{ + auto wrapper = static_cast(WrapperFor(aDoc)); + RefPtr 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 disp; + if (FAILED(comProxy->get_accChild(aVarChild, getter_AddRefs(disp)))) { + return nullptr; + } + + return disp.forget(); +} + +already_AddRefed +AccessibleWrap::GetIAccessibleFor(const VARIANT& aVarChild, bool* aIsDefunct) +{ + if (aVarChild.vt != VT_I4) + return nullptr; + + VARIANT varChild = aVarChild; + + MOZ_ASSERT(aIsDefunct); + *aIsDefunct = false; + + RefPtr 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(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(varChild.lVal)); + + // If it is a document then just return an accessible. + if (child && IsDoc()) { + *aIsDefunct = child->IsDefunct(); + static_cast(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(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 disp = GetProxiedAccessibleInSubtree(proxyDoc, varChild); + if (!disp) { + return nullptr; + } + + MOZ_ASSERT(mscom::IsProxy(disp)); + DebugOnly hr = disp->QueryInterface(IID_IAccessible, + getter_AddRefs(result)); + MOZ_ASSERT(SUCCEEDED(hr)); + return result.forget(); + } + + return nullptr; +} + +already_AddRefed +AccessibleWrap::GetRemoteIAccessibleFor(const VARIANT& aVarChild) +{ + DocAccessibleParent* proxyDoc = nullptr; + DocAccessible* doc = Document(); + const nsTArray* remoteDocs = + DocManager::TopLevelRemoteDocs(); + if (!remoteDocs) { + return nullptr; + } + + RefPtr 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 disp = + GetProxiedAccessibleInSubtree(remoteDoc, aVarChild); + if (!disp) { + continue; + } + + DebugOnly 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); +} -- cgit v1.2.3