summaryrefslogtreecommitdiffstats
path: root/accessible/base/ARIAStateMap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/base/ARIAStateMap.cpp')
-rw-r--r--accessible/base/ARIAStateMap.cpp376
1 files changed, 376 insertions, 0 deletions
diff --git a/accessible/base/ARIAStateMap.cpp b/accessible/base/ARIAStateMap.cpp
new file mode 100644
index 000000000..f121835d1
--- /dev/null
+++ b/accessible/base/ARIAStateMap.cpp
@@ -0,0 +1,376 @@
+/* -*- 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.
+ nsIAtom* const mAttrName;
+
+ // States if the attribute value is matched to the enum value. Used as
+ // nsIContent::AttrValuesArray, last item must be nullptr.
+ nsIAtom* const* 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(nsIAtom* 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.
+ nsIAtom* 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 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 eARIAReadonlyOrEditableIfDefined:
+ {
+ static const TokenTypeData data(
+ nsGkAtoms::aria_readonly, eBoolType,
+ 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 (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow) &&
+ !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext))
+ *aState |= states::MIXED;
+
+ return true;
+ }
+
+ case eFocusableUntilDisabled:
+ {
+ if (!nsAccUtils::HasDefinedARIAToken(aElement, nsGkAtoms::aria_disabled) ||
+ aElement->AttrValueIs(kNameSpaceID_None, 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 (aElement->FindAttrValueIn(kNameSpaceID_None, 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 ((aData.mType & eMixedType) &&
+ aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
+ nsGkAtoms::mixed, eCaseMatters)) {
+ *aState |= aData.mPermanentState | states::MIXED;
+ return;
+ }
+
+ if (aElement->AttrValueIs(kNameSpaceID_None, 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;
+}