summaryrefslogtreecommitdiffstats
path: root/accessible/base/ARIAStateMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--accessible/base/ARIAStateMap.cpp334
1 files changed, 334 insertions, 0 deletions
diff --git a/accessible/base/ARIAStateMap.cpp b/accessible/base/ARIAStateMap.cpp
new file mode 100644
index 0000000000..6bf20cf1cc
--- /dev/null
+++ b/accessible/base/ARIAStateMap.cpp
@@ -0,0 +1,334 @@
+/* -*- 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 "ARIAMap.h"
+#include "nsAccUtils.h"
+#include "States.h"
+
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+using namespace mozilla::a11y::aria;
+
+/**
+ * Used to store state map rule data for ARIA attribute of enum type.
+ */
+struct EnumTypeData {
+ // ARIA attribute name.
+ nsStaticAtom* const mAttrName;
+
+ // States if the attribute value is matched to the enum value. Used as
+ // Element::AttrValuesArray, last item must be nullptr.
+ nsStaticAtom* const mValues[4];
+
+ // States applied if corresponding enum values are matched.
+ const uint64_t mStates[3];
+
+ // States to clear in case of match.
+ const uint64_t mClearState;
+};
+
+enum ETokenType {
+ eBoolType = 0,
+ eMixedType = 1, // can take 'mixed' value
+ eDefinedIfAbsent = 2 // permanent and false state are applied if absent
+};
+
+/**
+ * Used to store state map rule data for ARIA attribute of token type (including
+ * mixed value).
+ */
+struct TokenTypeData {
+ TokenTypeData(nsAtom* aAttrName, uint32_t aType, uint64_t aPermanentState,
+ uint64_t aTrueState, uint64_t aFalseState = 0)
+ : mAttrName(aAttrName),
+ mType(aType),
+ mPermanentState(aPermanentState),
+ mTrueState(aTrueState),
+ mFalseState(aFalseState) {}
+
+ // ARIA attribute name.
+ nsAtom* const mAttrName;
+
+ // Type.
+ const uint32_t mType;
+
+ // State applied if the attribute is defined or mType doesn't have
+ // eDefinedIfAbsent flag set.
+ const uint64_t mPermanentState;
+
+ // States applied if the attribute value is true/false.
+ const uint64_t mTrueState;
+ const uint64_t mFalseState;
+};
+
+/**
+ * Map enum type attribute value to accessible state.
+ */
+static void MapEnumType(dom::Element* aElement, uint64_t* aState,
+ const EnumTypeData& aData);
+
+/**
+ * Map token type attribute value to states.
+ */
+static void MapTokenType(dom::Element* aContent, uint64_t* aState,
+ const TokenTypeData& aData);
+
+bool aria::MapToState(EStateRule aRule, dom::Element* aElement,
+ uint64_t* aState) {
+ switch (aRule) {
+ case eARIAAutoComplete: {
+ static const EnumTypeData data = {
+ nsGkAtoms::aria_autocomplete,
+ {nsGkAtoms::inlinevalue, nsGkAtoms::list_, nsGkAtoms::both, nullptr},
+ {states::SUPPORTS_AUTOCOMPLETION,
+ states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION,
+ states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION},
+ 0};
+
+ MapEnumType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIABusy: {
+ static const EnumTypeData data = {
+ nsGkAtoms::aria_busy,
+ {nsGkAtoms::_true, nsGkAtoms::error, nullptr},
+ {states::BUSY, states::INVALID},
+ 0};
+
+ MapEnumType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIACheckableBool: {
+ static const TokenTypeData data(nsGkAtoms::aria_checked,
+ eBoolType | eDefinedIfAbsent,
+ states::CHECKABLE, states::CHECKED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIACheckableMixed: {
+ static const TokenTypeData data(nsGkAtoms::aria_checked,
+ eMixedType | eDefinedIfAbsent,
+ states::CHECKABLE, states::CHECKED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIACheckedMixed: {
+ static const TokenTypeData data(nsGkAtoms::aria_checked, eMixedType,
+ states::CHECKABLE, states::CHECKED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIACurrent: {
+ static const TokenTypeData data(nsGkAtoms::aria_current, eBoolType, 0,
+ states::CURRENT);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIADisabled: {
+ static const TokenTypeData data(nsGkAtoms::aria_disabled, eBoolType, 0,
+ states::UNAVAILABLE);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAExpanded: {
+ static const TokenTypeData data(nsGkAtoms::aria_expanded, eBoolType, 0,
+ states::EXPANDED, states::COLLAPSED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAHasPopup: {
+ static const TokenTypeData data(nsGkAtoms::aria_haspopup, eBoolType, 0,
+ states::HASPOPUP);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAInvalid: {
+ static const TokenTypeData data(nsGkAtoms::aria_invalid, eBoolType, 0,
+ states::INVALID);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAModal: {
+ static const TokenTypeData data(nsGkAtoms::aria_modal, eBoolType, 0,
+ states::MODAL);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAMultiline: {
+ static const TokenTypeData data(nsGkAtoms::aria_multiline,
+ eBoolType | eDefinedIfAbsent, 0,
+ states::MULTI_LINE, states::SINGLE_LINE);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAMultiSelectable: {
+ static const TokenTypeData data(
+ nsGkAtoms::aria_multiselectable, eBoolType, 0,
+ states::MULTISELECTABLE | states::EXTSELECTABLE);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAOrientation: {
+ static const EnumTypeData data = {
+ nsGkAtoms::aria_orientation,
+ {nsGkAtoms::horizontal, nsGkAtoms::vertical, nullptr},
+ {states::HORIZONTAL, states::VERTICAL},
+ states::HORIZONTAL | states::VERTICAL};
+
+ MapEnumType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAPressed: {
+ static const TokenTypeData data(nsGkAtoms::aria_pressed, eMixedType, 0,
+ states::PRESSED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAReadonly: {
+ static const TokenTypeData data(nsGkAtoms::aria_readonly, eBoolType, 0,
+ states::READONLY);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIAReadonlyOrEditable: {
+ static const TokenTypeData data(nsGkAtoms::aria_readonly,
+ eBoolType | eDefinedIfAbsent, 0,
+ states::READONLY, states::EDITABLE);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIARequired: {
+ static const TokenTypeData data(nsGkAtoms::aria_required, eBoolType, 0,
+ states::REQUIRED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIASelectable: {
+ static const TokenTypeData data(nsGkAtoms::aria_selected,
+ eBoolType | eDefinedIfAbsent,
+ states::SELECTABLE, states::SELECTED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eARIASelectableIfDefined: {
+ static const TokenTypeData data(nsGkAtoms::aria_selected, eBoolType,
+ states::SELECTABLE, states::SELECTED);
+
+ MapTokenType(aElement, aState, data);
+ return true;
+ }
+
+ case eReadonlyUntilEditable: {
+ if (!(*aState & states::EDITABLE)) *aState |= states::READONLY;
+
+ return true;
+ }
+
+ case eIndeterminateIfNoValue: {
+ if (!nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuenow) &&
+ !nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuetext)) {
+ *aState |= states::MIXED;
+ }
+
+ return true;
+ }
+
+ case eFocusableUntilDisabled: {
+ if (!nsAccUtils::HasDefinedARIAToken(aElement,
+ nsGkAtoms::aria_disabled) ||
+ nsAccUtils::ARIAAttrValueIs(aElement, nsGkAtoms::aria_disabled,
+ nsGkAtoms::_false, eCaseMatters)) {
+ *aState |= states::FOCUSABLE;
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+static void MapEnumType(dom::Element* aElement, uint64_t* aState,
+ const EnumTypeData& aData) {
+ switch (nsAccUtils::FindARIAAttrValueIn(aElement, aData.mAttrName,
+ aData.mValues, eCaseMatters)) {
+ case 0:
+ *aState = (*aState & ~aData.mClearState) | aData.mStates[0];
+ return;
+ case 1:
+ *aState = (*aState & ~aData.mClearState) | aData.mStates[1];
+ return;
+ case 2:
+ *aState = (*aState & ~aData.mClearState) | aData.mStates[2];
+ return;
+ }
+}
+
+static void MapTokenType(dom::Element* aElement, uint64_t* aState,
+ const TokenTypeData& aData) {
+ if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
+ if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName, nsGkAtoms::mixed,
+ eCaseMatters)) {
+ if (aData.mType & eMixedType) {
+ *aState |= aData.mPermanentState | states::MIXED;
+ } else { // unsupported use of 'mixed' is an authoring error
+ *aState |= aData.mPermanentState | aData.mFalseState;
+ }
+ return;
+ }
+
+ if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName,
+ nsGkAtoms::_false, eCaseMatters)) {
+ *aState |= aData.mPermanentState | aData.mFalseState;
+ return;
+ }
+
+ *aState |= aData.mPermanentState | aData.mTrueState;
+ return;
+ }
+
+ if (aData.mType & eDefinedIfAbsent) {
+ *aState |= aData.mPermanentState | aData.mFalseState;
+ }
+}