diff options
Diffstat (limited to 'accessible/aom')
-rw-r--r-- | accessible/aom/AccessibleNode.cpp | 170 | ||||
-rw-r--r-- | accessible/aom/AccessibleNode.h | 212 | ||||
-rw-r--r-- | accessible/aom/moz.build | 44 |
3 files changed, 426 insertions, 0 deletions
diff --git a/accessible/aom/AccessibleNode.cpp b/accessible/aom/AccessibleNode.cpp new file mode 100644 index 0000000000..2aff34238c --- /dev/null +++ b/accessible/aom/AccessibleNode.cpp @@ -0,0 +1,170 @@ +/* -*- 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 "AccessibleNode.h" +#include "mozilla/dom/AccessibleNodeBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/StaticPrefs_accessibility.h" +#include "nsContentUtils.h" + +#include "LocalAccessible-inl.h" +#include "nsAccessibilityService.h" +#include "DocAccessible.h" + +#include "mozilla/dom/ToJSValue.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::dom; + +bool AccessibleNode::IsAOMEnabled(JSContext* aCx, JSObject* /*unused*/) { + return nsContentUtils::IsSystemCaller(aCx) || + StaticPrefs::accessibility_AOM_enabled(); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AccessibleNode, mRelationProperties, + mIntl, mDOMNode, mStates) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AccessibleNode) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AccessibleNode) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AccessibleNode) + +AccessibleNode::AccessibleNode(nsINode* aNode) + : mDoubleProperties(3), + mIntProperties(3), + mUIntProperties(6), + mBooleanProperties(0), + mRelationProperties(3), + mStringProperties(16), + mDOMNode(aNode) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!accService) { + return; + } + + DocAccessible* doc = accService->GetDocAccessible(mDOMNode->OwnerDoc()); + if (doc) { + mIntl = doc->GetAccessible(mDOMNode); + } +} + +AccessibleNode::~AccessibleNode() {} + +/* virtual */ +JSObject* AccessibleNode::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return AccessibleNode_Binding::Wrap(aCx, this, aGivenProto); +} + +/* virtual */ +ParentObject AccessibleNode::GetParentObject() const { + return mDOMNode->GetParentObject(); +} + +void AccessibleNode::GetComputedRole(nsAString& aRole) { + if (mIntl) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (accService) { + accService->GetStringRole(mIntl->Role(), aRole); + return; + } + } + + aRole.AssignLiteral("unknown"); +} + +void AccessibleNode::GetStates(nsTArray<nsString>& aStates) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!mIntl || !accService) { + aStates.AppendElement(u"defunct"_ns); + return; + } + + if (mStates) { + aStates = mStates->StringArray().Clone(); + return; + } + + mStates = accService->GetStringStates(mIntl->State()); + aStates = mStates->StringArray().Clone(); +} + +void AccessibleNode::GetAttributes(nsTArray<nsString>& aAttributes) { + if (!mIntl) { + return; + } + + RefPtr<AccAttributes> attrs = mIntl->Attributes(); + + for (const auto& iter : *attrs) { + aAttributes.AppendElement(nsAtomString(iter.Name())); + } +} + +bool AccessibleNode::Is(const Sequence<nsString>& aFlavors) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!mIntl || !accService) { + for (const auto& flavor : aFlavors) { + if (!flavor.EqualsLiteral("unknown") && + !flavor.EqualsLiteral("defunct")) { + return false; + } + } + return true; + } + + nsAutoString role; + accService->GetStringRole(mIntl->Role(), role); + + if (!mStates) { + mStates = accService->GetStringStates(mIntl->State()); + } + + for (const auto& flavor : aFlavors) { + if (!flavor.Equals(role) && !mStates->Contains(flavor)) { + return false; + } + } + return true; +} + +bool AccessibleNode::Has(const Sequence<nsString>& aAttributes) { + if (!mIntl) { + return false; + } + RefPtr<AccAttributes> attrs = mIntl->Attributes(); + for (const auto& attr : aAttributes) { + RefPtr<nsAtom> attrAtom = NS_Atomize(attr); + if (!attrs->HasAttribute(attrAtom)) { + return false; + } + } + return true; +} + +void AccessibleNode::Get(JSContext* aCX, const nsAString& aAttribute, + JS::MutableHandle<JS::Value> aValue, + ErrorResult& aRv) { + if (!mIntl) { + aRv.ThrowInvalidStateError("No attributes available"); + return; + } + + RefPtr<nsAtom> attrAtom = NS_Atomize(aAttribute); + RefPtr<AccAttributes> attrs = mIntl->Attributes(); + nsAutoString valueStr; + attrs->GetAttribute(attrAtom, valueStr); + if (!ToJSValue(aCX, valueStr, aValue)) { + aRv.NoteJSContextException(aCX); + return; + } +} + +nsINode* AccessibleNode::GetDOMNode() { return mDOMNode; } diff --git a/accessible/aom/AccessibleNode.h b/accessible/aom/AccessibleNode.h new file mode 100644 index 0000000000..e9b328b13d --- /dev/null +++ b/accessible/aom/AccessibleNode.h @@ -0,0 +1,212 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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 A11Y_AOM_ACCESSIBLENODE_H +#define A11Y_AOM_ACCESSIBLENODE_H + +#include "nsTHashMap.h" +#include "nsRefPtrHashtable.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMString.h" +#include "mozilla/dom/Nullable.h" + +class nsINode; + +namespace mozilla { + +class ErrorResult; + +namespace a11y { +class LocalAccessible; +} + +namespace dom { + +class DOMStringList; +struct ParentObject; + +#define ANODE_ENUM(name) e##name, + +#define ANODE_FUNC(typeName, type, name) \ + dom::Nullable<type> Get##name() { \ + return GetProperty(AOM##typeName##Property::e##name); \ + } \ + \ + void Set##name(const dom::Nullable<type>& a##name) { \ + SetProperty(AOM##typeName##Property::e##name, a##name); \ + } + +#define ANODE_STRING_FUNC(name) \ + void Get##name(nsAString& a##name) { \ + return GetProperty(AOMStringProperty::e##name, a##name); \ + } \ + \ + void Set##name(const nsAString& a##name) { \ + SetProperty(AOMStringProperty::e##name, a##name); \ + } + +#define ANODE_RELATION_FUNC(name) \ + already_AddRefed<AccessibleNode> Get##name() { \ + return GetProperty(AOMRelationProperty::e##name); \ + } \ + \ + void Set##name(AccessibleNode* a##name) { \ + SetProperty(AOMRelationProperty::e##name, a##name); \ + } + +#define ANODE_PROPS(typeName, type, ...) \ + enum class AOM##typeName##Property{ \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__))}; \ + MOZ_FOR_EACH(ANODE_FUNC, (typeName, type, ), (__VA_ARGS__)) + +#define ANODE_STRING_PROPS(...) \ + enum class AOMStringProperty { \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ + }; \ + MOZ_FOR_EACH(ANODE_STRING_FUNC, (), (__VA_ARGS__)) + +#define ANODE_RELATION_PROPS(...) \ + enum class AOMRelationProperty { \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ + }; \ + MOZ_FOR_EACH(ANODE_RELATION_FUNC, (), (__VA_ARGS__)) + +#define ANODE_ACCESSOR_MUTATOR(typeName, type, defVal) \ + nsTHashMap<nsUint32HashKey, type> m##typeName##Properties; \ + \ + dom::Nullable<type> GetProperty(AOM##typeName##Property aProperty) { \ + type value = defVal; \ + if (m##typeName##Properties.Get(static_cast<int>(aProperty), &value)) { \ + return dom::Nullable<type>(value); \ + } \ + return dom::Nullable<type>(); \ + } \ + \ + void SetProperty(AOM##typeName##Property aProperty, \ + const dom::Nullable<type>& aValue) { \ + if (aValue.IsNull()) { \ + m##typeName##Properties.Remove(static_cast<int>(aProperty)); \ + } else { \ + m##typeName##Properties.InsertOrUpdate(static_cast<int>(aProperty), \ + aValue.Value()); \ + } \ + } + +class AccessibleNode : public nsISupports, public nsWrapperCache { + public: + explicit AccessibleNode(nsINode* aNode); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS; + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AccessibleNode); + + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final; + dom::ParentObject GetParentObject() const; + + void GetComputedRole(nsAString& aRole); + void GetStates(nsTArray<nsString>& aStates); + void GetAttributes(nsTArray<nsString>& aAttributes); + nsINode* GetDOMNode(); + + bool Is(const Sequence<nsString>& aFlavors); + bool Has(const Sequence<nsString>& aAttributes); + void Get(JSContext* cx, const nsAString& aAttribute, + JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv); + + static bool IsAOMEnabled(JSContext*, JSObject*); + + ANODE_STRING_PROPS(Autocomplete, Checked, Current, HasPopUp, Invalid, + KeyShortcuts, Label, Live, Orientation, Placeholder, + Pressed, Relevant, Role, RoleDescription, Sort, ValueText) + + ANODE_PROPS(Boolean, bool, Atomic, Busy, Disabled, Expanded, Hidden, Modal, + Multiline, Multiselectable, ReadOnly, Required, Selected) + + ANODE_PROPS(UInt, uint32_t, ColIndex, ColSpan, Level, PosInSet, RowIndex, + RowSpan) + + ANODE_PROPS(Int, int32_t, ColCount, RowCount, SetSize) + + ANODE_PROPS(Double, double, ValueMax, ValueMin, ValueNow) + + ANODE_RELATION_PROPS(ActiveDescendant, Details, ErrorMessage) + + protected: + AccessibleNode(const AccessibleNode& aCopy) = delete; + AccessibleNode& operator=(const AccessibleNode& aCopy) = delete; + virtual ~AccessibleNode(); + + void GetProperty(AOMStringProperty aProperty, nsAString& aRetval) { + nsString data; + if (!mStringProperties.Get(static_cast<int>(aProperty), &data)) { + SetDOMStringToNull(data); + } + aRetval = data; + } + + void SetProperty(AOMStringProperty aProperty, const nsAString& aValue) { + if (DOMStringIsNull(aValue)) { + mStringProperties.Remove(static_cast<int>(aProperty)); + } else { + nsString value(aValue); + mStringProperties.InsertOrUpdate(static_cast<int>(aProperty), value); + } + } + + dom::Nullable<bool> GetProperty(AOMBooleanProperty aProperty) { + int num = static_cast<int>(aProperty); + if (mBooleanProperties & (1U << (2 * num))) { + bool data = static_cast<bool>(mBooleanProperties & (1U << (2 * num + 1))); + return dom::Nullable<bool>(data); + } + return dom::Nullable<bool>(); + } + + void SetProperty(AOMBooleanProperty aProperty, + const dom::Nullable<bool>& aValue) { + int num = static_cast<int>(aProperty); + if (aValue.IsNull()) { + mBooleanProperties &= ~(1U << (2 * num)); + } else { + mBooleanProperties |= (1U << (2 * num)); + mBooleanProperties = + (aValue.Value() ? mBooleanProperties | (1U << (2 * num + 1)) + : mBooleanProperties & ~(1U << (2 * num + 1))); + } + } + + ANODE_ACCESSOR_MUTATOR(Double, double, 0.0) + ANODE_ACCESSOR_MUTATOR(Int, int32_t, 0) + ANODE_ACCESSOR_MUTATOR(UInt, uint32_t, 0) + + already_AddRefed<AccessibleNode> GetProperty(AOMRelationProperty aProperty) { + return mRelationProperties.Get(static_cast<int>(aProperty)); + } + + void SetProperty(AOMRelationProperty aProperty, AccessibleNode* aValue) { + if (!aValue) { + mRelationProperties.Remove(static_cast<int>(aProperty)); + } else { + mRelationProperties.InsertOrUpdate(static_cast<int>(aProperty), + RefPtr{aValue}); + } + } + + // The 2k'th bit indicates whether the k'th boolean property is used(1) or + // not(0) and 2k+1'th bit contains the property's value(1:true, 0:false) + uint32_t mBooleanProperties; + nsRefPtrHashtable<nsUint32HashKey, AccessibleNode> mRelationProperties; + nsTHashMap<nsUint32HashKey, nsString> mStringProperties; + + RefPtr<a11y::LocalAccessible> mIntl; + RefPtr<nsINode> mDOMNode; + RefPtr<dom::DOMStringList> mStates; +}; + +} // namespace dom +} // namespace mozilla + +#endif // A11Y_JSAPI_ACCESSIBLENODE diff --git a/accessible/aom/moz.build b/accessible/aom/moz.build new file mode 100644 index 0000000000..88b941435e --- /dev/null +++ b/accessible/aom/moz.build @@ -0,0 +1,44 @@ +# -*- 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.mozilla.dom += [ + "AccessibleNode.h", +] + +UNIFIED_SOURCES += [ + "AccessibleNode.cpp", +] + +LOCAL_INCLUDES += [ + "/accessible/base", + "/accessible/generic", +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + LOCAL_INCLUDES += [ + "/accessible/atk", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + LOCAL_INCLUDES += [ + "/accessible/windows/ia2", + "/accessible/windows/msaa", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + LOCAL_INCLUDES += [ + "/accessible/mac", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + LOCAL_INCLUDES += [ + "/accessible/android", + ] +else: + LOCAL_INCLUDES += [ + "/accessible/other", + ] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" |