diff options
Diffstat (limited to 'accessible/windows')
80 files changed, 11569 insertions, 0 deletions
diff --git a/accessible/windows/ProxyWrappers.h b/accessible/windows/ProxyWrappers.h new file mode 100644 index 000000000..6e7c84e18 --- /dev/null +++ b/accessible/windows/ProxyWrappers.h @@ -0,0 +1,92 @@ +/* -*- 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_ProxyWrappers_h +#define MOZILLA_A11Y_ProxyWrappers_h + +#include "HyperTextAccessible.h" + +namespace mozilla { +namespace a11y { + +class ProxyAccessibleWrap : public AccessibleWrap +{ +public: + ProxyAccessibleWrap(ProxyAccessible* aProxy) : + AccessibleWrap(nullptr, nullptr) + { + mType = eProxyType; + mBits.proxy = aProxy; + } + + virtual void Shutdown() override + { + mBits.proxy = nullptr; + mStateFlags |= eIsDefunct; + } + + virtual void GetNativeInterface(void** aOutAccessible) override + { + mBits.proxy->GetCOMInterface(aOutAccessible); + } +}; + +class HyperTextProxyAccessibleWrap : public HyperTextAccessibleWrap +{ +public: + HyperTextProxyAccessibleWrap(ProxyAccessible* aProxy) : + HyperTextAccessibleWrap(nullptr, nullptr) + { + mType = eProxyType; + mBits.proxy = aProxy; + } + + virtual void Shutdown() override + { + mBits.proxy = nullptr; + mStateFlags |= eIsDefunct; + } + + virtual void GetNativeInterface(void** aOutAccessible) override + { + mBits.proxy->GetCOMInterface(aOutAccessible); + } +}; + +class DocProxyAccessibleWrap : public HyperTextProxyAccessibleWrap +{ +public: + DocProxyAccessibleWrap(ProxyAccessible* aProxy) : + HyperTextProxyAccessibleWrap(aProxy) + { mGenericTypes |= eDocument; } + + 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); } + +private: + /* + * This provides a mapping from 32 bit id to accessible objects. + */ + nsDataHashtable<nsUint32HashKey, AccessibleWrap*> mIDToAccessibleMap; +}; + +template<typename T> +inline ProxyAccessible* +HyperTextProxyFor(T* aWrapper) +{ + static_assert(mozilla::IsBaseOf<IUnknown, T>::value, "only IAccessible* should be passed in"); + auto wrapper = static_cast<HyperTextProxyAccessibleWrap*>(aWrapper); + return wrapper->IsProxy() ? wrapper->Proxy() : nullptr; +} + +} +} + +#endif diff --git a/accessible/windows/ia2/ia2Accessible.cpp b/accessible/windows/ia2/ia2Accessible.cpp new file mode 100644 index 000000000..c72719b51 --- /dev/null +++ b/accessible/windows/ia2/ia2Accessible.cpp @@ -0,0 +1,810 @@ +/* -*- 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 "Accessible2_i.c" +#include "Accessible2_2_i.c" +#include "Accessible2_3_i.c" +#include "AccessibleRole.h" +#include "AccessibleStates.h" + +#include "Compatibility.h" +#include "ia2AccessibleRelation.h" +#include "IUnknownImpl.h" +#include "nsCoreUtils.h" +#include "nsIAccessibleTypes.h" +#include "mozilla/a11y/PDocAccessible.h" +#include "Relation.h" +#include "TextRange-inl.h" +#include "nsAccessibilityService.h" + +#include "nsIPersistentProperties2.h" +#include "nsISimpleEnumerator.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +template<typename String> static void EscapeAttributeChars(String& aStr); + +//////////////////////////////////////////////////////////////////////////////// +// ia2Accessible +//////////////////////////////////////////////////////////////////////////////// + +STDMETHODIMP +ia2Accessible::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessible2_3 == iid) + *ppv = static_cast<IAccessible2_3*>(this); + else if (IID_IAccessible2_2 == iid) + *ppv = static_cast<IAccessible2_2*>(this); + else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off()) + *ppv = static_cast<IAccessible2*>(this); + + if (*ppv) { + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessible2 + +STDMETHODIMP +ia2Accessible::get_nRelations(long* aNRelations) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNRelations) + return E_INVALIDARG; + *aNRelations = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + MOZ_ASSERT(!acc->IsProxy()); + + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + Relation rel = acc->RelationByType(sRelationTypePairs[idx].first); + if (rel.Next()) + (*aNRelations)++; + } + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_relation(long aRelationIndex, + IAccessibleRelation** aRelation) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRelation || aRelationIndex < 0) + return E_INVALIDARG; + *aRelation = nullptr; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + MOZ_ASSERT(!acc->IsProxy()); + + long relIdx = 0; + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + RelationType relationType = sRelationTypePairs[idx].first; + Relation rel = acc->RelationByType(relationType); + RefPtr<ia2AccessibleRelation> ia2Relation = + new ia2AccessibleRelation(relationType, &rel); + if (ia2Relation->HasTargets()) { + if (relIdx == aRelationIndex) { + ia2Relation.forget(aRelation); + return S_OK; + } + + relIdx++; + } + } + + return E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_relations(long aMaxRelations, + IAccessibleRelation** aRelation, + long *aNRelations) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRelation || !aNRelations || aMaxRelations <= 0) + return E_INVALIDARG; + *aNRelations = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + MOZ_ASSERT(!acc->IsProxy()); + + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) && + *aNRelations < aMaxRelations; idx++) { + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + RelationType relationType = sRelationTypePairs[idx].first; + Relation rel = acc->RelationByType(relationType); + RefPtr<ia2AccessibleRelation> ia2Rel = + new ia2AccessibleRelation(relationType, &rel); + if (ia2Rel->HasTargets()) { + ia2Rel.forget(aRelation + (*aNRelations)); + (*aNRelations)++; + } + } + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::role(long* aRole) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRole) + return E_INVALIDARG; + *aRole = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + +#define ROLE(_geckoRole, stringRole, atkRole, macRole, \ + msaaRole, ia2Role, nameRule) \ + case roles::_geckoRole: \ + *aRole = ia2Role; \ + break; + + a11y::role geckoRole; + MOZ_ASSERT(!acc->IsProxy()); + geckoRole = acc->Role(); + switch (geckoRole) { +#include "RoleMap.h" + default: + MOZ_CRASH("Unknown role."); + } + +#undef ROLE + + // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call + // the IA2 role a ROLE_OUTLINEITEM. + MOZ_ASSERT(!acc->IsProxy()); + if (geckoRole == roles::ROW) { + Accessible* xpParent = acc->Parent(); + if (xpParent && xpParent->Role() == roles::TREE_TABLE) + *aRole = ROLE_SYSTEM_OUTLINEITEM; + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::scrollTo(enum IA2ScrollType aScrollType) +{ + A11Y_TRYBLOCK_BEGIN + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + MOZ_ASSERT(!acc->IsProxy()); + nsCoreUtils::ScrollTo(acc->Document()->PresShell(), acc->GetContent(), + aScrollType); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType, + long aX, long aY) +{ + A11Y_TRYBLOCK_BEGIN + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : + nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + MOZ_ASSERT(!acc->IsProxy()); + acc->ScrollToPoint(geckoCoordType, aX, aY); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_groupPosition(long* aGroupLevel, + long* aSimilarItemsInGroup, + long* aPositionInGroup) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aGroupLevel || !aSimilarItemsInGroup || !aPositionInGroup) + return E_INVALIDARG; + + *aGroupLevel = 0; + *aSimilarItemsInGroup = 0; + *aPositionInGroup = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + GroupPos groupPos = acc->GroupPosition(); + + // Group information for accessibles having level only (like html headings + // elements) isn't exposed by this method. AT should look for 'level' object + // attribute. + if (!groupPos.setSize && !groupPos.posInSet) + return S_FALSE; + + *aGroupLevel = groupPos.level; + *aSimilarItemsInGroup = groupPos.setSize; + *aPositionInGroup = groupPos.posInSet; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_states(AccessibleStates* aStates) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStates) + return E_INVALIDARG; + *aStates = 0; + + // XXX: bug 344674 should come with better approach that we have here. + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) { + *aStates = IA2_STATE_DEFUNCT; + return S_OK; + } + + uint64_t state; + MOZ_ASSERT(!acc->IsProxy()); + state = acc->State(); + + if (state & states::INVALID) + *aStates |= IA2_STATE_INVALID_ENTRY; + if (state & states::REQUIRED) + *aStates |= IA2_STATE_REQUIRED; + + // The following IA2 states are not supported by Gecko + // IA2_STATE_ARMED + // IA2_STATE_MANAGES_DESCENDANTS + // IA2_STATE_ICONIFIED + // IA2_STATE_INVALID // This is not a state, it is the absence of a state + + if (state & states::ACTIVE) + *aStates |= IA2_STATE_ACTIVE; + if (state & states::DEFUNCT) + *aStates |= IA2_STATE_DEFUNCT; + if (state & states::EDITABLE) + *aStates |= IA2_STATE_EDITABLE; + if (state & states::HORIZONTAL) + *aStates |= IA2_STATE_HORIZONTAL; + if (state & states::MODAL) + *aStates |= IA2_STATE_MODAL; + if (state & states::MULTI_LINE) + *aStates |= IA2_STATE_MULTI_LINE; + if (state & states::OPAQUE1) + *aStates |= IA2_STATE_OPAQUE; + if (state & states::SELECTABLE_TEXT) + *aStates |= IA2_STATE_SELECTABLE_TEXT; + if (state & states::SINGLE_LINE) + *aStates |= IA2_STATE_SINGLE_LINE; + if (state & states::STALE) + *aStates |= IA2_STATE_STALE; + if (state & states::SUPPORTS_AUTOCOMPLETION) + *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; + if (state & states::TRANSIENT) + *aStates |= IA2_STATE_TRANSIENT; + if (state & states::VERTICAL) + *aStates |= IA2_STATE_VERTICAL; + if (state & states::CHECKED) + *aStates |= IA2_STATE_CHECKABLE; + if (state & states::PINNED) + *aStates |= IA2_STATE_PINNED; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_extendedRole(BSTR* aExtendedRole) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aExtendedRole) + return E_INVALIDARG; + + *aExtendedRole = nullptr; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_localizedExtendedRole(BSTR* aLocalizedExtendedRole) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocalizedExtendedRole) + return E_INVALIDARG; + + *aLocalizedExtendedRole = nullptr; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_nExtendedStates(long* aNExtendedStates) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNExtendedStates) + return E_INVALIDARG; + + *aNExtendedStates = 0; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_extendedStates(long aMaxExtendedStates, + BSTR** aExtendedStates, + long* aNExtendedStates) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aExtendedStates || !aNExtendedStates) + return E_INVALIDARG; + + *aExtendedStates = nullptr; + *aNExtendedStates = 0; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_localizedExtendedStates(long aMaxLocalizedExtendedStates, + BSTR** aLocalizedExtendedStates, + long* aNLocalizedExtendedStates) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocalizedExtendedStates || !aNLocalizedExtendedStates) + return E_INVALIDARG; + + *aLocalizedExtendedStates = nullptr; + *aNLocalizedExtendedStates = 0; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_uniqueID(long* aUniqueID) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aUniqueID) + return E_INVALIDARG; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + *aUniqueID = AccessibleWrap::GetChildIDFor(acc); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_windowHandle(HWND* aWindowHandle) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aWindowHandle) + return E_INVALIDARG; + *aWindowHandle = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aWindowHandle = AccessibleWrap::GetHWNDFor(acc); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_indexInParent(long* aIndexInParent) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIndexInParent) + return E_INVALIDARG; + *aIndexInParent = -1; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + MOZ_ASSERT(!acc->IsProxy()); + *aIndexInParent = acc->IndexInParent(); + + if (*aIndexInParent == -1) + return S_FALSE; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_locale(IA2Locale* aLocale) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocale) + return E_INVALIDARG; + + // Language codes consist of a primary code and a possibly empty series of + // subcodes: language-code = primary-code ( "-" subcode )* + // Two-letter primary codes are reserved for [ISO639] language abbreviations. + // Any two-letter subcode is understood to be a [ISO3166] country code. + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString lang; + acc->Language(lang); + + // If primary code consists from two letters then expose it as language. + int32_t offset = lang.FindChar('-', 0); + if (offset == -1) { + if (lang.Length() == 2) { + aLocale->language = ::SysAllocString(lang.get()); + return S_OK; + } + } else if (offset == 2) { + aLocale->language = ::SysAllocStringLen(lang.get(), 2); + + // If the first subcode consists from two letters then expose it as + // country. + offset = lang.FindChar('-', 3); + if (offset == -1) { + if (lang.Length() == 5) { + aLocale->country = ::SysAllocString(lang.get() + 3); + return S_OK; + } + } else if (offset == 5) { + aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2); + } + } + + // Expose as a string if primary code or subcode cannot point to language or + // country abbreviations or if there are more than one subcode. + aLocale->variant = ::SysAllocString(lang.get()); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_attributes(BSTR* aAttributes) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAttributes) + return E_INVALIDARG; + *aAttributes = nullptr; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + // The format is name:value;name:value; with \ for escaping these + // characters ":;=,\". + if (!acc->IsProxy()) { + nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes(); + return ConvertToIA2Attributes(attributes, aAttributes); + } + + MOZ_ASSERT(!acc->IsProxy()); + return E_UNEXPECTED; + + A11Y_TRYBLOCK_END +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessible2_2 + +STDMETHODIMP +ia2Accessible::get_attribute(BSTR name, VARIANT* aAttribute) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAttribute) + return E_INVALIDARG; + + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible, + long* aCaretOffset) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccessible || !aCaretOffset) + return E_INVALIDARG; + + *aAccessible = nullptr; + *aCaretOffset = -1; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + int32_t caretOffset = -1; + Accessible* accWithCaret = SelectionMgr()->AccessibleWithCaret(&caretOffset); + if (acc->Document() != accWithCaret->Document()) + return S_FALSE; + + Accessible* child = accWithCaret; + while (!child->IsDoc() && child != acc) + child = child->Parent(); + + if (child != acc) + return S_FALSE; + + *aAccessible = static_cast<IAccessible2*>( + static_cast<AccessibleWrap*>(accWithCaret)); + (*aAccessible)->AddRef(); + *aCaretOffset = caretOffset; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_relationTargetsOfType(BSTR aType, + long aMaxTargets, + IUnknown*** aTargets, + long* aNTargets) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aTargets || !aNTargets || aMaxTargets < 0) + return E_INVALIDARG; + *aNTargets = 0; + + Maybe<RelationType> relationType; + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { + if (wcscmp(aType, sRelationTypePairs[idx].second) == 0) { + relationType.emplace(sRelationTypePairs[idx].first); + break; + } + } + if (!relationType) + return E_INVALIDARG; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsTArray<Accessible*> targets; + MOZ_ASSERT(!acc->IsProxy()); + Relation rel = acc->RelationByType(*relationType); + Accessible* target = nullptr; + while ((target = rel.Next()) && + static_cast<long>(targets.Length()) <= aMaxTargets) { + targets.AppendElement(target); + } + + *aNTargets = targets.Length(); + *aTargets = static_cast<IUnknown**>( + ::CoTaskMemAlloc(sizeof(IUnknown*) * *aNTargets)); + if (!*aTargets) + return E_OUTOFMEMORY; + + for (int32_t i = 0; i < *aNTargets; i++) { + AccessibleWrap* target= static_cast<AccessibleWrap*>(targets[i]); + (*aTargets)[i] = static_cast<IAccessible2*>(target); + (*aTargets)[i]->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2Accessible::get_selectionRanges(IA2Range** aRanges, + long *aNRanges) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRanges || !aNRanges) + return E_INVALIDARG; + + *aNRanges = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<TextRange, 1> ranges; + acc->Document()->SelectionRanges(&ranges); + uint32_t len = ranges.Length(); + for (uint32_t idx = 0; idx < len; idx++) { + if (!ranges[idx].Crop(acc)) { + ranges.RemoveElementAt(idx); + } + } + + *aNRanges = ranges.Length(); + *aRanges = static_cast<IA2Range*>( + ::CoTaskMemAlloc(sizeof(IA2Range) * *aNRanges)); + if (!*aRanges) + return E_OUTOFMEMORY; + + for (uint32_t idx = 0; idx < static_cast<uint32_t>(*aNRanges); idx++) { + AccessibleWrap* anchor = + static_cast<AccessibleWrap*>(ranges[idx].StartContainer()); + (*aRanges)[idx].anchor = static_cast<IAccessible2*>(anchor); + anchor->AddRef(); + + (*aRanges)[idx].anchorOffset = ranges[idx].StartOffset(); + + AccessibleWrap* active = + static_cast<AccessibleWrap*>(ranges[idx].EndContainer()); + (*aRanges)[idx].active = static_cast<IAccessible2*>(active); + active->AddRef(); + + (*aRanges)[idx].activeOffset = ranges[idx].EndOffset(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + + +//////////////////////////////////////////////////////////////////////////////// +// Helpers + +template<typename String> +static inline void +EscapeAttributeChars(String& aStr) +{ + int32_t offset = 0; + static const char kCharsToEscape[] = ":;=,\\"; + while ((offset = aStr.FindCharInSet(kCharsToEscape, offset)) != kNotFound) { + aStr.Insert('\\', offset); + offset += 2; + } +} + +HRESULT +ia2Accessible::ConvertToIA2Attributes(nsTArray<Attribute>* aAttributes, + BSTR* aIA2Attributes) +{ + nsString attrStr; + size_t attrCount = aAttributes->Length(); + for (size_t i = 0; i < attrCount; i++) { + EscapeAttributeChars(aAttributes->ElementAt(i).Name()); + EscapeAttributeChars(aAttributes->ElementAt(i).Value()); + AppendUTF8toUTF16(aAttributes->ElementAt(i).Name(), attrStr); + attrStr.Append(':'); + attrStr.Append(aAttributes->ElementAt(i).Value()); + attrStr.Append(';'); + } + + if (attrStr.IsEmpty()) + return S_FALSE; + + *aIA2Attributes = ::SysAllocStringLen(attrStr.get(), attrStr.Length()); + return *aIA2Attributes ? S_OK : E_OUTOFMEMORY; +} + +HRESULT +ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, + BSTR* aIA2Attributes) +{ + *aIA2Attributes = nullptr; + + // The format is name:value;name:value; with \ for escaping these + // characters ":;=,\". + + if (!aAttributes) + return S_FALSE; + + nsCOMPtr<nsISimpleEnumerator> propEnum; + aAttributes->Enumerate(getter_AddRefs(propEnum)); + if (!propEnum) + return E_FAIL; + + nsAutoString strAttrs; + + bool hasMore = false; + while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> propSupports; + propEnum->GetNext(getter_AddRefs(propSupports)); + + nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports)); + if (!propElem) + return E_FAIL; + + nsAutoCString name; + if (NS_FAILED(propElem->GetKey(name))) + return E_FAIL; + + EscapeAttributeChars(name); + + nsAutoString value; + if (NS_FAILED(propElem->GetValue(value))) + return E_FAIL; + + EscapeAttributeChars(value); + + AppendUTF8toUTF16(name, strAttrs); + strAttrs.Append(':'); + strAttrs.Append(value); + strAttrs.Append(';'); + } + + if (strAttrs.IsEmpty()) + return S_FALSE; + + *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length()); + return *aIA2Attributes ? S_OK : E_OUTOFMEMORY; +} diff --git a/accessible/windows/ia2/ia2Accessible.h b/accessible/windows/ia2/ia2Accessible.h new file mode 100644 index 000000000..98ca4339b --- /dev/null +++ b/accessible/windows/ia2/ia2Accessible.h @@ -0,0 +1,122 @@ +/* -*- 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_ia2Accessible_h_ +#define mozilla_a11y_ia2Accessible_h_ + +#include "nsISupports.h" + +#include "Accessible2_3.h" + +namespace mozilla { +namespace a11y { +class Attribute; + +class ia2Accessible : public IAccessible2_3 +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessible2 + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRelations( + /* [retval][out] */ long* nRelations); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relation( + /* [in] */ long relationIndex, + /* [retval][out] */ IAccessibleRelation** relation); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relations( + /* [in] */ long maxRelations, + /* [length_is][size_is][out] */ IAccessibleRelation** relation, + /* [retval][out] */ long* nRelations); + + virtual HRESULT STDMETHODCALLTYPE role( + /* [retval][out] */ long* role); + + virtual HRESULT STDMETHODCALLTYPE scrollTo( + /* [in] */ enum IA2ScrollType scrollType); + + virtual HRESULT STDMETHODCALLTYPE scrollToPoint( + /* [in] */ enum IA2CoordinateType coordinateType, + /* [in] */ long x, + /* [in] */ long y); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_groupPosition( + /* [out] */ long* groupLevel, + /* [out] */ long* similarItemsInGroup, + /* [retval][out] */ long* positionInGroup); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_states( + /* [retval][out] */ AccessibleStates* states); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_extendedRole( + /* [retval][out] */ BSTR* extendedRole); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedExtendedRole( + /* [retval][out] */ BSTR* localizedExtendedRole); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nExtendedStates( + /* [retval][out] */ long* nExtendedStates); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_extendedStates( + /* [in] */ long maxExtendedStates, + /* [length_is][length_is][size_is][size_is][out] */ BSTR** extendedStates, + /* [retval][out] */ long* nExtendedStates); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedExtendedStates( + /* [in] */ long maxLocalizedExtendedStates, + /* [length_is][length_is][size_is][size_is][out] */ BSTR** localizedExtendedStates, + /* [retval][out] */ long* nLocalizedExtendedStates); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_uniqueID( + /* [retval][out] */ long* uniqueID); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_windowHandle( + /* [retval][out] */ HWND* windowHandle); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_indexInParent( + /* [retval][out] */ long* indexInParent); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_locale( + /* [retval][out] */ IA2Locale* locale); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attributes( + /* [retval][out] */ BSTR* attributes); + + // IAccessible2_2 + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attribute( + /* [in] */ BSTR name, + /* [out, retval] */ VARIANT* attribute); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_accessibleWithCaret( + /* [out] */ IUnknown** accessible, + /* [out, retval] */ long* caretOffset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationTargetsOfType( + /* [in] */ BSTR type, + /* [in] */ long maxTargets, + /* [out, size_is(,*nTargets)] */ IUnknown*** targets, + /* [out, retval] */ long* nTargets + ); + + // IAccessible2_3 + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectionRanges( + /* [out, size_is(,*nRanges)] */ IA2Range** ranges, + /* [out, retval] */ long *nRanges); + + // Helper method + static HRESULT ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, + BSTR* aIA2Attributes); + static HRESULT ConvertToIA2Attributes(nsTArray<Attribute>* aAttributes, + BSTR* aIA2Attributes); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleAction.cpp b/accessible/windows/ia2/ia2AccessibleAction.cpp new file mode 100644 index 000000000..0710b753f --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleAction.cpp @@ -0,0 +1,188 @@ +/* -*- 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 "ia2AccessibleAction.h" + +#include "AccessibleAction_i.c" + +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleAction == iid && + !static_cast<AccessibleWrap*>(this)->IsProxy()) { + *ppv = static_cast<IAccessibleAction*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +// IAccessibleAction + +STDMETHODIMP +ia2AccessibleAction::nActions(long* aActionCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aActionCount) + return E_INVALIDARG; + + *aActionCount = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aActionCount = acc->ActionCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleAction::doAction(long aActionIndex) +{ + A11Y_TRYBLOCK_BEGIN + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + uint8_t index = static_cast<uint8_t>(aActionIndex); + return acc->DoAction(index) ? S_OK : E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleAction::get_description(long aActionIndex, BSTR *aDescription) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aDescription) + return E_INVALIDARG; + *aDescription = nullptr; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString description; + uint8_t index = static_cast<uint8_t>(aActionIndex); + acc->ActionDescriptionAt(index, description); + if (description.IsEmpty()) + return S_FALSE; + + *aDescription = ::SysAllocStringLen(description.get(), + description.Length()); + return *aDescription ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding, + BSTR **aKeyBinding, + long *aNumBinding) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aKeyBinding) + return E_INVALIDARG; + *aKeyBinding = nullptr; + + if (!aNumBinding) + return E_INVALIDARG; + *aNumBinding = 0; + + if (aActionIndex != 0 || aNumMaxBinding < 1) + return E_INVALIDARG; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + // Expose keyboard shortcut if it's not exposed via MSAA keyboard shortcut. + KeyBinding keyBinding = acc->AccessKey(); + if (keyBinding.IsEmpty()) + return S_FALSE; + + keyBinding = acc->KeyboardShortcut(); + if (keyBinding.IsEmpty()) + return S_FALSE; + + nsAutoString keyStr; + keyBinding.ToString(keyStr); + + *aKeyBinding = static_cast<BSTR*>(::CoTaskMemAlloc(sizeof(BSTR*))); + if (!*aKeyBinding) + return E_OUTOFMEMORY; + + *(aKeyBinding[0]) = ::SysAllocStringLen(keyStr.get(), keyStr.Length()); + if (!*(aKeyBinding[0])) { + ::CoTaskMemFree(*aKeyBinding); + return E_OUTOFMEMORY; + } + + *aNumBinding = 1; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleAction::get_name(long aActionIndex, BSTR *aName) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aName) + return E_INVALIDARG; + + *aName = nullptr; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString name; + uint8_t index = static_cast<uint8_t>(aActionIndex); + acc->ActionNameAt(index, name); + if (name.IsEmpty()) + return E_INVALIDARG; + + *aName = ::SysAllocStringLen(name.get(), name.Length()); + return *aName ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleAction::get_localizedName(long aActionIndex, BSTR *aLocalizedName) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocalizedName) + return E_INVALIDARG; + + *aLocalizedName = nullptr; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/ia2/ia2AccessibleAction.h b/accessible/windows/ia2/ia2AccessibleAction.h new file mode 100644 index 000000000..6c978ff16 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleAction.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 _ACCESSIBLE_ACTION_H +#define _ACCESSIBLE_ACTION_H + +#include "nsISupports.h" + +#include "AccessibleAction.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleAction: public IAccessibleAction +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleAction + virtual HRESULT STDMETHODCALLTYPE nActions( + /* [retval][out] */ long *nActions); + + virtual HRESULT STDMETHODCALLTYPE doAction( + /* [in] */ long actionIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_description( + /* [in] */ long actionIndex, + /* [retval][out] */ BSTR *description); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_keyBinding( + /* [in] */ long actionIndex, + /* [in] */ long nMaxBinding, + /* [length_is][length_is][size_is][size_is][out] */ BSTR **keyBinding, + /* [retval][out] */ long *nBinding); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_name( + /* [in] */ long actionIndex, + /* [retval][out] */ BSTR *name); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedName( + /* [in] */ long actionIndex, + /* [retval][out] */ BSTR *localizedName); + +}; + +} // namespace a11y +} // namespace mozilla + +#define FORWARD_IACCESSIBLEACTION(Class) \ +virtual HRESULT STDMETHODCALLTYPE nActions(long *nActions) \ +{ \ + return Class::nActions(nActions); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE doAction(long actionIndex) \ +{ \ + return Class::doAction(actionIndex); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_description(long actionIndex, \ + BSTR *description) \ +{ \ + return Class::get_description(actionIndex, description); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_keyBinding(long actionIndex, \ + long nMaxBinding, \ + BSTR **keyBinding, \ + long *nBinding) \ +{ \ + return Class::get_keyBinding(actionIndex, nMaxBinding, keyBinding, nBinding);\ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_name(long actionIndex, BSTR *name) \ +{ \ + return Class::get_name(actionIndex, name); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_localizedName(long actionIndex, \ + BSTR *localizedName) \ +{ \ + return Class::get_localizedName(actionIndex, localizedName); \ +} \ + \ + +#endif + diff --git a/accessible/windows/ia2/ia2AccessibleComponent.cpp b/accessible/windows/ia2/ia2AccessibleComponent.cpp new file mode 100644 index 000000000..f32c09d1e --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleComponent.cpp @@ -0,0 +1,127 @@ +/* -*- 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 "ia2AccessibleComponent.h" + +#include "AccessibleComponent_i.c" + +#include "AccessibleWrap.h" +#include "States.h" +#include "IUnknownImpl.h" + +#include "nsIFrame.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleComponent::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleComponent == iid) { + *ppv = static_cast<IAccessibleComponent*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +// IAccessibleComponent + +STDMETHODIMP +ia2AccessibleComponent::get_locationInParent(long* aX, long* aY) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aX || !aY) + return E_INVALIDARG; + + *aX = 0; + *aY = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + // If the object is not on any screen the returned position is (0,0). + uint64_t state = acc->State(); + if (state & states::INVISIBLE) + return S_OK; + + nsIntRect rect = acc->Bounds(); + + // The coordinates of the returned position are relative to this object's + // parent or relative to the screen on which this object is rendered if it + // has no parent. + if (!acc->Parent()) { + *aX = rect.x; + *aY = rect.y; + return S_OK; + } + + // The coordinates of the bounding box are given relative to the parent's + // coordinate system. + nsIntRect parentRect = acc->Parent()->Bounds(); + *aX = rect.x - parentRect.x; + *aY = rect.y - parentRect.y; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleComponent::get_foreground(IA2Color* aForeground) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aForeground) + return E_INVALIDARG; + + *aForeground = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsIFrame* frame = acc->GetFrame(); + if (frame) + *aForeground = frame->StyleColor()->mColor; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleComponent::get_background(IA2Color* aBackground) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aBackground) + return E_INVALIDARG; + + *aBackground = 0; + + AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsIFrame* frame = acc->GetFrame(); + if (frame) + *aBackground = frame->StyleBackground()->mBackgroundColor; + + return S_OK; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/ia2/ia2AccessibleComponent.h b/accessible/windows/ia2/ia2AccessibleComponent.h new file mode 100644 index 000000000..c14d7a113 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleComponent.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 IA2_ACCESSIBLE_COMPONENT_H_ +#define IA2_ACCESSIBLE_COMPONENT_H_ + +#include "AccessibleComponent.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleComponent : public IAccessibleComponent +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleComponent + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_locationInParent( + /* [out] */ long *x, + /* [retval][out] */ long *y); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_foreground( + /* [retval][out] */ IA2Color *foreground); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_background( + /* [retval][out] */ IA2Color *background); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleEditableText.cpp b/accessible/windows/ia2/ia2AccessibleEditableText.cpp new file mode 100644 index 000000000..361d6a130 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleEditableText.cpp @@ -0,0 +1,153 @@ +/* -*- 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 "ia2AccessibleEditableText.h" + +#include "AccessibleEditableText_i.c" +#include "HyperTextAccessible-inl.h" +#include "HyperTextAccessibleWrap.h" +#include "ProxyWrappers.h" + +#include "nsCOMPtr.h" +#include "nsString.h" + +using namespace mozilla::a11y; + +// IAccessibleEditableText + +STDMETHODIMP +ia2AccessibleEditableText::copyText(long aStartOffset, long aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) + return E_INVALIDARG; + + textAcc->CopyText(aStartOffset, aEndOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::deleteText(long aStartOffset, long aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) + return E_INVALIDARG; + + textAcc->DeleteText(aStartOffset, aEndOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::insertText(long aOffset, BSTR *aText) +{ + A11Y_TRYBLOCK_BEGIN + + uint32_t length = ::SysStringLen(*aText); + nsAutoString text(*aText, length); + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + textAcc->InsertText(text, aOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::cutText(long aStartOffset, long aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) + return E_INVALIDARG; + + textAcc->CutText(aStartOffset, aEndOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::pasteText(long aOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + textAcc->PasteText(aOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::replaceText(long aStartOffset, long aEndOffset, + BSTR *aText) +{ + A11Y_TRYBLOCK_BEGIN + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) + return E_INVALIDARG; + + textAcc->DeleteText(aStartOffset, aEndOffset); + + uint32_t length = ::SysStringLen(*aText); + nsAutoString text(*aText, length); + textAcc->InsertText(text, aStartOffset); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleEditableText::setAttributes(long aStartOffset, long aEndOffset, + BSTR *aAttributes) +{ + return E_NOTIMPL; +} diff --git a/accessible/windows/ia2/ia2AccessibleEditableText.h b/accessible/windows/ia2/ia2AccessibleEditableText.h new file mode 100644 index 000000000..a1501ca51 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleEditableText.h @@ -0,0 +1,56 @@ +/* -*- 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 _ACCESSIBLE_EDITABLETEXT_H +#define _ACCESSIBLE_EDITABLETEXT_H + +#include "nsISupports.h" + +#include "AccessibleEditableText.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleEditableText: public IAccessibleEditableText +{ +public: + + // IAccessibleEditableText + virtual HRESULT STDMETHODCALLTYPE copyText( + /* [in] */ long startOffset, + /* [in] */ long endOffset); + + virtual HRESULT STDMETHODCALLTYPE deleteText( + /* [in] */ long startOffset, + /* [in] */ long endOffset); + + virtual HRESULT STDMETHODCALLTYPE insertText( + /* [in] */ long offset, + /* [in] */ BSTR *text); + + virtual HRESULT STDMETHODCALLTYPE cutText( + /* [in] */ long startOffset, + /* [in] */ long endOffset); + + virtual HRESULT STDMETHODCALLTYPE pasteText( + /* [in] */ long offset); + + virtual HRESULT STDMETHODCALLTYPE replaceText( + /* [in] */ long startOffset, + /* [in] */ long endOffset, + /* [in] */ BSTR *text); + + virtual HRESULT STDMETHODCALLTYPE setAttributes( + /* [in] */ long startOffset, + /* [in] */ long endOffset, + /* [in] */ BSTR *attributes); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp new file mode 100644 index 000000000..c6d564103 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp @@ -0,0 +1,205 @@ +/* -*- 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 "Accessible2.h" +#include "AccessibleHyperlink.h" +#include "AccessibleHyperlink_i.c" + +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" +#include "nsIURI.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleHyperlink::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleHyperlink == iid) { + auto accWrap = static_cast<AccessibleWrap*>(this); + if (accWrap->IsProxy() ? + !(accWrap->ProxyInterfaces() & Interfaces::HYPERLINK) : + !accWrap->IsLink()) + return E_NOINTERFACE; + + *ppv = static_cast<IAccessibleHyperlink*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return ia2AccessibleAction::QueryInterface(iid, ppv); +} + +// IAccessibleHyperlink + +STDMETHODIMP +ia2AccessibleHyperlink::get_anchor(long aIndex, VARIANT* aAnchor) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAnchor) + return E_INVALIDARG; + + VariantInit(aAnchor); + + Accessible* thisObj = static_cast<AccessibleWrap*>(this); + MOZ_ASSERT(!thisObj->IsProxy()); + + if (thisObj->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount())) + return E_INVALIDARG; + + if (!thisObj->IsLink()) + return S_FALSE; + + AccessibleWrap* anchor = + static_cast<AccessibleWrap*>(thisObj->AnchorAt(aIndex)); + if (!anchor) + return S_FALSE; + + void* instancePtr = nullptr; + HRESULT result = anchor->QueryInterface(IID_IUnknown, &instancePtr); + if (FAILED(result)) + return result; + + aAnchor->punkVal = static_cast<IUnknown*>(instancePtr); + aAnchor->vt = VT_UNKNOWN; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHyperlink::get_anchorTarget(long aIndex, VARIANT* aAnchorTarget) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAnchorTarget) { + return E_INVALIDARG; + } + + VariantInit(aAnchorTarget); + + Accessible* thisObj = static_cast<AccessibleWrap*>(this); + nsAutoCString uriStr; + MOZ_ASSERT(!thisObj->IsProxy()); + if (thisObj->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount())) { + return E_INVALIDARG; + } + + if (!thisObj->IsLink()) { + return S_FALSE; + } + + nsCOMPtr<nsIURI> uri = thisObj->AnchorURIAt(aIndex); + if (!uri) { + return S_FALSE; + } + + nsresult rv = uri->GetSpec(uriStr); + if (NS_FAILED(rv)) { + return GetHRESULT(rv); + } + + nsAutoString stringURI; + AppendUTF8toUTF16(uriStr, stringURI); + + aAnchorTarget->vt = VT_BSTR; + aAnchorTarget->bstrVal = ::SysAllocStringLen(stringURI.get(), + stringURI.Length()); + return aAnchorTarget->bstrVal ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHyperlink::get_startIndex(long* aIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIndex) + return E_INVALIDARG; + + *aIndex = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + Accessible* thisObj = static_cast<AccessibleWrap*>(this); + if (thisObj->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!thisObj->IsLink()) + return S_FALSE; + + *aIndex = thisObj->StartOffset(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHyperlink::get_endIndex(long* aIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIndex) + return E_INVALIDARG; + + *aIndex = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + Accessible* thisObj = static_cast<AccessibleWrap*>(this); + if (thisObj->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!thisObj->IsLink()) + return S_FALSE; + + *aIndex = thisObj->EndOffset(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHyperlink::get_valid(boolean* aValid) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aValid) + return E_INVALIDARG; + + *aValid = false; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + Accessible* thisObj = static_cast<AccessibleWrap*>(this); + if (thisObj->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!thisObj->IsLink()) + return S_FALSE; + + *aValid = thisObj->IsLinkValid(); + return S_OK; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/ia2/ia2AccessibleHyperlink.h b/accessible/windows/ia2/ia2AccessibleHyperlink.h new file mode 100644 index 000000000..dc34f5bc9 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleHyperlink.h @@ -0,0 +1,51 @@ +/* -*- 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 _ACCESSIBLE_HYPERLINK_H +#define _ACCESSIBLE_HYPERLINK_H + +#include "nsISupports.h" + +#include "ia2AccessibleAction.h" +#include "AccessibleHyperlink.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleHyperlink : public ia2AccessibleAction, + public IAccessibleHyperlink +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleAction + FORWARD_IACCESSIBLEACTION(ia2AccessibleAction) + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_anchor( + /* [in] */ long index, + /* [retval][out] */ VARIANT *anchor); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_anchorTarget( + /* [in] */ long index, + /* [retval][out] */ VARIANT *anchorTarget); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_startIndex( + /* [retval][out] */ long *index); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_endIndex( + /* [retval][out] */ long *index); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_valid( + /* [retval][out] */ boolean *valid); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleHypertext.cpp b/accessible/windows/ia2/ia2AccessibleHypertext.cpp new file mode 100644 index 000000000..f4b3bdaf2 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleHypertext.cpp @@ -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/. */ + +#include "ia2AccessibleHypertext.h" + +#include "AccessibleHypertext_i.c" + +#include "HyperTextAccessibleWrap.h" +#include "IUnknownImpl.h" + +using namespace mozilla::a11y; + +// IAccessibleHypertext + +STDMETHODIMP +ia2AccessibleHypertext::get_nHyperlinks(long* aHyperlinkCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aHyperlinkCount) + return E_INVALIDARG; + + *aHyperlinkCount = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessibleWrap* hyperText = static_cast<HyperTextAccessibleWrap*>(this); + if (hyperText->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aHyperlinkCount = hyperText->LinkCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHypertext::get_hyperlink(long aLinkIndex, + IAccessibleHyperlink** aHyperlink) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aHyperlink) + return E_INVALIDARG; + + *aHyperlink = nullptr; + + AccessibleWrap* hyperLink; + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessibleWrap* hyperText = static_cast<HyperTextAccessibleWrap*>(this); + if (hyperText->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + hyperLink = static_cast<AccessibleWrap*>(hyperText->LinkAt(aLinkIndex)); + + if (!hyperLink) + return E_FAIL; + + *aHyperlink = + static_cast<IAccessibleHyperlink*>(hyperLink); + (*aHyperlink)->AddRef(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleHypertext::get_hyperlinkIndex(long aCharIndex, long* aHyperlinkIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aHyperlinkIndex) + return E_INVALIDARG; + + *aHyperlinkIndex = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessibleWrap* hyperAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (hyperAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aHyperlinkIndex = hyperAcc->LinkIndexAtOffset(aCharIndex); + return S_OK; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/ia2/ia2AccessibleHypertext.h b/accessible/windows/ia2/ia2AccessibleHypertext.h new file mode 100644 index 000000000..af5c209b4 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleHypertext.h @@ -0,0 +1,43 @@ +/* -*- 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 _ACCESSIBLE_HYPERTEXT_H +#define _ACCESSIBLE_HYPERTEXT_H + +#include "nsISupports.h" + +#include "ia2AccessibleText.h" +#include "AccessibleHypertext.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleHypertext : public ia2AccessibleText, + public IAccessibleHypertext +{ +public: + + // IAccessibleText + FORWARD_IACCESSIBLETEXT(ia2AccessibleText) + + // IAccessibleHypertext + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nHyperlinks( + /* [retval][out] */ long* hyperlinkCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlink( + /* [in] */ long index, + /* [retval][out] */ IAccessibleHyperlink** hyperlink); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_hyperlinkIndex( + /* [in] */ long charIndex, + /* [retval][out] */ long* hyperlinkIndex); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleImage.cpp b/accessible/windows/ia2/ia2AccessibleImage.cpp new file mode 100644 index 000000000..989fde925 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleImage.cpp @@ -0,0 +1,119 @@ +/* -*- 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 "ia2AccessibleImage.h" + +#include "AccessibleImage_i.c" + +#include "ImageAccessibleWrap.h" +#include "IUnknownImpl.h" +#include "nsIAccessibleTypes.h" + +#include "nsString.h" +#include "nsIURI.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleImage::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleImage == iid) { + *ppv = static_cast<IAccessibleImage*>(this); + (static_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +// IAccessibleImage + +STDMETHODIMP +ia2AccessibleImage::get_description(BSTR* aDescription) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aDescription) + return E_INVALIDARG; + + *aDescription = nullptr; + + ImageAccessibleWrap* acc = static_cast<ImageAccessibleWrap*>(this); + if (acc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString description; + acc->Name(description); + if (description.IsEmpty()) + return S_FALSE; + + *aDescription = ::SysAllocStringLen(description.get(), description.Length()); + return *aDescription ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleImage::get_imagePosition(enum IA2CoordinateType aCoordType, + long* aX, + long* aY) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aX || !aY) + return E_INVALIDARG; + + *aX = 0; + *aY = 0; + + ImageAccessibleWrap* imageAcc = static_cast<ImageAccessibleWrap*>(this); + if (imageAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : + nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + nsIntPoint pos = imageAcc->Position(geckoCoordType); + *aX = pos.x; + *aY = pos.y; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleImage::get_imageSize(long* aHeight, long* aWidth) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aHeight || !aWidth) + return E_INVALIDARG; + + *aHeight = 0; + *aWidth = 0; + + ImageAccessibleWrap* imageAcc = static_cast<ImageAccessibleWrap*>(this); + if (imageAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsIntSize size = imageAcc->Size(); + *aHeight = size.width; + *aWidth = size.height; + return S_OK; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/ia2/ia2AccessibleImage.h b/accessible/windows/ia2/ia2AccessibleImage.h new file mode 100644 index 000000000..d065cf34c --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleImage.h @@ -0,0 +1,40 @@ +/* -*- 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 _ACCESSIBLE_IMAGE_H +#define _ACCESSIBLE_IMAGE_H + +#include "AccessibleImage.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleImage : public IAccessibleImage +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleImage + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_description( + /* [retval][out] */ BSTR *description); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_imagePosition( + /* [in] */ enum IA2CoordinateType coordinateType, + /* [out] */ long *x, + /* [retval][out] */ long *y); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_imageSize( + /* [out] */ long *height, + /* [retval][out] */ long *width); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleRelation.cpp b/accessible/windows/ia2/ia2AccessibleRelation.cpp new file mode 100644 index 000000000..cc6fd83c8 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleRelation.cpp @@ -0,0 +1,125 @@ +/* -*- 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 "ia2AccessibleRelation.h" + +#include "Relation.h" +#include "nsID.h" + +#include "AccessibleRelation_i.c" + +using namespace mozilla::a11y; + +ia2AccessibleRelation::ia2AccessibleRelation(RelationType aType, Relation* aRel) : + mType(aType) +{ + Accessible* target = nullptr; + while ((target = aRel->Next())) + mTargets.AppendElement(target); +} + +// IUnknown + +IMPL_IUNKNOWN_QUERY_HEAD(ia2AccessibleRelation) + IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleRelation) + IMPL_IUNKNOWN_QUERY_IFACE(IUnknown) +IMPL_IUNKNOWN_QUERY_TAIL + +// IAccessibleRelation + +STDMETHODIMP +ia2AccessibleRelation::get_relationType(BSTR* aRelationType) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRelationType) + return E_INVALIDARG; + + *aRelationType = nullptr; + +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ + case RelationType::geckoType: \ + *aRelationType = ::SysAllocString(ia2Type); \ + break; + + switch (mType) { +#include "RelationTypeMap.h" + } + + return *aRelationType ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleRelation::get_localizedRelationType(BSTR *aLocalizedRelationType) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocalizedRelationType) + return E_INVALIDARG; + + *aLocalizedRelationType = nullptr; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleRelation::get_nTargets(long *aNTargets) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNTargets) + return E_INVALIDARG; + + *aNTargets = mTargets.Length(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleRelation::get_target(long aTargetIndex, IUnknown **aTarget) +{ + A11Y_TRYBLOCK_BEGIN + + if (aTargetIndex < 0 || (uint32_t)aTargetIndex >= mTargets.Length() || !aTarget) + return E_INVALIDARG; + + AccessibleWrap* target = + static_cast<AccessibleWrap*>(mTargets[aTargetIndex].get()); + *aTarget = static_cast<IAccessible*>(target); + (*aTarget)->AddRef(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleRelation::get_targets(long aMaxTargets, IUnknown **aTargets, + long *aNTargets) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNTargets || !aTargets) + return E_INVALIDARG; + + *aNTargets = 0; + long maxTargets = mTargets.Length(); + if (maxTargets > aMaxTargets) + maxTargets = aMaxTargets; + + for (long idx = 0; idx < maxTargets; idx++) + get_target(idx, aTargets + idx); + + *aNTargets = maxTargets; + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/ia2/ia2AccessibleRelation.h b/accessible/windows/ia2/ia2AccessibleRelation.h new file mode 100644 index 000000000..faaa31cb0 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleRelation.h @@ -0,0 +1,85 @@ +/* -*- 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 _NS_ACCESSIBLE_RELATION_WRAP_H +#define _NS_ACCESSIBLE_RELATION_WRAP_H + +#include "Accessible.h" +#include "IUnknownImpl.h" + +#include <utility> +#include "nsTArray.h" + +#include "AccessibleRelation.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleRelation final : public IAccessibleRelation +{ +public: + ia2AccessibleRelation(RelationType aType, Relation* aRel); + + ia2AccessibleRelation(RelationType aType, + nsTArray<RefPtr<Accessible>>&& aTargets) : + mType(aType), mTargets(Move(aTargets)) {} + + // IUnknown + DECL_IUNKNOWN + + // IAccessibleRelation + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_relationType( + /* [retval][out] */ BSTR *relationType); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_localizedRelationType( + /* [retval][out] */ BSTR *localizedRelationType); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nTargets( + /* [retval][out] */ long *nTargets); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_target( + /* [in] */ long targetIndex, + /* [retval][out] */ IUnknown **target); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_targets( + /* [in] */ long maxTargets, + /* [length_is][size_is][out] */ IUnknown **target, + /* [retval][out] */ long *nTargets); + + inline bool HasTargets() const + { return mTargets.Length(); } + +private: + ia2AccessibleRelation(); + ia2AccessibleRelation(const ia2AccessibleRelation&); + ia2AccessibleRelation& operator = (const ia2AccessibleRelation&); + + RelationType mType; + nsTArray<RefPtr<Accessible> > mTargets; +}; + + +/** + * Gecko to IAccessible2 relation types map. + */ + +const WCHAR *const IA2_RELATION_NULL = L""; + +#define RELATIONTYPE(geckoType, name, atkType, msaaType, ia2Type) \ + std::pair<RelationType, const WCHAR *const>(RelationType::geckoType, ia2Type), + +static const std::pair<RelationType, const WCHAR *const> sRelationTypePairs[] = { +#include "RelationTypeMap.h" +}; + +#undef RELATIONTYPE + +} // namespace a11y +} // namespace mozilla + +#endif + diff --git a/accessible/windows/ia2/ia2AccessibleTable.cpp b/accessible/windows/ia2/ia2AccessibleTable.cpp new file mode 100644 index 000000000..6ac6bbab4 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleTable.cpp @@ -0,0 +1,747 @@ +/* -*- 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 "ia2AccessibleTable.h" + +#include "Accessible2.h" +#include "AccessibleTable_i.c" +#include "AccessibleTable2_i.c" + +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" +#include "Statistics.h" +#include "TableAccessible.h" + +#include "nsCOMPtr.h" +#include "nsString.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleTable::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleTable == iid) { + statistics::IAccessibleTableUsed(); + *ppv = static_cast<IAccessibleTable*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + if (IID_IAccessibleTable2 == iid) { + *ppv = static_cast<IAccessibleTable2*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessibleTable + +STDMETHODIMP +ia2AccessibleTable::get_accessibleAt(long aRowIdx, long aColIdx, + IUnknown** aAccessible) +{ + return get_cellAt(aRowIdx, aColIdx, aAccessible); +} + +STDMETHODIMP +ia2AccessibleTable::get_caption(IUnknown** aAccessible) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccessible) + return E_INVALIDARG; + + *aAccessible = nullptr; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AccessibleWrap* caption = static_cast<AccessibleWrap*>(mTable->Caption()); + if (!caption) + return S_FALSE; + + (*aAccessible = static_cast<IAccessible*>(caption))->AddRef(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_childIndex(long aRowIdx, long aColIdx, + long* aChildIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aChildIdx) + return E_INVALIDARG; + + *aChildIdx = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || aColIdx < 0 || + static_cast<uint32_t>(aRowIdx) >= mTable->RowCount() || + static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + *aChildIdx = mTable->CellIndexAt(aRowIdx, aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_columnDescription(long aColIdx, BSTR* aDescription) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aDescription) + return E_INVALIDARG; + + *aDescription = nullptr; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + nsAutoString descr; + mTable->ColDescription(aColIdx, descr); + if (descr.IsEmpty()) + return S_FALSE; + + *aDescription = ::SysAllocStringLen(descr.get(), descr.Length()); + return *aDescription ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_columnExtentAt(long aRowIdx, long aColIdx, + long* aSpan) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aSpan) + return E_INVALIDARG; + + *aSpan = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || aColIdx < 0 || + static_cast<uint32_t>(aRowIdx) >= mTable->RowCount() || + static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + *aSpan = mTable->ColExtentAt(aRowIdx, aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_columnHeader(IAccessibleTable** aAccessibleTable, + long* aStartingRowIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccessibleTable || !aStartingRowIndex) + return E_INVALIDARG; + + *aAccessibleTable = nullptr; + *aStartingRowIndex = -1; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_columnIndex(long aCellIdx, long* aColIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aColIdx) + return E_INVALIDARG; + + *aColIdx = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aCellIdx < 0 || + static_cast<uint32_t>(aCellIdx) >= mTable->ColCount() * mTable->RowCount()) + return E_INVALIDARG; + + *aColIdx = mTable->ColIndexAt(aCellIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_nColumns(long* aColCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aColCount) + return E_INVALIDARG; + + *aColCount = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + *aColCount = mTable->ColCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_nRows(long* aRowCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowCount) + return E_INVALIDARG; + + *aRowCount = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + *aRowCount = mTable->RowCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_nSelectedChildren(long* aChildCount) +{ + return get_nSelectedCells(aChildCount); +} + +STDMETHODIMP +ia2AccessibleTable::get_nSelectedColumns(long* aColCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aColCount) + return E_INVALIDARG; + + *aColCount = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + *aColCount = mTable->SelectedColCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_nSelectedRows(long* aRowCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowCount) + return E_INVALIDARG; + + *aRowCount = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + *aRowCount = mTable->SelectedRowCount(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_rowDescription(long aRowIdx, BSTR* aDescription) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aDescription) + return E_INVALIDARG; + + *aDescription = nullptr; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= mTable->RowCount()) + return E_INVALIDARG; + + nsAutoString descr; + mTable->RowDescription(aRowIdx, descr); + if (descr.IsEmpty()) + return S_FALSE; + + *aDescription = ::SysAllocStringLen(descr.get(), descr.Length()); + return *aDescription ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_rowExtentAt(long aRowIdx, long aColIdx, long* aSpan) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aSpan) + return E_INVALIDARG; + + *aSpan = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || aColIdx < 0 || + static_cast<uint32_t>(aRowIdx) >= mTable->RowCount() || + static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + *aSpan = mTable->RowExtentAt(aRowIdx, aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_rowHeader(IAccessibleTable** aAccessibleTable, + long* aStartingColumnIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccessibleTable || !aStartingColumnIndex) + return E_INVALIDARG; + + *aAccessibleTable = nullptr; + *aStartingColumnIndex = -1; + return E_NOTIMPL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_rowIndex(long aCellIdx, long* aRowIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowIdx) + return E_INVALIDARG; + + *aRowIdx = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aCellIdx < 0 || + static_cast<uint32_t>(aCellIdx) >= mTable->ColCount() * mTable->RowCount()) + return E_INVALIDARG; + + *aRowIdx = mTable->RowIndexAt(aCellIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedChildren(long aMaxChildren, long** aChildren, + long* aNChildren) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aChildren || !aNChildren) + return E_INVALIDARG; + + *aChildren = nullptr; + *aNChildren = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<uint32_t, 30> cellIndices; + mTable->SelectedCellIndices(&cellIndices); + + uint32_t maxCells = cellIndices.Length(); + if (maxCells == 0) + return S_FALSE; + + *aChildren = static_cast<LONG*>(moz_xmalloc(sizeof(LONG) * maxCells)); + *aNChildren = maxCells; + for (uint32_t i = 0; i < maxCells; i++) + (*aChildren)[i] = cellIndices[i]; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedColumns(long aMaxColumns, long** aColumns, + long* aNColumns) +{ + A11Y_TRYBLOCK_BEGIN + + return get_selectedColumns(aColumns, aNColumns); + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedRows(long aMaxRows, long** aRows, long* aNRows) +{ + A11Y_TRYBLOCK_BEGIN + + return get_selectedRows(aRows, aNRows); + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_summary(IUnknown** aAccessible) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccessible) + return E_INVALIDARG; + + // Neither html:table nor xul:tree nor ARIA grid/tree have an ability to + // link an accessible object to specify a summary. There is closes method + // in Table::summary to get a summary as a string which is not mapped + // directly to IAccessible2. + + *aAccessible = nullptr; + return S_FALSE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_isColumnSelected(long aColIdx, boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIsSelected) + return E_INVALIDARG; + + *aIsSelected = false; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + *aIsSelected = mTable->IsColSelected(aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_isRowSelected(long aRowIdx, boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIsSelected) + return E_INVALIDARG; + + *aIsSelected = false; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= mTable->RowCount()) + return E_INVALIDARG; + + *aIsSelected = mTable->IsRowSelected(aRowIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_isSelected(long aRowIdx, long aColIdx, + boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIsSelected) + return E_INVALIDARG; + + *aIsSelected = false; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || aColIdx < 0 || + static_cast<uint32_t>(aColIdx) >= mTable->ColCount() || + static_cast<uint32_t>(aRowIdx) >= mTable->RowCount()) + return E_INVALIDARG; + + *aIsSelected = mTable->IsCellSelected(aRowIdx, aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::selectRow(long aRowIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= mTable->RowCount()) + return E_INVALIDARG; + + mTable->SelectRow(aRowIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::selectColumn(long aColIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + mTable->SelectCol(aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::unselectRow(long aRowIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aRowIdx < 0 || static_cast<uint32_t>(aRowIdx) >= mTable->RowCount()) + return E_INVALIDARG; + + mTable->UnselectRow(aRowIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::unselectColumn(long aColIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aColIdx < 0 || static_cast<uint32_t>(aColIdx) >= mTable->ColCount()) + return E_INVALIDARG; + + mTable->UnselectCol(aColIdx); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_rowColumnExtentsAtIndex(long aCellIdx, long* aRowIdx, + long* aColIdx, + long* aRowExtents, + long* aColExtents, + boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowIdx || !aColIdx || !aRowExtents || !aColExtents || !aIsSelected) + return E_INVALIDARG; + + *aRowIdx = 0; + *aColIdx = 0; + *aRowExtents = 0; + *aColExtents = 0; + *aIsSelected = false; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + if (aCellIdx < 0 || + static_cast<uint32_t>(aCellIdx) >= mTable->ColCount() * mTable->RowCount()) + return E_INVALIDARG; + + int32_t colIdx = 0, rowIdx = 0; + mTable->RowAndColIndicesAt(aCellIdx, &rowIdx, &colIdx); + *aRowIdx = rowIdx; + *aColIdx = colIdx; + *aRowExtents = mTable->RowExtentAt(rowIdx, colIdx); + *aColExtents = mTable->ColExtentAt(rowIdx, colIdx); + *aIsSelected = mTable->IsCellSelected(rowIdx, colIdx); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_modelChange(IA2TableModelChange* aModelChange) +{ + return E_NOTIMPL; +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessibleTable2 + +STDMETHODIMP +ia2AccessibleTable::get_cellAt(long aRowIdx, long aColIdx, IUnknown** aCell) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCell) + return E_INVALIDARG; + + *aCell = nullptr; + + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AccessibleWrap* cell = + static_cast<AccessibleWrap*>(mTable->CellAt(aRowIdx, aColIdx)); + if (!cell) + return E_INVALIDARG; + + (*aCell = static_cast<IAccessible*>(cell))->AddRef(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_nSelectedCells(long* aCellCount) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCellCount) + return E_INVALIDARG; + + *aCellCount = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + *aCellCount = mTable->SelectedCellCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedCells(IUnknown*** aCells, long* aNSelectedCells) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCells || !aNSelectedCells) + return E_INVALIDARG; + + *aCells = nullptr; + *aNSelectedCells = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<Accessible*, 30> cells; + mTable->SelectedCells(&cells); + if (cells.IsEmpty()) + return S_FALSE; + + *aCells = + static_cast<IUnknown**>(::CoTaskMemAlloc(sizeof(IUnknown*) * + cells.Length())); + if (!*aCells) + return E_OUTOFMEMORY; + + for (uint32_t i = 0; i < cells.Length(); i++) { + (*aCells)[i] = + static_cast<IAccessible*>(static_cast<AccessibleWrap*>(cells[i])); + ((*aCells)[i])->AddRef(); + } + + *aNSelectedCells = cells.Length(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedColumns(long** aColumns, long* aNColumns) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aColumns || !aNColumns) + return E_INVALIDARG; + + *aColumns = nullptr; + *aNColumns = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<uint32_t, 30> colIndices; + mTable->SelectedColIndices(&colIndices); + + uint32_t maxCols = colIndices.Length(); + if (maxCols == 0) + return S_FALSE; + + *aColumns = static_cast<LONG*>(moz_xmalloc(sizeof(LONG) * maxCols)); + *aNColumns = maxCols; + for (uint32_t i = 0; i < maxCols; i++) + (*aColumns)[i] = colIndices[i]; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTable::get_selectedRows(long** aRows, long* aNRows) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRows || !aNRows) + return E_INVALIDARG; + + *aRows = nullptr; + *aNRows = 0; + if (!mTable) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<uint32_t, 30> rowIndices; + mTable->SelectedRowIndices(&rowIndices); + + uint32_t maxRows = rowIndices.Length(); + if (maxRows == 0) + return S_FALSE; + + *aRows = static_cast<LONG*>(moz_xmalloc(sizeof(LONG) * maxRows)); + *aNRows = maxRows; + for (uint32_t i = 0; i < maxRows; i++) + (*aRows)[i] = rowIndices[i]; + + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/ia2/ia2AccessibleTable.h b/accessible/windows/ia2/ia2AccessibleTable.h new file mode 100644 index 000000000..6738e2dfc --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleTable.h @@ -0,0 +1,176 @@ +/* -*- 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 _ACCESSIBLE_TABLE_H +#define _ACCESSIBLE_TABLE_H + +#include "AccessibleTable.h" +#include "AccessibleTable2.h" + +namespace mozilla { +namespace a11y { + +class TableAccessible; + +class ia2AccessibleTable : public IAccessibleTable, + public IAccessibleTable2 +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleTable + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_accessibleAt( + /* [in] */ long row, + /* [in] */ long column, + /* [retval][out] */ IUnknown **accessible); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_caption( + /* [retval][out] */ IUnknown **accessible); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_childIndex( + /* [in] */ long rowIndex, + /* [in] */ long columnIndex, + /* [retval][out] */ long *childIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnDescription( + /* [in] */ long column, + /* [retval][out] */ BSTR *description); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnExtentAt( + /* [in] */ long row, + /* [in] */ long column, + /* [retval][out] */ long *nColumnsSpanned); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnHeader( + /* [out] */ IAccessibleTable **accessibleTable, + /* [retval][out] */ long *startingRowIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnIndex( + /* [in] */ long childIndex, + /* [retval][out] */ long *columnIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nColumns( + /* [retval][out] */ long *columnCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nRows( + /* [retval][out] */ long *rowCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedChildren( + /* [retval][out] */ long *childCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedColumns( + /* [retval][out] */ long *columnCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedRows( + /* [retval][out] */ long *rowCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowDescription( + /* [in] */ long row, + /* [retval][out] */ BSTR *description); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowExtentAt( + /* [in] */ long row, + /* [in] */ long column, + /* [retval][out] */ long *nRowsSpanned); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowHeader( + /* [out] */ IAccessibleTable **accessibleTable, + /* [retval][out] */ long *startingColumnIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowIndex( + /* [in] */ long childIndex, + /* [retval][out] */ long *rowIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedChildren( + /* [in] */ long maxChildren, + /* [length_is][length_is][size_is][size_is][out] */ long **children, + /* [retval][out] */ long *nChildren); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedColumns( + /* [in] */ long maxColumns, + /* [length_is][length_is][size_is][size_is][out] */ long **columns, + /* [retval][out] */ long *nColumns); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedRows( + /* [in] */ long maxRows, + /* [length_is][length_is][size_is][size_is][out] */ long **rows, + /* [retval][out] */ long *nRows); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_summary( + /* [retval][out] */ IUnknown **accessible); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isColumnSelected( + /* [in] */ long column, + /* [retval][out] */ boolean *isSelected); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isRowSelected( + /* [in] */ long row, + /* [retval][out] */ boolean *isSelected); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isSelected( + /* [in] */ long row, + /* [in] */ long column, + /* [retval][out] */ boolean *isSelected); + + virtual HRESULT STDMETHODCALLTYPE selectRow( + /* [in] */ long row); + + virtual HRESULT STDMETHODCALLTYPE selectColumn( + /* [in] */ long column); + + virtual HRESULT STDMETHODCALLTYPE unselectRow( + /* [in] */ long row); + + virtual HRESULT STDMETHODCALLTYPE unselectColumn( + /* [in] */ long column); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowColumnExtentsAtIndex( + /* [in] */ long index, + /* [out] */ long *row, + /* [out] */ long *column, + /* [out] */ long *rowExtents, + /* [out] */ long *columnExtents, + /* [retval][out] */ boolean *isSelected); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_modelChange( + /* [retval][out] */ IA2TableModelChange *modelChange); + + + // IAccessibleTable2 + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_cellAt( + /* [in] */ long row, + /* [in] */ long column, + /* [out, retval] */ IUnknown **cell); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelectedCells( + /* [out, retval] */ long *cellCount); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedCells( + /* [out, size_is(,*nSelectedCells,)] */ IUnknown ***cells, + /* [out, retval] */ long *nSelectedCells); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedColumns( + /* [out, size_is(,*nColumns)] */ long **selectedColumns, + /* [out, retval] */ long *nColumns); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectedRows( + /* [out, size_is(,*nRows)] */ long **selectedRows, + /* [out, retval] */ long *nRows); + +protected: + ia2AccessibleTable(TableAccessible* aTable) : mTable(aTable) {} + + TableAccessible* mTable; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleTableCell.cpp b/accessible/windows/ia2/ia2AccessibleTableCell.cpp new file mode 100644 index 000000000..e625a7049 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleTableCell.cpp @@ -0,0 +1,257 @@ +/* -*- 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 "ia2AccessibleTableCell.h" + +#include "Accessible2.h" +#include "AccessibleTable2_i.c" +#include "AccessibleTableCell_i.c" + +#include "AccessibleWrap.h" +#include "TableAccessible.h" +#include "TableCellAccessible.h" +#include "IUnknownImpl.h" + +#include "nsCOMPtr.h" +#include "nsString.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleTableCell::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleTableCell == iid) { + *ppv = static_cast<IAccessibleTableCell*>(this); + (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; +} + +//////////////////////////////////////////////////////////////////////////////// +// IAccessibleTableCell + +STDMETHODIMP +ia2AccessibleTableCell::get_table(IUnknown** aTable) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aTable) + return E_INVALIDARG; + + *aTable = nullptr; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + TableAccessible* table = mTableCell->Table(); + if (!table) + return E_FAIL; + + AccessibleWrap* wrap = static_cast<AccessibleWrap*>(table->AsAccessible()); + *aTable = static_cast<IAccessible*>(wrap); + (*aTable)->AddRef(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_columnExtent(long* aSpan) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aSpan) + return E_INVALIDARG; + + *aSpan = 0; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aSpan = mTableCell->ColExtent(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_columnHeaderCells(IUnknown*** aCellAccessibles, + long* aNColumnHeaderCells) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCellAccessibles || !aNColumnHeaderCells) + return E_INVALIDARG; + + *aCellAccessibles = nullptr; + *aNColumnHeaderCells = 0; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<Accessible*, 10> cells; + mTableCell->ColHeaderCells(&cells); + + *aNColumnHeaderCells = cells.Length(); + *aCellAccessibles = + static_cast<IUnknown**>(::CoTaskMemAlloc(sizeof(IUnknown*) * + cells.Length())); + + if (!*aCellAccessibles) + return E_OUTOFMEMORY; + + for (uint32_t i = 0; i < cells.Length(); i++) { + AccessibleWrap* cell = static_cast<AccessibleWrap*>(cells[i]); + (*aCellAccessibles)[i] = static_cast<IAccessible*>(cell); + (*aCellAccessibles)[i]->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_columnIndex(long* aColIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aColIdx) + return E_INVALIDARG; + + *aColIdx = -1; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aColIdx = mTableCell->ColIdx(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_rowExtent(long* aSpan) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aSpan) + return E_INVALIDARG; + + *aSpan = 0; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aSpan = mTableCell->RowExtent(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_rowHeaderCells(IUnknown*** aCellAccessibles, + long* aNRowHeaderCells) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCellAccessibles || !aNRowHeaderCells) + return E_INVALIDARG; + + *aCellAccessibles = nullptr; + *aNRowHeaderCells = 0; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + AutoTArray<Accessible*, 10> cells; + mTableCell->RowHeaderCells(&cells); + + *aNRowHeaderCells = cells.Length(); + *aCellAccessibles = + static_cast<IUnknown**>(::CoTaskMemAlloc(sizeof(IUnknown*) * + cells.Length())); + if (!*aCellAccessibles) + return E_OUTOFMEMORY; + + for (uint32_t i = 0; i < cells.Length(); i++) { + AccessibleWrap* cell = static_cast<AccessibleWrap*>(cells[i]); + (*aCellAccessibles)[i] = static_cast<IAccessible*>(cell); + (*aCellAccessibles)[i]->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_rowIndex(long* aRowIdx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowIdx) + return E_INVALIDARG; + + *aRowIdx = -1; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aRowIdx = mTableCell->RowIdx(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_rowColumnExtents(long* aRowIdx, long* aColIdx, + long* aRowExtents, + long* aColExtents, + boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRowIdx || !aColIdx || !aRowExtents || !aColExtents || !aIsSelected) + return E_INVALIDARG; + + *aRowIdx = *aColIdx = *aRowExtents = *aColExtents = 0; + *aIsSelected = false; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aRowIdx = mTableCell->RowIdx(); + *aColIdx = mTableCell->ColIdx(); + *aRowExtents = mTableCell->RowExtent(); + *aColExtents = mTableCell->ColExtent(); + *aIsSelected = mTableCell->Selected(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleTableCell::get_isSelected(boolean* aIsSelected) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aIsSelected) + return E_INVALIDARG; + + *aIsSelected = false; + if (!mTableCell) + return CO_E_OBJNOTCONNECTED; + + *aIsSelected = mTableCell->Selected(); + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/ia2/ia2AccessibleTableCell.h b/accessible/windows/ia2/ia2AccessibleTableCell.h new file mode 100644 index 000000000..883ca2875 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleTableCell.h @@ -0,0 +1,69 @@ +/* -*- 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 _ACCESSIBLE_TABLECELL_H +#define _ACCESSIBLE_TABLECELL_H + +#include "AccessibleTableCell.h" + +namespace mozilla { +namespace a11y { +class TableCellAccessible; + +class ia2AccessibleTableCell : public IAccessibleTableCell +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleTableCell + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_table( + /* [out, retval] */ IUnknown **table); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnExtent( + /* [out, retval] */ long *nColumnsSpanned); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnHeaderCells( + /* [out, size_is(,*nColumnHeaderCells,)] */ IUnknown ***cellAccessibles, + /* [out, retval] */ long *nColumnHeaderCells); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_columnIndex( + /* [out, retval] */ long *columnIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowExtent( + /* [out, retval] */ long *nRowsSpanned); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowHeaderCells( + /* [out, size_is(,*nRowHeaderCells,)] */ IUnknown ***cellAccessibles, + /* [out, retval] */ long *nRowHeaderCells); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowIndex( + /* [out, retval] */ long *rowIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_rowColumnExtents( + /* [out] */ long *row, + /* [out] */ long *column, + /* [out] */ long *rowExtents, + /* [out] */ long *columnExtents, + /* [out, retval] */ boolean *isSelected); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_isSelected( + /* [out, retval] */ boolean *isSelected); + +protected: + ia2AccessibleTableCell(TableCellAccessible* aTableCell) : + mTableCell(aTableCell) {} + + TableCellAccessible* mTableCell; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/ia2AccessibleText.cpp b/accessible/windows/ia2/ia2AccessibleText.cpp new file mode 100644 index 000000000..7ac766f30 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleText.cpp @@ -0,0 +1,598 @@ +/* -*- 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 "ia2AccessibleText.h" + +#include "Accessible2.h" +#include "AccessibleText_i.c" + +#include "HyperTextAccessibleWrap.h" +#include "HyperTextAccessible-inl.h" +#include "ProxyWrappers.h" +#include "mozilla/ClearOnShutdown.h" + +using namespace mozilla::a11y; + +StaticRefPtr<HyperTextAccessibleWrap> ia2AccessibleText::sLastTextChangeAcc; +StaticAutoPtr<nsString> ia2AccessibleText::sLastTextChangeString; +uint32_t ia2AccessibleText::sLastTextChangeStart = 0; +uint32_t ia2AccessibleText::sLastTextChangeEnd = 0; +bool ia2AccessibleText::sLastTextChangeWasInsert = false; + +// IAccessibleText + +STDMETHODIMP +ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + return textAcc->AddToSelection(aStartOffset, aEndOffset) ? + S_OK : E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_attributes(long aOffset, long *aStartOffset, + long *aEndOffset, BSTR *aTextAttributes) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStartOffset || !aEndOffset || !aTextAttributes) + return E_INVALIDARG; + + *aStartOffset = 0; + *aEndOffset = 0; + *aTextAttributes = nullptr; + + int32_t startOffset = 0, endOffset = 0; + HRESULT hr; + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + nsCOMPtr<nsIPersistentProperties> attributes = + textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset); + + hr = AccessibleWrap::ConvertToIA2Attributes(attributes, aTextAttributes); + if (FAILED(hr)) + return hr; + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_caretOffset(long *aOffset) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aOffset) + return E_INVALIDARG; + + *aOffset = -1; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + *aOffset = textAcc->CaretOffset(); + + return *aOffset != -1 ? S_OK : S_FALSE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_characterExtents(long aOffset, + enum IA2CoordinateType aCoordType, + long* aX, long* aY, + long* aWidth, long* aHeight) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aX || !aY || !aWidth || !aHeight) + return E_INVALIDARG; + *aX = *aY = *aWidth = *aHeight = 0; + + uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : + nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + nsIntRect rect; + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + rect = textAcc->CharBounds(aOffset, geckoCoordType); + + *aX = rect.x; + *aY = rect.y; + *aWidth = rect.width; + *aHeight = rect.height; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_nSelections(long* aNSelections) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNSelections) + return E_INVALIDARG; + *aNSelections = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + *aNSelections = textAcc->SelectionCount(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_offsetAtPoint(long aX, long aY, + enum IA2CoordinateType aCoordType, + long* aOffset) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aOffset) + return E_INVALIDARG; + *aOffset = 0; + + uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : + nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType); + + return *aOffset == -1 ? S_FALSE : S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset, + long* aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStartOffset || !aEndOffset) + return E_INVALIDARG; + *aStartOffset = *aEndOffset = 0; + + int32_t startOffset = 0, endOffset = 0; + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset)) { + return E_INVALIDARG; + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aText) + return E_INVALIDARG; + + *aText = nullptr; + + nsAutoString text; + MOZ_ASSERT(!HyperTextProxyFor(this)); + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) { + return E_INVALIDARG; + } + + textAcc->TextSubstring(aStartOffset, aEndOffset, text); + + if (text.IsEmpty()) + return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_textBeforeOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStartOffset || !aEndOffset || !aText) + return E_INVALIDARG; + + *aStartOffset = *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) + return S_FALSE; + + textAcc->TextBeforeOffset(aOffset, boundaryType, &startOffset, &endOffset, text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) + return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_textAfterOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStartOffset || !aEndOffset || !aText) + return E_INVALIDARG; + + *aStartOffset = 0; + *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) + return S_FALSE; + textAcc->TextAfterOffset(aOffset, boundaryType, &startOffset, &endOffset, text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) + return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_textAtOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStartOffset || !aEndOffset || !aText) + return E_INVALIDARG; + + *aStartOffset = *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) + return S_FALSE; + textAcc->TextAtOffset(aOffset, boundaryType, &startOffset, &endOffset, text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) + return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::removeSelection(long aSelectionIndex) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + return textAcc->RemoveFromSelection(aSelectionIndex) ? + S_OK : E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::setCaretOffset(long aOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) + return E_INVALIDARG; + + textAcc->SetCaretOffset(aOffset); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset, + long aEndOffset) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset, aEndOffset) ? + S_OK : E_INVALIDARG; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_nCharacters(long* aNCharacters) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNCharacters) + return E_INVALIDARG; + *aNCharacters = 0; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aNCharacters = textAcc->CharacterCount(); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex, + enum IA2ScrollType aScrollType) +{ + A11Y_TRYBLOCK_BEGIN + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) + return E_INVALIDARG; + + textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex, + enum IA2CoordinateType aCoordType, + long aX, long aY) +{ + A11Y_TRYBLOCK_BEGIN + + uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? + nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : + nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + MOZ_ASSERT(!HyperTextProxyFor(this)); + + HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this); + if (textAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) + return E_INVALIDARG; + + textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex, + geckoCoordType, aX, aY); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_newText(IA2TextSegment *aNewText) +{ + A11Y_TRYBLOCK_BEGIN + + return GetModifiedText(true, aNewText); + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleText::get_oldText(IA2TextSegment *aOldText) +{ + A11Y_TRYBLOCK_BEGIN + + return GetModifiedText(false, aOldText); + + A11Y_TRYBLOCK_END +} + +// ia2AccessibleText + +HRESULT +ia2AccessibleText::GetModifiedText(bool aGetInsertedText, + IA2TextSegment *aText) +{ + if (!aText) + return E_INVALIDARG; + + if (!sLastTextChangeAcc) + return S_OK; + + if (aGetInsertedText != sLastTextChangeWasInsert) + return S_OK; + + if (sLastTextChangeAcc != this) + return S_OK; + + aText->start = sLastTextChangeStart; + aText->end = sLastTextChangeEnd; + + if (sLastTextChangeString->IsEmpty()) + return S_FALSE; + + aText->text = ::SysAllocStringLen(sLastTextChangeString->get(), sLastTextChangeString->Length()); + return aText->text ? S_OK : E_OUTOFMEMORY; +} + +AccessibleTextBoundary +ia2AccessibleText::GetGeckoTextBoundary(enum IA2TextBoundaryType aBoundaryType) +{ + switch (aBoundaryType) { + case IA2_TEXT_BOUNDARY_CHAR: + return nsIAccessibleText::BOUNDARY_CHAR; + case IA2_TEXT_BOUNDARY_WORD: + return nsIAccessibleText::BOUNDARY_WORD_START; + case IA2_TEXT_BOUNDARY_LINE: + return nsIAccessibleText::BOUNDARY_LINE_START; + //case IA2_TEXT_BOUNDARY_SENTENCE: + //case IA2_TEXT_BOUNDARY_PARAGRAPH: + // XXX: not implemented + default: + return -1; + } +} + +void +ia2AccessibleText::InitTextChangeData() +{ + ClearOnShutdown(&sLastTextChangeAcc); + ClearOnShutdown(&sLastTextChangeString); +} + +void +ia2AccessibleText::UpdateTextChangeData(HyperTextAccessibleWrap* aAcc, + bool aInsert, const nsString& aStr, + int32_t aStart, uint32_t aLen) +{ + if (!sLastTextChangeString) + sLastTextChangeString = new nsString(); + + sLastTextChangeAcc = aAcc; + sLastTextChangeStart = aStart; + sLastTextChangeEnd = aStart + aLen; + sLastTextChangeWasInsert = aInsert; + *sLastTextChangeString = aStr; +} diff --git a/accessible/windows/ia2/ia2AccessibleText.h b/accessible/windows/ia2/ia2AccessibleText.h new file mode 100644 index 000000000..a513e44a2 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleText.h @@ -0,0 +1,275 @@ +/* -*- 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 _ACCESSIBLE_TEXT_H +#define _ACCESSIBLE_TEXT_H + +#include "nsIAccessibleText.h" + +#include "AccessibleText.h" + +namespace mozilla { +namespace a11y { +class HyperTextAccessibleWrap; + +class ia2AccessibleText: public IAccessibleText +{ +public: + + // IAccessibleText + virtual HRESULT STDMETHODCALLTYPE addSelection( + /* [in] */ long startOffset, + /* [in] */ long endOffset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_attributes( + /* [in] */ long offset, + /* [out] */ long *startOffset, + /* [out] */ long *endOffset, + /* [retval][out] */ BSTR *textAttributes); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_caretOffset( + /* [retval][out] */ long *offset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_characterExtents( + /* [in] */ long offset, + /* [in] */ enum IA2CoordinateType coordType, + /* [out] */ long *x, + /* [out] */ long *y, + /* [out] */ long *width, + /* [retval][out] */ long *height); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nSelections( + /* [retval][out] */ long *nSelections); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_offsetAtPoint( + /* [in] */ long x, + /* [in] */ long y, + /* [in] */ enum IA2CoordinateType coordType, + /* [retval][out] */ long *offset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selection( + /* [in] */ long selectionIndex, + /* [out] */ long *startOffset, + /* [retval][out] */ long *endOffset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_text( + /* [in] */ long startOffset, + /* [in] */ long endOffset, + /* [retval][out] */ BSTR *text); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textBeforeOffset( + /* [in] */ long offset, + /* [in] */ enum IA2TextBoundaryType boundaryType, + /* [out] */ long *startOffset, + /* [out] */ long *endOffset, + /* [retval][out] */ BSTR *text); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textAfterOffset( + /* [in] */ long offset, + /* [in] */ enum IA2TextBoundaryType boundaryType, + /* [out] */ long *startOffset, + /* [out] */ long *endOffset, + /* [retval][out] */ BSTR *text); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_textAtOffset( + /* [in] */ long offset, + /* [in] */ enum IA2TextBoundaryType boundaryType, + /* [out] */ long *startOffset, + /* [out] */ long *endOffset, + /* [retval][out] */ BSTR *text); + + virtual HRESULT STDMETHODCALLTYPE removeSelection( + /* [in] */ long selectionIndex); + + virtual HRESULT STDMETHODCALLTYPE setCaretOffset( + /* [in] */ long offset); + + virtual HRESULT STDMETHODCALLTYPE setSelection( + /* [in] */ long selectionIndex, + /* [in] */ long startOffset, + /* [in] */ long endOffset); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nCharacters( + /* [retval][out] */ long *nCharacters); + + virtual HRESULT STDMETHODCALLTYPE scrollSubstringTo( + /* [in] */ long startIndex, + /* [in] */ long endIndex, + /* [in] */ enum IA2ScrollType scrollType); + + virtual HRESULT STDMETHODCALLTYPE scrollSubstringToPoint( + /* [in] */ long startIndex, + /* [in] */ long endIndex, + /* [in] */ enum IA2CoordinateType coordinateType, + /* [in] */ long x, + /* [in] */ long y); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_newText( + /* [retval][out] */ IA2TextSegment *newText); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_oldText( + /* [retval][out] */ IA2TextSegment *oldText); + + static void InitTextChangeData(); + static void UpdateTextChangeData(HyperTextAccessibleWrap* aAcc, bool aInsert, + const nsString& aStr, int32_t aStart, + uint32_t aLen); + +protected: + static StaticRefPtr<HyperTextAccessibleWrap> sLastTextChangeAcc; + static StaticAutoPtr<nsString> sLastTextChangeString; + static bool sLastTextChangeWasInsert; + static uint32_t sLastTextChangeStart; + static uint32_t sLastTextChangeEnd; + +private: + HRESULT GetModifiedText(bool aGetInsertedText, IA2TextSegment *aNewText); + AccessibleTextBoundary GetGeckoTextBoundary(enum IA2TextBoundaryType coordinateType); +}; + +} // namespace a11y +} // namespace mozilla + + +#define FORWARD_IACCESSIBLETEXT(Class) \ +virtual HRESULT STDMETHODCALLTYPE addSelection(long startOffset, \ + long endOffset) \ +{ \ + return Class::addSelection(startOffset, endOffset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_attributes(long offset, \ + long *startOffset, \ + long *endOffset, \ + BSTR *textAttributes) \ +{ \ + return Class::get_attributes(offset, startOffset, endOffset, textAttributes);\ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_caretOffset(long *offset) \ +{ \ + return Class::get_caretOffset(offset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_characterExtents(long offset, \ + enum IA2CoordinateType coordType,\ + long *x, \ + long *y, \ + long *width, \ + long *height) \ +{ \ + return Class::get_characterExtents(offset, coordType, x, y, width, height); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_nSelections(long *nSelections) \ +{ \ + return Class::get_nSelections(nSelections); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_offsetAtPoint(long x, \ + long y, \ + enum IA2CoordinateType coordType,\ + long *offset) \ +{ \ + return Class::get_offsetAtPoint(x, y, coordType, offset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_selection(long selectionIndex, \ + long *startOffset, \ + long *endOffset) \ +{ \ + return Class::get_selection(selectionIndex, startOffset, endOffset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_text(long startOffset, \ + long endOffset, \ + BSTR *text) \ +{ \ + return Class::get_text(startOffset, endOffset, text); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_textBeforeOffset(long offset, \ + enum IA2TextBoundaryType boundaryType,\ + long *startOffset, \ + long *endOffset, \ + BSTR *text) \ +{ \ + return Class::get_textBeforeOffset(offset, boundaryType, \ + startOffset, endOffset, text); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_textAfterOffset(long offset, \ + enum IA2TextBoundaryType boundaryType,\ + long *startOffset, \ + long *endOffset, \ + BSTR *text) \ +{ \ + return Class::get_textAfterOffset(offset, boundaryType, \ + startOffset, endOffset, text); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_textAtOffset(long offset, \ + enum IA2TextBoundaryType boundaryType,\ + long *startOffset, \ + long *endOffset, \ + BSTR *text) \ +{ \ + return Class::get_textAtOffset(offset, boundaryType, \ + startOffset, endOffset, text); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE removeSelection(long selectionIndex) \ +{ \ + return Class::removeSelection(selectionIndex); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE setCaretOffset(long offset) \ +{ \ + return Class::setCaretOffset(offset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE setSelection(long selectionIndex, \ + long startOffset, \ + long endOffset) \ +{ \ + return Class::setSelection(selectionIndex, startOffset, endOffset); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_nCharacters(long *nCharacters) \ +{ \ + return Class::get_nCharacters(nCharacters); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE scrollSubstringTo(long startIndex, \ + long endIndex, \ + enum IA2ScrollType scrollType)\ +{ \ + return Class::scrollSubstringTo(startIndex, endIndex, scrollType); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE scrollSubstringToPoint(long startIndex, \ + long endIndex, \ + enum IA2CoordinateType coordinateType,\ + long x, \ + long y) \ +{ \ + return Class::scrollSubstringToPoint(startIndex, endIndex, \ + coordinateType, x, y); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_newText(IA2TextSegment *newText) \ +{ \ + return Class::get_newText(newText); \ +} \ + \ +virtual HRESULT STDMETHODCALLTYPE get_oldText(IA2TextSegment *oldText) \ +{ \ + return Class::get_oldText(oldText); \ +} \ + +#endif + diff --git a/accessible/windows/ia2/ia2AccessibleValue.cpp b/accessible/windows/ia2/ia2AccessibleValue.cpp new file mode 100644 index 000000000..e33442295 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleValue.cpp @@ -0,0 +1,151 @@ +/* -*- 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 "ia2AccessibleValue.h" + +#include "AccessibleValue_i.c" + +#include "AccessibleWrap.h" +#include "Accessible-inl.h" +#include "IUnknownImpl.h" + +#include "mozilla/FloatingPoint.h" + +using namespace mozilla::a11y; + +// IUnknown + +STDMETHODIMP +ia2AccessibleValue::QueryInterface(REFIID iid, void** ppv) +{ + if (!ppv) + return E_INVALIDARG; + + *ppv = nullptr; + + if (IID_IAccessibleValue == iid) { + AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this); + if (valueAcc->HasNumericValue()) { + *ppv = static_cast<IAccessibleValue*>(this); + valueAcc->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + + return E_NOINTERFACE; +} + +// IAccessibleValue + +STDMETHODIMP +ia2AccessibleValue::get_currentValue(VARIANT* aCurrentValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCurrentValue) + return E_INVALIDARG; + + VariantInit(aCurrentValue); + + AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this); + double currentValue; + MOZ_ASSERT(!valueAcc->IsProxy()); + if (valueAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + currentValue = valueAcc->CurValue(); + + if (IsNaN(currentValue)) + return S_FALSE; + + aCurrentValue->vt = VT_R8; + aCurrentValue->dblVal = currentValue; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleValue::setCurrentValue(VARIANT aValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (aValue.vt != VT_R8) + return E_INVALIDARG; + + AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this); + MOZ_ASSERT(!valueAcc->IsProxy()); + + if (valueAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + return valueAcc->SetCurValue(aValue.dblVal) ? S_OK : E_FAIL; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleValue::get_maximumValue(VARIANT* aMaximumValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aMaximumValue) + return E_INVALIDARG; + + VariantInit(aMaximumValue); + + AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this); + double maximumValue; + MOZ_ASSERT(!valueAcc->IsProxy()); + if (valueAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + maximumValue = valueAcc->MaxValue(); + + if (IsNaN(maximumValue)) + return S_FALSE; + + aMaximumValue->vt = VT_R8; + aMaximumValue->dblVal = maximumValue; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +ia2AccessibleValue::get_minimumValue(VARIANT* aMinimumValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aMinimumValue) + return E_INVALIDARG; + + VariantInit(aMinimumValue); + + AccessibleWrap* valueAcc = static_cast<AccessibleWrap*>(this); + double minimumValue; + MOZ_ASSERT(!valueAcc->IsProxy()); + if (valueAcc->IsDefunct()) { + return CO_E_OBJNOTCONNECTED; + } + + minimumValue = valueAcc->MinValue(); + + if (IsNaN(minimumValue)) + return S_FALSE; + + aMinimumValue->vt = VT_R8; + aMinimumValue->dblVal = minimumValue; + return S_OK; + + A11Y_TRYBLOCK_END +} + diff --git a/accessible/windows/ia2/ia2AccessibleValue.h b/accessible/windows/ia2/ia2AccessibleValue.h new file mode 100644 index 000000000..97ce5ea59 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleValue.h @@ -0,0 +1,41 @@ +/* -*- 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 _ACCESSIBLE_VALUE_H +#define _ACCESSIBLE_VALUE_H + +#include "AccessibleValue.h" + +namespace mozilla { +namespace a11y { + +class ia2AccessibleValue: public IAccessibleValue +{ +public: + + // IUnknown + STDMETHODIMP QueryInterface(REFIID, void**); + + // IAccessibleValue + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_currentValue( + /* [retval][out] */ VARIANT *currentValue); + + virtual HRESULT STDMETHODCALLTYPE setCurrentValue( + /* [in] */ VARIANT value); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_maximumValue( + /* [retval][out] */ VARIANT *maximumValue); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_minimumValue( + /* [retval][out] */ VARIANT *minimumValue); + +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/ia2/moz.build b/accessible/windows/ia2/moz.build new file mode 100644 index 000000000..443e87663 --- /dev/null +++ b/accessible/windows/ia2/moz.build @@ -0,0 +1,58 @@ +# -*- 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 += [ + 'ia2Accessible.h', + 'ia2AccessibleAction.h', + 'ia2AccessibleComponent.h', + 'ia2AccessibleEditableText.h', + 'ia2AccessibleHyperlink.h', + 'ia2AccessibleHypertext.h', + 'ia2AccessibleText.h', + 'ia2AccessibleValue.h', +] + +UNIFIED_SOURCES += [ + 'ia2Accessible.cpp', + 'ia2AccessibleAction.cpp', + 'ia2AccessibleComponent.cpp', + 'ia2AccessibleEditableText.cpp', + 'ia2AccessibleHyperlink.cpp', + 'ia2AccessibleHypertext.cpp', + 'ia2AccessibleImage.cpp', + 'ia2AccessibleRelation.cpp', + 'ia2AccessibleText.cpp', + 'ia2AccessibleValue.cpp', +] + +# These files cannot be built in unified mode because they both include +# AccessibleTable2_i.c. +SOURCES += [ + 'ia2AccessibleTable.cpp', + 'ia2AccessibleTableCell.cpp', +] + +LOCAL_INCLUDES += [ + '/accessible/base', + '/accessible/generic', + '/accessible/html', + '/accessible/windows', + '/accessible/windows/msaa', + '/accessible/xpcom', + '/accessible/xul', +] + +FINAL_LIBRARY = 'xul' + +# 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') diff --git a/accessible/windows/moz.build b/accessible/windows/moz.build new file mode 100644 index 000000000..4bfa4f330 --- /dev/null +++ b/accessible/windows/moz.build @@ -0,0 +1,8 @@ +# -*- 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/. + +DIRS += ['msaa', 'ia2', 'sdn', 'uia'] + 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 diff --git a/accessible/windows/sdn/moz.build b/accessible/windows/sdn/moz.build new file mode 100644 index 000000000..ae0d5e20d --- /dev/null +++ b/accessible/windows/sdn/moz.build @@ -0,0 +1,24 @@ +# -*- 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/. + +UNIFIED_SOURCES += [ + 'sdnAccessible.cpp', + 'sdnDocAccessible.cpp', + 'sdnTextAccessible.cpp', +] + +LOCAL_INCLUDES += [ + '/accessible/base', + '/accessible/generic', + '/accessible/html', + '/accessible/windows/msaa', + '/accessible/xpcom', + '/accessible/xul', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/accessible/windows/sdn/sdnAccessible-inl.h b/accessible/windows/sdn/sdnAccessible-inl.h new file mode 100644 index 000000000..a3d1a95e8 --- /dev/null +++ b/accessible/windows/sdn/sdnAccessible-inl.h @@ -0,0 +1,34 @@ +/* -*- 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_sdnAccessible_inl_h_ +#define mozilla_a11y_sdnAccessible_inl_h_ + +#include "sdnAccessible.h" + +#include "DocAccessible.h" +#include "nsAccessibilityService.h" + +namespace mozilla { +namespace a11y { + +inline DocAccessible* +sdnAccessible::GetDocument() const +{ + return GetExistingDocAccessible(mNode->OwnerDoc()); +} + +inline Accessible* +sdnAccessible::GetAccessible() const +{ + DocAccessible* document = GetDocument(); + return document ? document->GetAccessibleEvenIfNotInMap(mNode) : nullptr; +} + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_sdnAccessible_inl_h_ diff --git a/accessible/windows/sdn/sdnAccessible.cpp b/accessible/windows/sdn/sdnAccessible.cpp new file mode 100644 index 000000000..909b0779c --- /dev/null +++ b/accessible/windows/sdn/sdnAccessible.cpp @@ -0,0 +1,539 @@ +/* -*- 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 "sdnAccessible-inl.h" +#include "ISimpleDOMNode_i.c" + +#include "DocAccessibleWrap.h" + +#include "nsAttrName.h" +#include "nsCoreUtils.h" +#include "nsIAccessibleTypes.h" +#include "nsIDOMHTMLElement.h" +#include "nsIDOMCSSStyleDeclaration.h" +#include "nsNameSpaceManager.h" +#include "nsServiceManagerUtils.h" +#include "nsWinUtils.h" +#include "nsRange.h" + +#include "mozilla/dom/BorrowedAttrInfo.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +STDMETHODIMP +sdnAccessible::QueryInterface(REFIID aREFIID, void** aInstancePtr) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aInstancePtr) + return E_FAIL; + *aInstancePtr = nullptr; + + if (aREFIID == IID_ISimpleDOMNode) { + *aInstancePtr = static_cast<ISimpleDOMNode*>(this); + AddRef(); + return S_OK; + } + + AccessibleWrap* accessible = static_cast<AccessibleWrap*>(GetAccessible()); + if (accessible) + return accessible->QueryInterface(aREFIID, aInstancePtr); + + // IUnknown* is the canonical one if and only if this accessible doesn't have + // an accessible. + if (aREFIID == IID_IUnknown) { + *aInstancePtr = static_cast<ISimpleDOMNode*>(this); + AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_nodeInfo(BSTR __RPC_FAR* aNodeName, + short __RPC_FAR* aNameSpaceID, + BSTR __RPC_FAR* aNodeValue, + unsigned int __RPC_FAR* aNumChildren, + unsigned int __RPC_FAR* aUniqueID, + unsigned short __RPC_FAR* aNodeType) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNodeName || !aNameSpaceID || !aNodeValue || !aNumChildren || + !aUniqueID || !aNodeType) + return E_INVALIDARG; + + *aNodeName = nullptr; + *aNameSpaceID = 0; + *aNodeValue = nullptr; + *aNumChildren = 0; + *aUniqueID = 0; + *aNodeType = 0; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mNode)); + + uint16_t nodeType = 0; + DOMNode->GetNodeType(&nodeType); + *aNodeType = static_cast<unsigned short>(nodeType); + + if (*aNodeType != NODETYPE_TEXT) { + nsAutoString nodeName; + DOMNode->GetNodeName(nodeName); + *aNodeName = ::SysAllocString(nodeName.get()); + } + + nsAutoString nodeValue; + DOMNode->GetNodeValue(nodeValue); + *aNodeValue = ::SysAllocString(nodeValue.get()); + + *aNameSpaceID = mNode->IsNodeOfType(nsINode::eCONTENT) ? + static_cast<short>(mNode->AsContent()->GetNameSpaceID()) : 0; + + // This is a unique ID for every content node. The 3rd party accessibility + // application can compare this to the childID we return for events such as + // focus events, to correlate back to data nodes in their internal object + // model. + Accessible* accessible = GetAccessible(); + if (accessible) { + *aUniqueID = AccessibleWrap::GetChildIDFor(accessible); + } else { + *aUniqueID = - NS_PTR_TO_INT32(static_cast<void*>(this)); + } + + *aNumChildren = mNode->GetChildCount(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_attributes(unsigned short aMaxAttribs, + BSTR __RPC_FAR* aAttribNames, + short __RPC_FAR* aNameSpaceIDs, + BSTR __RPC_FAR* aAttribValues, + unsigned short __RPC_FAR* aNumAttribs) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAttribNames || !aNameSpaceIDs || !aAttribValues || !aNumAttribs) + return E_INVALIDARG; + + *aNumAttribs = 0; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!mNode->IsElement()) + return S_FALSE; + + dom::Element* elm = mNode->AsElement(); + uint32_t numAttribs = elm->GetAttrCount(); + if (numAttribs > aMaxAttribs) + numAttribs = aMaxAttribs; + + *aNumAttribs = static_cast<unsigned short>(numAttribs); + + for (uint32_t index = 0; index < numAttribs; index++) { + aNameSpaceIDs[index] = 0; + aAttribValues[index] = aAttribNames[index] = nullptr; + nsAutoString attributeValue; + + dom::BorrowedAttrInfo attr = elm->GetAttrInfoAt(index); + attr.mValue->ToString(attributeValue); + + aNameSpaceIDs[index] = static_cast<short>(attr.mName->NamespaceID()); + aAttribNames[index] = ::SysAllocString(attr.mName->LocalName()->GetUTF16String()); + aAttribValues[index] = ::SysAllocString(attributeValue.get()); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_attributesForNames(unsigned short aMaxAttribs, + BSTR __RPC_FAR* aAttribNames, + short __RPC_FAR* aNameSpaceID, + BSTR __RPC_FAR* aAttribValues) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAttribNames || !aNameSpaceID || !aAttribValues) + return E_INVALIDARG; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!mNode->IsElement()) + return S_FALSE; + + nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(mNode)); + nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance(); + + int32_t index = 0; + for (index = 0; index < aMaxAttribs; index++) { + aAttribValues[index] = nullptr; + if (aAttribNames[index]) { + nsAutoString attributeValue, nameSpaceURI; + nsAutoString attributeName(nsDependentString( + static_cast<const wchar_t*>(aAttribNames[index]))); + + nsresult rv = NS_OK; + if (aNameSpaceID[index]>0 && + NS_SUCCEEDED(nameSpaceManager->GetNameSpaceURI(aNameSpaceID[index], + nameSpaceURI))) { + rv = domElement->GetAttributeNS(nameSpaceURI, attributeName, + attributeValue); + } else { + rv = domElement->GetAttribute(attributeName, attributeValue); + } + + if (NS_SUCCEEDED(rv)) + aAttribValues[index] = ::SysAllocString(attributeValue.get()); + } + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_computedStyle(unsigned short aMaxStyleProperties, + boolean aUseAlternateView, + BSTR __RPC_FAR* aStyleProperties, + BSTR __RPC_FAR* aStyleValues, + unsigned short __RPC_FAR* aNumStyleProperties) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStyleProperties || aStyleValues || !aNumStyleProperties) + return E_INVALIDARG; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aNumStyleProperties = 0; + + if (mNode->IsNodeOfType(nsINode::eDOCUMENT)) + return S_FALSE; + + nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl = + nsWinUtils::GetComputedStyleDeclaration(mNode->AsContent()); + NS_ENSURE_TRUE(cssDecl, E_FAIL); + + uint32_t length = 0; + cssDecl->GetLength(&length); + + uint32_t index = 0, realIndex = 0; + for (index = realIndex = 0; index < length && realIndex < aMaxStyleProperties; + index ++) { + nsAutoString property, value; + + // Ignore -moz-* properties. + if (NS_SUCCEEDED(cssDecl->Item(index, property)) && property.CharAt(0) != '-') + cssDecl->GetPropertyValue(property, value); // Get property value + + if (!value.IsEmpty()) { + aStyleProperties[realIndex] = ::SysAllocString(property.get()); + aStyleValues[realIndex] = ::SysAllocString(value.get()); + ++realIndex; + } + } + + *aNumStyleProperties = static_cast<unsigned short>(realIndex); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_computedStyleForProperties(unsigned short aNumStyleProperties, + boolean aUseAlternateView, + BSTR __RPC_FAR* aStyleProperties, + BSTR __RPC_FAR* aStyleValues) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aStyleProperties || !aStyleValues) + return E_INVALIDARG; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (mNode->IsNodeOfType(nsINode::eDOCUMENT)) + return S_FALSE; + + nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl = + nsWinUtils::GetComputedStyleDeclaration(mNode->AsContent()); + NS_ENSURE_TRUE(cssDecl, E_FAIL); + + uint32_t index = 0; + for (index = 0; index < aNumStyleProperties; index++) { + nsAutoString value; + if (aStyleProperties[index]) + cssDecl->GetPropertyValue(nsDependentString(aStyleProperties[index]), value); // Get property value + aStyleValues[index] = ::SysAllocString(value.get()); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::scrollTo(boolean aScrollTopLeft) +{ + A11Y_TRYBLOCK_BEGIN + + DocAccessible* document = GetDocument(); + if (!document) // that's IsDefunct check + return CO_E_OBJNOTCONNECTED; + + if (!mNode->IsContent()) + return S_FALSE; + + uint32_t scrollType = + aScrollTopLeft ? nsIAccessibleScrollType::SCROLL_TYPE_TOP_LEFT : + nsIAccessibleScrollType::SCROLL_TYPE_BOTTOM_RIGHT; + + nsCoreUtils::ScrollTo(document->PresShell(), mNode->AsContent(), scrollType); + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_parentNode(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetParentNode(); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_firstChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetFirstChild(); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_lastChild(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetLastChild(); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_previousSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetPreviousSibling(); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_nextSibling(ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetNextSibling(); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_childAt(unsigned aChildIndex, + ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNode) + return E_INVALIDARG; + *aNode = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsINode* resultNode = mNode->GetChildAt(aChildIndex); + if (resultNode) { + *aNode = static_cast<ISimpleDOMNode*>(new sdnAccessible(resultNode)); + (*aNode)->AddRef(); + } + + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_innerHTML(BSTR __RPC_FAR* aInnerHTML) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aInnerHTML) + return E_INVALIDARG; + *aInnerHTML = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (!mNode->IsElement()) + return S_FALSE; + + nsAutoString innerHTML; + mNode->AsElement()->GetInnerHTML(innerHTML); + if (innerHTML.IsEmpty()) + return S_FALSE; + + *aInnerHTML = ::SysAllocStringLen(innerHTML.get(), innerHTML.Length()); + if (!*aInnerHTML) + return E_OUTOFMEMORY; + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_localInterface(void __RPC_FAR *__RPC_FAR* aLocalInterface) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLocalInterface) + return E_INVALIDARG; + *aLocalInterface = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aLocalInterface = this; + AddRef(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnAccessible::get_language(BSTR __RPC_FAR* aLanguage) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aLanguage) + return E_INVALIDARG; + *aLanguage = nullptr; + + if (IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString language; + if (mNode->IsContent()) + nsCoreUtils::GetLanguageFor(mNode->AsContent(), nullptr, language); + if (language.IsEmpty()) { // Nothing found, so use document's language + mNode->OwnerDoc()->GetHeaderData(nsGkAtoms::headerContentLanguage, + language); + } + + if (language.IsEmpty()) + return S_FALSE; + + *aLanguage = ::SysAllocStringLen(language.get(), language.Length()); + if (!*aLanguage) + return E_OUTOFMEMORY; + + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/sdn/sdnAccessible.h b/accessible/windows/sdn/sdnAccessible.h new file mode 100644 index 000000000..2876ad270 --- /dev/null +++ b/accessible/windows/sdn/sdnAccessible.h @@ -0,0 +1,119 @@ +/* -*- 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_sdnAccessible_h_ +#define mozilla_a11y_sdnAccessible_h_ + +#include "ISimpleDOMNode.h" +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" + +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace a11y { + +class sdnAccessible final : public ISimpleDOMNode +{ +public: + sdnAccessible(nsINode* aNode) : + mNode(aNode) + { + if (!mNode) + MOZ_CRASH(); + } + ~sdnAccessible() { } + + /** + * Retrun if the object is defunct. + */ + bool IsDefunct() const { return !GetDocument(); } + + /** + * Return a document accessible it belongs to if any. + */ + DocAccessible* GetDocument() const; + + /* + * Return associated accessible if any. + */ + Accessible* GetAccessible() const; + + //IUnknown + DECL_IUNKNOWN + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nodeInfo( + /* [out] */ BSTR __RPC_FAR* aNodeName, + /* [out] */ short __RPC_FAR* aNameSpaceID, + /* [out] */ BSTR __RPC_FAR* aNodeValue, + /* [out] */ unsigned int __RPC_FAR* aNumChildren, + /* [out] */ unsigned int __RPC_FAR* aUniqueID, + /* [out][retval] */ unsigned short __RPC_FAR* aNodeType); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_attributes( + /* [in] */ unsigned short aMaxAttribs, + /* [length_is][size_is][out] */ BSTR __RPC_FAR* aAttribNames, + /* [length_is][size_is][out] */ short __RPC_FAR* aNameSpaceIDs, + /* [length_is][size_is][out] */ BSTR __RPC_FAR* aAttribValues, + /* [out][retval] */ unsigned short __RPC_FAR* aNumAttribs); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_attributesForNames( + /* [in] */ unsigned short aMaxAttribs, + /* [length_is][size_is][in] */ BSTR __RPC_FAR* aAttribNames, + /* [length_is][size_is][in] */ short __RPC_FAR* aNameSpaceID, + /* [length_is][size_is][retval] */ BSTR __RPC_FAR* aAttribValues); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_computedStyle( + /* [in] */ unsigned short aMaxStyleProperties, + /* [in] */ boolean aUseAlternateView, + /* [length_is][size_is][out] */ BSTR __RPC_FAR* aStyleProperties, + /* [length_is][size_is][out] */ BSTR __RPC_FAR* aStyleValues, + /* [out][retval] */ unsigned short __RPC_FAR* aNumStyleProperties); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_computedStyleForProperties( + /* [in] */ unsigned short aNumStyleProperties, + /* [in] */ boolean aUseAlternateView, + /* [length_is][size_is][in] */ BSTR __RPC_FAR* aStyleProperties, + /* [length_is][size_is][out][retval] */ BSTR __RPC_FAR* aStyleValues); + + virtual HRESULT STDMETHODCALLTYPE scrollTo(/* [in] */ boolean aScrollTopLeft); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_parentNode( + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_firstChild( + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_lastChild( + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_previousSibling( + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_nextSibling( + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_childAt( + /* [in] */ unsigned aChildIndex, + /* [out][retval] */ ISimpleDOMNode __RPC_FAR *__RPC_FAR* aNode); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_innerHTML( + /* [out][retval] */ BSTR __RPC_FAR* aInnerHTML); + + virtual /* [local][propget] */ HRESULT STDMETHODCALLTYPE get_localInterface( + /* [retval][out] */ void __RPC_FAR *__RPC_FAR* aLocalInterface); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_language( + /* [out][retval] */ BSTR __RPC_FAR* aLanguage); + +private: + nsCOMPtr<nsINode> mNode; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_sdnAccessible_h_ diff --git a/accessible/windows/sdn/sdnDocAccessible.cpp b/accessible/windows/sdn/sdnDocAccessible.cpp new file mode 100644 index 000000000..07b39e66f --- /dev/null +++ b/accessible/windows/sdn/sdnDocAccessible.cpp @@ -0,0 +1,157 @@ +/* -*- 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 "sdnDocAccessible.h" + +#include "ISimpleDOMDocument_i.c" + +#include "nsNameSpaceManager.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// sdnDocAccessible +//////////////////////////////////////////////////////////////////////////////// + +IMPL_IUNKNOWN_QUERY_HEAD(sdnDocAccessible) + IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMDocument) +IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible) + +STDMETHODIMP +sdnDocAccessible::get_URL(BSTR __RPC_FAR* aURL) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aURL) + return E_INVALIDARG; + *aURL = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString URL; + mAccessible->URL(URL); + if (URL.IsEmpty()) + return S_FALSE; + + *aURL = ::SysAllocStringLen(URL.get(), URL.Length()); + return *aURL ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnDocAccessible::get_title(BSTR __RPC_FAR* aTitle) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aTitle) + return E_INVALIDARG; + *aTitle = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString title; + mAccessible->Title(title); + *aTitle = ::SysAllocStringLen(title.get(), title.Length()); + return *aTitle ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnDocAccessible::get_mimeType(BSTR __RPC_FAR* aMimeType) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aMimeType) + return E_INVALIDARG; + *aMimeType = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString mimeType; + mAccessible->MimeType(mimeType); + if (mimeType.IsEmpty()) + return S_FALSE; + + *aMimeType = ::SysAllocStringLen(mimeType.get(), mimeType.Length()); + return *aMimeType ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnDocAccessible::get_docType(BSTR __RPC_FAR* aDocType) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aDocType) + return E_INVALIDARG; + *aDocType = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString docType; + mAccessible->DocType(docType); + if (docType.IsEmpty()) + return S_FALSE; + + *aDocType = ::SysAllocStringLen(docType.get(), docType.Length()); + return *aDocType ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnDocAccessible::get_nameSpaceURIForID(short aNameSpaceID, + BSTR __RPC_FAR* aNameSpaceURI) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aNameSpaceURI) + return E_INVALIDARG; + *aNameSpaceURI = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + if (aNameSpaceID < 0) + return E_INVALIDARG; // -1 is kNameSpaceID_Unknown + + nsAutoString nameSpaceURI; + nsNameSpaceManager* nameSpaceManager = nsNameSpaceManager::GetInstance(); + if (nameSpaceManager) + nameSpaceManager->GetNameSpaceURI(aNameSpaceID, nameSpaceURI); + + if (nameSpaceURI.IsEmpty()) + return S_FALSE; + + *aNameSpaceURI = ::SysAllocStringLen(nameSpaceURI.get(), + nameSpaceURI.Length()); + + return *aNameSpaceURI ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnDocAccessible::put_alternateViewMediaTypes(BSTR __RPC_FAR* aCommaSeparatedMediaTypes) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aCommaSeparatedMediaTypes) + return E_INVALIDARG; + *aCommaSeparatedMediaTypes = nullptr; + + return mAccessible->IsDefunct() ? CO_E_OBJNOTCONNECTED : E_NOTIMPL; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/sdn/sdnDocAccessible.h b/accessible/windows/sdn/sdnDocAccessible.h new file mode 100644 index 000000000..22c7124b2 --- /dev/null +++ b/accessible/windows/sdn/sdnDocAccessible.h @@ -0,0 +1,53 @@ +/* -*- 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_sdnDocAccessible_h_ +#define mozilla_a11y_sdnDocAccessible_h_ + +#include "ISimpleDOMDocument.h" +#include "IUnknownImpl.h" + +#include "DocAccessibleWrap.h" + +namespace mozilla { +namespace a11y { + +class sdnDocAccessible final : public ISimpleDOMDocument +{ +public: + sdnDocAccessible(DocAccessibleWrap* aAccessible) : mAccessible(aAccessible) {}; + ~sdnDocAccessible() { }; + + DECL_IUNKNOWN + + // ISimpleDOMDocument + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_URL( + /* [out] */ BSTR __RPC_FAR *url); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_title( + /* [out] */ BSTR __RPC_FAR *title); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_mimeType( + /* [out] */ BSTR __RPC_FAR *mimeType); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_docType( + /* [out] */ BSTR __RPC_FAR *docType); + + virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nameSpaceURIForID( + /* [in] */ short nameSpaceID, + /* [out] */ BSTR __RPC_FAR *nameSpaceURI); + + virtual /* [id] */ HRESULT STDMETHODCALLTYPE put_alternateViewMediaTypes( + /* [in] */ BSTR __RPC_FAR *commaSeparatedMediaTypes); + +protected: + RefPtr<DocAccessibleWrap> mAccessible; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/sdn/sdnTextAccessible.cpp b/accessible/windows/sdn/sdnTextAccessible.cpp new file mode 100644 index 000000000..b51caf44e --- /dev/null +++ b/accessible/windows/sdn/sdnTextAccessible.cpp @@ -0,0 +1,210 @@ +/* -*- 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 "sdnTextAccessible.h" + +#include "ISimpleDOMText_i.c" + +#include "nsCoreUtils.h" +#include "DocAccessible.h" + +#include "nsIFrame.h" +#include "nsFontMetrics.h" +#include "nsPresContext.h" +#include "nsLayoutUtils.h" +#include "nsRange.h" +#include "gfxFont.h" +#include "nsIAccessibleTypes.h" +#include "mozilla/gfx/2D.h" + +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// sdnTextAccessible +//////////////////////////////////////////////////////////////////////////////// + +IMPL_IUNKNOWN_QUERY_HEAD(sdnTextAccessible) + IMPL_IUNKNOWN_QUERY_IFACE(ISimpleDOMText) +IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAccessible) + +STDMETHODIMP +sdnTextAccessible::get_domText(BSTR __RPC_FAR* aText) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aText) + return E_INVALIDARG; + *aText = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsAutoString nodeValue; + + nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mAccessible->GetContent())); + DOMNode->GetNodeValue(nodeValue); + if (nodeValue.IsEmpty()) + return S_FALSE; + + *aText = ::SysAllocStringLen(nodeValue.get(), nodeValue.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnTextAccessible::get_clippedSubstringBounds(unsigned int aStartIndex, + unsigned int aEndIndex, + int __RPC_FAR* aX, + int __RPC_FAR* aY, + int __RPC_FAR* aWidth, + int __RPC_FAR* aHeight) +{ + A11Y_TRYBLOCK_BEGIN + + nscoord x = 0, y = 0, width = 0, height = 0; + HRESULT rv = get_unclippedSubstringBounds(aStartIndex, aEndIndex, + &x, &y, &width, &height); + if (FAILED(rv)) + return rv; + + DocAccessible* document = mAccessible->Document(); + NS_ASSERTION(document, + "There must always be a doc accessible, but there isn't. Crash!"); + + nsIntRect docRect = document->Bounds(); + nsIntRect unclippedRect(x, y, width, height); + + nsIntRect clippedRect; + clippedRect.IntersectRect(unclippedRect, docRect); + + *aX = clippedRect.x; + *aY = clippedRect.y; + *aWidth = clippedRect.width; + *aHeight = clippedRect.height; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnTextAccessible::get_unclippedSubstringBounds(unsigned int aStartIndex, + unsigned int aEndIndex, + int __RPC_FAR* aX, + int __RPC_FAR* aY, + int __RPC_FAR* aWidth, + int __RPC_FAR* aHeight) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aX || !aY || !aWidth || !aHeight) + return E_INVALIDARG; + *aX = *aY = *aWidth = *aHeight = 0; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsIFrame *frame = mAccessible->GetFrame(); + NS_ENSURE_TRUE(frame, E_FAIL); + + nsPoint startPoint, endPoint; + nsIFrame* startFrame = GetPointFromOffset(frame, aStartIndex, true, startPoint); + nsIFrame* endFrame = GetPointFromOffset(frame, aEndIndex, false, endPoint); + if (!startFrame || !endFrame) + return E_FAIL; + + nsRect sum; + nsIFrame* iter = startFrame; + nsIFrame* stopLoopFrame = endFrame->GetNextContinuation(); + for (; iter != stopLoopFrame; iter = iter->GetNextContinuation()) { + nsRect rect = iter->GetScreenRectInAppUnits(); + nscoord start = (iter == startFrame) ? startPoint.x : 0; + nscoord end = (iter == endFrame) ? endPoint.x : rect.width; + rect.x += start; + rect.width = end - start; + sum.UnionRect(sum, rect); + } + + nsPresContext* presContext = mAccessible->Document()->PresContext(); + *aX = presContext->AppUnitsToDevPixels(sum.x); + *aY = presContext->AppUnitsToDevPixels(sum.y); + *aWidth = presContext->AppUnitsToDevPixels(sum.width); + *aHeight = presContext->AppUnitsToDevPixels(sum.height); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnTextAccessible::scrollToSubstring(unsigned int aStartIndex, + unsigned int aEndIndex) +{ + A11Y_TRYBLOCK_BEGIN + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + RefPtr<nsRange> range = new nsRange(mAccessible->GetContent()); + if (NS_FAILED(range->SetStart(mAccessible->GetContent(), aStartIndex))) + return E_FAIL; + + if (NS_FAILED(range->SetEnd(mAccessible->GetContent(), aEndIndex))) + return E_FAIL; + + nsresult rv = + nsCoreUtils::ScrollSubstringTo(mAccessible->GetFrame(), range, + nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE); + return GetHRESULT(rv); + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +sdnTextAccessible::get_fontFamily(BSTR __RPC_FAR* aFontFamily) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aFontFamily) + return E_INVALIDARG; + *aFontFamily = nullptr; + + if (mAccessible->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + nsIFrame* frame = mAccessible->GetFrame(); + if (!frame) + return E_FAIL; + + RefPtr<nsFontMetrics> fm = + nsLayoutUtils::GetFontMetricsForFrame(frame, 1.0f); + + const nsString& name = + fm->GetThebesFontGroup()->GetFirstValidFont()->GetName(); + if (name.IsEmpty()) + return S_FALSE; + + *aFontFamily = ::SysAllocStringLen(name.get(), name.Length()); + return *aFontFamily ? S_OK : E_OUTOFMEMORY; + + A11Y_TRYBLOCK_END +} + +nsIFrame* +sdnTextAccessible::GetPointFromOffset(nsIFrame* aContainingFrame, + int32_t aOffset, + bool aPreferNext, + nsPoint& aOutPoint) +{ + nsIFrame* textFrame = nullptr; + int32_t outOffset; + aContainingFrame->GetChildFrameContainingOffset(aOffset, aPreferNext, + &outOffset, &textFrame); + if (textFrame) + textFrame->GetPointFromOffset(aOffset, &aOutPoint); + + return textFrame; +} diff --git a/accessible/windows/sdn/sdnTextAccessible.h b/accessible/windows/sdn/sdnTextAccessible.h new file mode 100644 index 000000000..ed8eecf29 --- /dev/null +++ b/accessible/windows/sdn/sdnTextAccessible.h @@ -0,0 +1,71 @@ +/* -*- 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_sdnTextAccessible_h_ +#define mozilla_a11y_sdnTextAccessible_h_ + +#include "ISimpleDOMText.h" +#include "IUnknownImpl.h" + +#include "AccessibleWrap.h" + +class nsIFrame; +struct nsPoint; + +namespace mozilla { +namespace a11y { + +class sdnTextAccessible final : public ISimpleDOMText +{ +public: + sdnTextAccessible(AccessibleWrap* aAccessible) : mAccessible(aAccessible) {}; + ~sdnTextAccessible() {} + + DECL_IUNKNOWN + + // ISimpleDOMText + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_domText( + /* [retval][out] */ BSTR __RPC_FAR *aText); + + virtual HRESULT STDMETHODCALLTYPE get_clippedSubstringBounds( + /* [in] */ unsigned int startIndex, + /* [in] */ unsigned int endIndex, + /* [out] */ int __RPC_FAR* aX, + /* [out] */ int __RPC_FAR* aY, + /* [out] */ int __RPC_FAR* aWidth, + /* [out] */ int __RPC_FAR* aHeight); + + virtual HRESULT STDMETHODCALLTYPE get_unclippedSubstringBounds( + /* [in] */ unsigned int aStartIndex, + /* [in] */ unsigned int aEndIndex, + /* [out] */ int __RPC_FAR* aX, + /* [out] */ int __RPC_FAR* aY, + /* [out] */ int __RPC_FAR* aWidth, + /* [out] */ int __RPC_FAR* aHeight); + + virtual HRESULT STDMETHODCALLTYPE scrollToSubstring( + /* [in] */ unsigned int aStartIndex, + /* [in] */ unsigned int aEndIndex); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_fontFamily( + /* [retval][out] */ BSTR __RPC_FAR* aFontFamily); + +private: + /** + * Return child frame containing offset on success. + */ + nsIFrame* GetPointFromOffset(nsIFrame* aContainingFrame, + int32_t aOffset, bool aPreferNext, + nsPoint& aOutPoint); + + RefPtr<AccessibleWrap> mAccessible; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/windows/uia/moz.build b/accessible/windows/uia/moz.build new file mode 100644 index 000000000..afc150e11 --- /dev/null +++ b/accessible/windows/uia/moz.build @@ -0,0 +1,22 @@ +# -*- 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/. + +SOURCES += [ + 'uiaRawElmProvider.cpp', +] + +LOCAL_INCLUDES += [ + '/accessible/base', + '/accessible/generic', + '/accessible/html', + '/accessible/windows/msaa', + '/accessible/xpcom', + '/accessible/xul', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp new file mode 100644 index 000000000..54e54766d --- /dev/null +++ b/accessible/windows/uia/uiaRawElmProvider.cpp @@ -0,0 +1,246 @@ +/* -*- 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 "uiaRawElmProvider.h" + +#include "AccessibleWrap.h" +#include "ARIAMap.h" +#include "nsIPersistentProperties2.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// uiaRawElmProvider +//////////////////////////////////////////////////////////////////////////////// + +IMPL_IUNKNOWN2(uiaRawElmProvider, + IAccessibleEx, + IRawElementProviderSimple) + +//////////////////////////////////////////////////////////////////////////////// +// IAccessibleEx + +STDMETHODIMP +uiaRawElmProvider::GetObjectForChild(long aIdChild, + __RPC__deref_out_opt IAccessibleEx** aAccEx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAccEx) + return E_INVALIDARG; + + *aAccEx = nullptr; + + return mAcc->IsDefunct() ? CO_E_OBJNOTCONNECTED : S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::GetIAccessiblePair(__RPC__deref_out_opt IAccessible** aAcc, + __RPC__out long* aIdChild) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aAcc || !aIdChild) + return E_INVALIDARG; + + *aAcc = nullptr; + *aIdChild = 0; + + if (mAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + *aIdChild = CHILDID_SELF; + *aAcc = mAcc; + mAcc->AddRef(); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::GetRuntimeId(__RPC__deref_out_opt SAFEARRAY** aRuntimeIds) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRuntimeIds) + return E_INVALIDARG; + + int ids[] = { UiaAppendRuntimeId, static_cast<int>(reinterpret_cast<intptr_t>(mAcc->UniqueID())) }; + *aRuntimeIds = SafeArrayCreateVector(VT_I4, 0, 2); + if (!*aRuntimeIds) + return E_OUTOFMEMORY; + + for (LONG i = 0; i < (LONG)ArrayLength(ids); i++) + SafeArrayPutElement(*aRuntimeIds, &i, (void*)&(ids[i])); + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::ConvertReturnedElement(__RPC__in_opt IRawElementProviderSimple* aRawElmProvider, + __RPC__deref_out_opt IAccessibleEx** aAccEx) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRawElmProvider || !aAccEx) + return E_INVALIDARG; + + *aAccEx = nullptr; + + void* instancePtr = nullptr; + HRESULT hr = aRawElmProvider->QueryInterface(IID_IAccessibleEx, &instancePtr); + if (SUCCEEDED(hr)) + *aAccEx = static_cast<IAccessibleEx*>(instancePtr); + + return hr; + + A11Y_TRYBLOCK_END +} + +//////////////////////////////////////////////////////////////////////////////// +// IRawElementProviderSimple + +STDMETHODIMP +uiaRawElmProvider::get_ProviderOptions(__RPC__out enum ProviderOptions* aOptions) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aOptions) + return E_INVALIDARG; + + // This method is not used with IAccessibleEx implementations. + *aOptions = ProviderOptions_ServerSideProvider; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::GetPatternProvider(PATTERNID aPatternId, + __RPC__deref_out_opt IUnknown** aPatternProvider) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aPatternProvider) + return E_INVALIDARG; + + *aPatternProvider = nullptr; + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId, + __RPC__out VARIANT* aPropertyValue) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aPropertyValue) + return E_INVALIDARG; + + if (mAcc->IsDefunct()) + return CO_E_OBJNOTCONNECTED; + + aPropertyValue->vt = VT_EMPTY; + + switch (aPropertyId) { + // Accelerator Key / shortcut. + case UIA_AcceleratorKeyPropertyId: { + nsAutoString keyString; + + mAcc->KeyboardShortcut().ToString(keyString); + + if (!keyString.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(keyString.get()); + return S_OK; + } + + break; + } + + // Access Key / mneumonic. + case UIA_AccessKeyPropertyId: { + nsAutoString keyString; + + mAcc->AccessKey().ToString(keyString); + + if (!keyString.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(keyString.get()); + return S_OK; + } + + break; + } + + //ARIA Role / shortcut + case UIA_AriaRolePropertyId: { + nsAutoString xmlRoles; + + nsCOMPtr<nsIPersistentProperties> attributes = mAcc->Attributes(); + attributes->GetStringProperty(NS_LITERAL_CSTRING("xml-roles"), xmlRoles); + + if(!xmlRoles.IsEmpty()) { + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(xmlRoles.get()); + return S_OK; + } + + break; + } + + //ARIA Properties + case UIA_AriaPropertiesPropertyId: { + nsAutoString ariaProperties; + + aria::AttrIterator attribIter(mAcc->GetContent()); + nsAutoString attribName, attribValue; + while (attribIter.Next(attribName, attribValue)) { + ariaProperties.Append(attribName); + ariaProperties.Append('='); + ariaProperties.Append(attribValue); + ariaProperties.Append(';'); + } + + if (!ariaProperties.IsEmpty()) { + //remove last delimiter: + ariaProperties.Truncate(ariaProperties.Length()-1); + aPropertyValue->vt = VT_BSTR; + aPropertyValue->bstrVal = ::SysAllocString(ariaProperties.get()); + return S_OK; + } + + break; + } + } + + return S_OK; + + A11Y_TRYBLOCK_END +} + +STDMETHODIMP +uiaRawElmProvider::get_HostRawElementProvider(__RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider) +{ + A11Y_TRYBLOCK_BEGIN + + if (!aRawElmProvider) + return E_INVALIDARG; + + // This method is not used with IAccessibleEx implementations. + *aRawElmProvider = nullptr; + return S_OK; + + A11Y_TRYBLOCK_END +} diff --git a/accessible/windows/uia/uiaRawElmProvider.h b/accessible/windows/uia/uiaRawElmProvider.h new file mode 100644 index 000000000..f32daa19b --- /dev/null +++ b/accessible/windows/uia/uiaRawElmProvider.h @@ -0,0 +1,75 @@ +/* -*- 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_uiaRawElmProvider_h__ +#define mozilla_a11y_uiaRawElmProvider_h__ + +#include "objbase.h" +#include "AccessibleWrap.h" +#include "IUnknownImpl.h" +#include "uiautomation.h" + +namespace mozilla { +namespace a11y { + +class AccessibleWrap; + +/** + * IRawElementProviderSimple implementation (maintains IAccessibleEx approach). + */ +class uiaRawElmProvider final : public IAccessibleEx, + public IRawElementProviderSimple +{ +public: + uiaRawElmProvider(AccessibleWrap* aAcc) : mAcc(aAcc) { } + + // IUnknown + DECL_IUNKNOWN + + // IAccessibleEx + virtual HRESULT STDMETHODCALLTYPE GetObjectForChild( + /* [in] */ long aIdChild, + /* [retval][out] */ __RPC__deref_out_opt IAccessibleEx** aAccEx); + + virtual HRESULT STDMETHODCALLTYPE GetIAccessiblePair( + /* [out] */ __RPC__deref_out_opt IAccessible** aAcc, + /* [out] */ __RPC__out long* aIdChild); + + virtual HRESULT STDMETHODCALLTYPE GetRuntimeId( + /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRuntimeIds); + + virtual HRESULT STDMETHODCALLTYPE ConvertReturnedElement( + /* [in] */ __RPC__in_opt IRawElementProviderSimple* aRawElmProvider, + /* [out] */ __RPC__deref_out_opt IAccessibleEx** aAccEx); + + // IRawElementProviderSimple + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ProviderOptions( + /* [retval][out] */ __RPC__out enum ProviderOptions* aProviderOptions); + + virtual HRESULT STDMETHODCALLTYPE GetPatternProvider( + /* [in] */ PATTERNID aPatternId, + /* [retval][out] */ __RPC__deref_out_opt IUnknown** aPatternProvider); + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + /* [in] */ PROPERTYID aPropertyId, + /* [retval][out] */ __RPC__out VARIANT* aPropertyValue); + + virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HostRawElementProvider( + /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider); + +private: + uiaRawElmProvider() = delete; + uiaRawElmProvider& operator =(const uiaRawElmProvider&) = delete; + uiaRawElmProvider(const uiaRawElmProvider&) = delete; + +protected: + RefPtr<AccessibleWrap> mAcc; +}; + +} // a11y namespace +} // mozilla namespace + +#endif |