diff options
Diffstat (limited to 'accessible/base/ARIAMap.cpp')
-rw-r--r-- | accessible/base/ARIAMap.cpp | 1628 |
1 files changed, 1628 insertions, 0 deletions
diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp new file mode 100644 index 0000000000..ea0dee60b9 --- /dev/null +++ b/accessible/base/ARIAMap.cpp @@ -0,0 +1,1628 @@ +/* -*- 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 "ARIAMap.h" + +#include "AccAttributes.h" +#include "nsAccUtils.h" +#include "nsCoreUtils.h" +#include "Role.h" +#include "States.h" + +#include "nsAttrName.h" +#include "nsGenericHTMLElement.h" +#include "nsWhitespaceTokenizer.h" + +#include "mozilla/BinarySearch.h" +#include "mozilla/dom/Element.h" + +#include "nsUnicharUtils.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::a11y::aria; + +static const uint32_t kGenericAccType = 0; + +/** + * This list of WAI-defined roles are currently hardcoded. + * Eventually we will most likely be loading an RDF resource that contains this + * information Using RDF will also allow for role extensibility. See bug 280138. + * + * Definition of nsRoleMapEntry contains comments explaining this table. + * + * When no Role enum mapping exists for an ARIA role, the role will be exposed + * via the object attribute "xml-roles". + */ + +static const nsRoleMapEntry sWAIRoleMaps[] = { + // clang-format off + { // alert + nsGkAtoms::alert, + roles::ALERT, + kUseMapRole, + eNoValue, + eNoAction, +#if defined(XP_MACOSX) + eAssertiveLiveAttr, +#else + eNoLiveAttr, +#endif + eAlert, + kNoReqStates + }, + { // alertdialog + nsGkAtoms::alertdialog, + roles::DIALOG, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // application + nsGkAtoms::application, + roles::APPLICATION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // article + nsGkAtoms::article, + roles::ARTICLE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eReadonlyUntilEditable + }, + { // banner + nsGkAtoms::banner, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // blockquote + nsGkAtoms::blockquote, + roles::BLOCKQUOTE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // button + nsGkAtoms::button, + roles::PUSHBUTTON, + kUseMapRole, + eNoValue, + ePressAction, + eNoLiveAttr, + eButton, + kNoReqStates + // eARIAPressed is auto applied on any button + }, + { // caption + nsGkAtoms::caption, + roles::CAPTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // cell + nsGkAtoms::cell, + roles::CELL, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eTableCell, + kNoReqStates + }, + { // checkbox + nsGkAtoms::checkbox, + roles::CHECKBUTTON, + kUseMapRole, + eNoValue, + eCheckUncheckAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIACheckableMixed, + eARIAReadonly + }, + { // code + nsGkAtoms::code, + roles::CODE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // columnheader + nsGkAtoms::columnheader, + roles::COLUMNHEADER, + kUseMapRole, + eNoValue, + eSortAction, + eNoLiveAttr, + eTableCell, + kNoReqStates, + eARIASelectableIfDefined, + eARIAReadonly + }, + { // combobox, which consists of text input and popup + nsGkAtoms::combobox, + roles::EDITCOMBOBOX, + kUseMapRole, + eNoValue, + eOpenCloseAction, + eNoLiveAttr, + eCombobox, + states::COLLAPSED | states::HASPOPUP, + eARIAAutoComplete, + eARIAReadonly, + eARIAOrientation + }, + { // comment + nsGkAtoms::comment, + roles::COMMENT, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // complementary + nsGkAtoms::complementary, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // contentinfo + nsGkAtoms::contentinfo, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // deletion + nsGkAtoms::deletion, + roles::CONTENT_DELETION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // dialog + nsGkAtoms::dialog, + roles::DIALOG, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // directory + nsGkAtoms::directory, + roles::LIST, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eList, + states::READONLY + }, + { // doc-abstract + nsGkAtoms::docAbstract, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-acknowledgments + nsGkAtoms::docAcknowledgments, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-afterword + nsGkAtoms::docAfterword, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-appendix + nsGkAtoms::docAppendix, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-backlink + nsGkAtoms::docBacklink, + roles::LINK, + kUseMapRole, + eNoValue, + eJumpAction, + eNoLiveAttr, + kGenericAccType, + states::LINKED + }, + { // doc-biblioentry + nsGkAtoms::docBiblioentry, + roles::LISTITEM, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::READONLY + }, + { // doc-bibliography + nsGkAtoms::docBibliography, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-biblioref + nsGkAtoms::docBiblioref, + roles::LINK, + kUseMapRole, + eNoValue, + eJumpAction, + eNoLiveAttr, + kGenericAccType, + states::LINKED + }, + { // doc-chapter + nsGkAtoms::docChapter, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-colophon + nsGkAtoms::docColophon, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-conclusion + nsGkAtoms::docConclusion, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-cover + nsGkAtoms::docCover, + roles::GRAPHIC, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-credit + nsGkAtoms::docCredit, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-credits + nsGkAtoms::docCredits, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-dedication + nsGkAtoms::docDedication, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-endnote + nsGkAtoms::docEndnote, + roles::LISTITEM, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::READONLY + }, + { // doc-endnotes + nsGkAtoms::docEndnotes, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-epigraph + nsGkAtoms::docEpigraph, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-epilogue + nsGkAtoms::docEpilogue, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-errata + nsGkAtoms::docErrata, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-example + nsGkAtoms::docExample, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-footnote + nsGkAtoms::docFootnote, + roles::FOOTNOTE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-foreword + nsGkAtoms::docForeword, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-glossary + nsGkAtoms::docGlossary, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-glossref + nsGkAtoms::docGlossref, + roles::LINK, + kUseMapRole, + eNoValue, + eJumpAction, + eNoLiveAttr, + kGenericAccType, + states::LINKED + }, + { // doc-index + nsGkAtoms::docIndex, + roles::NAVIGATION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-introduction + nsGkAtoms::docIntroduction, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-noteref + nsGkAtoms::docNoteref, + roles::LINK, + kUseMapRole, + eNoValue, + eJumpAction, + eNoLiveAttr, + kGenericAccType, + states::LINKED + }, + { // doc-notice + nsGkAtoms::docNotice, + roles::NOTE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-pagebreak + nsGkAtoms::docPagebreak, + roles::SEPARATOR, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-pagelist + nsGkAtoms::docPagelist, + roles::NAVIGATION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-part + nsGkAtoms::docPart, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-preface + nsGkAtoms::docPreface, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-prologue + nsGkAtoms::docPrologue, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // doc-pullquote + nsGkAtoms::docPullquote, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-qna + nsGkAtoms::docQna, + roles::SECTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-subtitle + nsGkAtoms::docSubtitle, + roles::HEADING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-tip + nsGkAtoms::docTip, + roles::NOTE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // doc-toc + nsGkAtoms::docToc, + roles::NAVIGATION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // document + nsGkAtoms::document, + roles::NON_NATIVE_DOCUMENT, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eReadonlyUntilEditable + }, + { // feed + nsGkAtoms::feed, + roles::GROUPING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // figure + nsGkAtoms::figure, + roles::FIGURE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // form + nsGkAtoms::form, + roles::FORM, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // graphics-document + nsGkAtoms::graphicsDocument, + roles::NON_NATIVE_DOCUMENT, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eReadonlyUntilEditable + }, + { // graphics-object + nsGkAtoms::graphicsObject, + roles::GROUPING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // graphics-symbol + nsGkAtoms::graphicsSymbol, + roles::GRAPHIC, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // grid + nsGkAtoms::grid, + roles::TABLE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eSelect | eTable, + kNoReqStates, + eARIAMultiSelectable, + eARIAReadonly, + eFocusableUntilDisabled + }, + { // gridcell + nsGkAtoms::gridcell, + roles::GRID_CELL, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eTableCell, + kNoReqStates, + eARIASelectable, + eARIAReadonly + }, + { // group + nsGkAtoms::group, + roles::GROUPING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // heading + nsGkAtoms::heading, + roles::HEADING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // img + nsGkAtoms::img, + roles::GRAPHIC, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // insertion + nsGkAtoms::insertion, + roles::CONTENT_INSERTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // key + nsGkAtoms::key, + roles::KEY, + kUseMapRole, + eNoValue, + ePressAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIAPressed + }, + { // link + nsGkAtoms::link, + roles::LINK, + kUseMapRole, + eNoValue, + eJumpAction, + eNoLiveAttr, + kGenericAccType, + states::LINKED + }, + { // list + nsGkAtoms::list_, + roles::LIST, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eList, + states::READONLY + }, + { // listbox + nsGkAtoms::listbox, + roles::LISTBOX, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eListControl | eSelect, + states::VERTICAL, + eARIAMultiSelectable, + eARIAReadonly, + eFocusableUntilDisabled, + eARIAOrientation + }, + { // listitem + nsGkAtoms::listitem, + roles::LISTITEM, + kUseMapRole, + eNoValue, + eNoAction, // XXX: should depend on state, parent accessible + eNoLiveAttr, + kGenericAccType, + states::READONLY + }, + { // log + nsGkAtoms::log_, + roles::NOTHING, + kUseNativeRole, + eNoValue, + eNoAction, + ePoliteLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // main + nsGkAtoms::main, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // mark + nsGkAtoms::mark, + roles::MARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // marquee + nsGkAtoms::marquee, + roles::ANIMATION, + kUseMapRole, + eNoValue, + eNoAction, + eOffLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // math + nsGkAtoms::math, + roles::FLAT_EQUATION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // menu + nsGkAtoms::menu, + roles::MENUPOPUP, + kUseMapRole, + eNoValue, + eNoAction, // XXX: technically accessibles of menupopup role haven't + // any action, but menu can be open or close. + eNoLiveAttr, + kGenericAccType, + states::VERTICAL, + eARIAOrientation + }, + { // menubar + nsGkAtoms::menubar, + roles::MENUBAR, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::HORIZONTAL, + eARIAOrientation + }, + { // menuitem + nsGkAtoms::menuitem, + roles::MENUITEM, + kUseMapRole, + eNoValue, + eClickAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // menuitemcheckbox + nsGkAtoms::menuitemcheckbox, + roles::CHECK_MENU_ITEM, + kUseMapRole, + eNoValue, + eClickAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIACheckableMixed, + eARIAReadonly + }, + { // menuitemradio + nsGkAtoms::menuitemradio, + roles::RADIO_MENU_ITEM, + kUseMapRole, + eNoValue, + eClickAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIACheckableBool, + eARIAReadonly + }, + { // meter + nsGkAtoms::meter, + roles::METER, + kUseMapRole, + eHasValueMinMax, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::READONLY + }, + { // navigation + nsGkAtoms::navigation, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // none + nsGkAtoms::none, + roles::NOTHING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // note + nsGkAtoms::note_, + roles::NOTE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // option + nsGkAtoms::option, + roles::OPTION, + kUseMapRole, + eNoValue, + eSelectAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIASelectable, + eARIACheckedMixed + }, + { // paragraph + nsGkAtoms::paragraph, + roles::PARAGRAPH, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // presentation + nsGkAtoms::presentation, + roles::NOTHING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // progressbar + nsGkAtoms::progressbar, + roles::PROGRESSBAR, + kUseMapRole, + eHasValueMinMax, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::READONLY, + eIndeterminateIfNoValue + }, + { // radio + nsGkAtoms::radio, + roles::RADIOBUTTON, + kUseMapRole, + eNoValue, + eSelectAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIACheckableBool + }, + { // radiogroup + nsGkAtoms::radiogroup, + roles::RADIO_GROUP, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIAOrientation, + eARIAReadonly + }, + { // region + nsGkAtoms::region, + roles::REGION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // row + nsGkAtoms::row, + roles::ROW, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eTableRow, + kNoReqStates, + eARIASelectable + }, + { // rowgroup + nsGkAtoms::rowgroup, + roles::GROUPING, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // rowheader + nsGkAtoms::rowheader, + roles::ROWHEADER, + kUseMapRole, + eNoValue, + eSortAction, + eNoLiveAttr, + eTableCell, + kNoReqStates, + eARIASelectableIfDefined, + eARIAReadonly + }, + { // scrollbar + nsGkAtoms::scrollbar, + roles::SCROLLBAR, + kUseMapRole, + eHasValueMinMax, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::VERTICAL, + eARIAOrientation, + eARIAReadonly + }, + { // search + nsGkAtoms::search, + roles::LANDMARK, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eLandmark, + kNoReqStates + }, + { // searchbox + nsGkAtoms::searchbox, + roles::ENTRY, + kUseMapRole, + eNoValue, + eActivateAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIAAutoComplete, + eARIAMultiline, + eARIAReadonlyOrEditable + }, + { // separator + nsGkAtoms::separator_, + roles::SEPARATOR, + kUseMapRole, + eHasValueMinMaxIfFocusable, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::HORIZONTAL, + eARIAOrientation + }, + { // slider + nsGkAtoms::slider, + roles::SLIDER, + kUseMapRole, + eHasValueMinMax, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::HORIZONTAL, + eARIAOrientation, + eARIAReadonly + }, + { // spinbutton + nsGkAtoms::spinbutton, + roles::SPINBUTTON, + kUseMapRole, + eHasValueMinMax, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIAReadonly + }, + { // status + nsGkAtoms::status, + roles::STATUSBAR, + kUseMapRole, + eNoValue, + eNoAction, + ePoliteLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // subscript + nsGkAtoms::subscript, + roles::SUBSCRIPT, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType + }, + { // suggestion + nsGkAtoms::suggestion, + roles::SUGGESTION, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + }, + { // superscript + nsGkAtoms::superscript, + roles::SUPERSCRIPT, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType + }, + { // switch + nsGkAtoms::svgSwitch, + roles::SWITCH, + kUseMapRole, + eNoValue, + eCheckUncheckAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIACheckableBool, + eARIAReadonly + }, + { // tab + nsGkAtoms::tab, + roles::PAGETAB, + kUseMapRole, + eNoValue, + eSwitchAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIASelectable + }, + { // table + nsGkAtoms::table, + roles::TABLE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eTable, + kNoReqStates, + eARIASelectable + }, + { // tablist + nsGkAtoms::tablist, + roles::PAGETABLIST, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eSelect, + states::HORIZONTAL, + eARIAOrientation, + eARIAMultiSelectable + }, + { // tabpanel + nsGkAtoms::tabpanel, + roles::PROPERTYPAGE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // term + nsGkAtoms::term, + roles::TERM, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::READONLY + }, + { // textbox + nsGkAtoms::textbox, + roles::ENTRY, + kUseMapRole, + eNoValue, + eActivateAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIAAutoComplete, + eARIAMultiline, + eARIAReadonlyOrEditable + }, + { // timer + nsGkAtoms::timer, + roles::NOTHING, + kUseNativeRole, + eNoValue, + eNoAction, + eOffLiveAttr, + kNoReqStates + }, + { // toolbar + nsGkAtoms::toolbar, + roles::TOOLBAR, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + states::HORIZONTAL, + eARIAOrientation + }, + { // tooltip + nsGkAtoms::tooltip, + roles::TOOLTIP, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + kGenericAccType, + kNoReqStates + }, + { // tree + nsGkAtoms::tree, + roles::OUTLINE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eSelect, + states::VERTICAL, + eARIAReadonly, + eARIAMultiSelectable, + eFocusableUntilDisabled, + eARIAOrientation + }, + { // treegrid + nsGkAtoms::treegrid, + roles::TREE_TABLE, + kUseMapRole, + eNoValue, + eNoAction, + eNoLiveAttr, + eSelect | eTable, + kNoReqStates, + eARIAReadonly, + eARIAMultiSelectable, + eFocusableUntilDisabled, + eARIAOrientation + }, + { // treeitem + nsGkAtoms::treeitem, + roles::OUTLINEITEM, + kUseMapRole, + eNoValue, + eActivateAction, // XXX: should expose second 'expand/collapse' action based + // on states + eNoLiveAttr, + kGenericAccType, + kNoReqStates, + eARIASelectable, + eARIACheckedMixed + } + // clang-format on +}; + +static const nsRoleMapEntry sLandmarkRoleMap = { + nsGkAtoms::_empty, roles::NOTHING, kUseNativeRole, eNoValue, + eNoAction, eNoLiveAttr, kGenericAccType, kNoReqStates}; + +nsRoleMapEntry aria::gEmptyRoleMap = { + nsGkAtoms::_empty, roles::TEXT_CONTAINER, kUseMapRole, eNoValue, + eNoAction, eNoLiveAttr, kGenericAccType, kNoReqStates}; + +/** + * Universal (Global) states: + * The following state rules are applied to any accessible element, + * whether there is an ARIA role or not: + */ +static const EStateRule sWAIUnivStateMap[] = { + eARIABusy, eARIACurrent, eARIADisabled, + eARIAExpanded, // Currently under spec review but precedent exists + eARIAHasPopup, // Note this is a tokenised attribute starting in ARIA 1.1 + eARIAInvalid, eARIAModal, + eARIARequired, // XXX not global, Bug 553117 + eARIANone}; + +/** + * ARIA attribute map for attribute characteristics. + * @note ARIA attributes that don't have any flags are not included here. + */ + +struct AttrCharacteristics { + const nsStaticAtom* const attributeName; + const uint8_t characteristics; +}; + +static const AttrCharacteristics gWAIUnivAttrMap[] = { + // clang-format off + {nsGkAtoms::aria_activedescendant, ATTR_BYPASSOBJ }, + {nsGkAtoms::aria_atomic, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_busy, ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_checked, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, /* exposes checkable obj attr */ + {nsGkAtoms::aria_colcount, ATTR_VALINT }, + {nsGkAtoms::aria_colindex, ATTR_VALINT }, + {nsGkAtoms::aria_controls, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_current, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_describedby, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + // XXX Ideally, aria-description shouldn't expose a description object + // attribute (i.e. it should have ATTR_BYPASSOBJ). However, until the + // description-from attribute is implemented (bug 1726087), clients such as + // NVDA depend on the description object attribute to work out whether the + // accDescription originated from aria-description. + {nsGkAtoms::aria_description, ATTR_GLOBAL }, + {nsGkAtoms::aria_details, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_disabled, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_dropeffect, ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_errormessage, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_expanded, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_flowto, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_grabbed, ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_haspopup, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_hidden, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */ + {nsGkAtoms::aria_invalid, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_label, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_labelledby, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_level, ATTR_BYPASSOBJ }, /* handled via groupPosition */ + {nsGkAtoms::aria_live, ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_modal, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, + {nsGkAtoms::aria_multiline, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_multiselectable, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_owns, ATTR_BYPASSOBJ | ATTR_GLOBAL }, + {nsGkAtoms::aria_orientation, ATTR_VALTOKEN }, + {nsGkAtoms::aria_posinset, ATTR_BYPASSOBJ }, /* handled via groupPosition */ + {nsGkAtoms::aria_pressed, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_readonly, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_relevant, ATTR_GLOBAL }, + {nsGkAtoms::aria_required, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_rowcount, ATTR_VALINT }, + {nsGkAtoms::aria_rowindex, ATTR_VALINT }, + {nsGkAtoms::aria_selected, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, + {nsGkAtoms::aria_setsize, ATTR_BYPASSOBJ }, /* handled via groupPosition */ + {nsGkAtoms::aria_sort, ATTR_VALTOKEN }, + {nsGkAtoms::aria_valuenow, ATTR_BYPASSOBJ }, + {nsGkAtoms::aria_valuemin, ATTR_BYPASSOBJ }, + {nsGkAtoms::aria_valuemax, ATTR_BYPASSOBJ }, + {nsGkAtoms::aria_valuetext, ATTR_BYPASSOBJ } + // clang-format on +}; + +const nsRoleMapEntry* aria::GetRoleMap(dom::Element* aEl) { + return GetRoleMapFromIndex(GetRoleMapIndex(aEl)); +} + +uint8_t aria::GetRoleMapIndex(dom::Element* aEl) { + nsAutoString roles; + if (!aEl || !nsAccUtils::GetARIAAttr(aEl, nsGkAtoms::role, roles) || + roles.IsEmpty()) { + // We treat role="" as if the role attribute is absent (per aria spec:8.1.1) + return NO_ROLE_MAP_ENTRY_INDEX; + } + + nsWhitespaceTokenizer tokenizer(roles); + while (tokenizer.hasMoreTokens()) { + // Do a binary search through table for the next role in role list + const nsDependentSubstring role = tokenizer.nextToken(); + size_t idx; + auto comparator = [&role](const nsRoleMapEntry& aEntry) { + return Compare(role, aEntry.ARIARoleString(), + nsCaseInsensitiveStringComparator); + }; + if (BinarySearchIf(sWAIRoleMaps, 0, ArrayLength(sWAIRoleMaps), comparator, + &idx)) { + return idx; + } + } + + // Always use some entry index if there is a non-empty role string + // To ensure an accessible object is created + return LANDMARK_ROLE_MAP_ENTRY_INDEX; +} + +const nsRoleMapEntry* aria::GetRoleMapFromIndex(uint8_t aRoleMapIndex) { + switch (aRoleMapIndex) { + case NO_ROLE_MAP_ENTRY_INDEX: + return nullptr; + case EMPTY_ROLE_MAP_ENTRY_INDEX: + return &gEmptyRoleMap; + case LANDMARK_ROLE_MAP_ENTRY_INDEX: + return &sLandmarkRoleMap; + default: + return sWAIRoleMaps + aRoleMapIndex; + } +} + +uint8_t aria::GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMapEntry) { + if (aRoleMapEntry == nullptr) { + return NO_ROLE_MAP_ENTRY_INDEX; + } else if (aRoleMapEntry == &gEmptyRoleMap) { + return EMPTY_ROLE_MAP_ENTRY_INDEX; + } else if (aRoleMapEntry == &sLandmarkRoleMap) { + return LANDMARK_ROLE_MAP_ENTRY_INDEX; + } else { + uint8_t index = aRoleMapEntry - sWAIRoleMaps; + MOZ_ASSERT(aria::IsRoleMapIndexValid(index)); + return index; + } +} + +bool aria::IsRoleMapIndexValid(uint8_t aRoleMapIndex) { + switch (aRoleMapIndex) { + case NO_ROLE_MAP_ENTRY_INDEX: + case EMPTY_ROLE_MAP_ENTRY_INDEX: + case LANDMARK_ROLE_MAP_ENTRY_INDEX: + return true; + } + return aRoleMapIndex < ArrayLength(sWAIRoleMaps); +} + +uint64_t aria::UniversalStatesFor(mozilla::dom::Element* aElement) { + uint64_t state = 0; + uint32_t index = 0; + while (MapToState(sWAIUnivStateMap[index], aElement, &state)) index++; + + return state; +} + +uint8_t aria::AttrCharacteristicsFor(nsAtom* aAtom) { + for (uint32_t i = 0; i < ArrayLength(gWAIUnivAttrMap); i++) { + if (gWAIUnivAttrMap[i].attributeName == aAtom) { + return gWAIUnivAttrMap[i].characteristics; + } + } + + return 0; +} + +bool aria::HasDefinedARIAHidden(nsIContent* aContent) { + return aContent && aContent->IsElement() && + nsAccUtils::ARIAAttrValueIs(aContent->AsElement(), + nsGkAtoms::aria_hidden, nsGkAtoms::_true, + eCaseMatters); +} + +//////////////////////////////////////////////////////////////////////////////// +// AttrIterator class + +AttrIterator::AttrIterator(nsIContent* aContent) + : mElement(dom::Element::FromNode(aContent)), + mIteratingDefaults(false), + mAttrIdx(0), + mAttrCharacteristics(0) { + mAttrs = mElement ? &mElement->GetAttrs() : nullptr; + mAttrCount = mAttrs ? mAttrs->AttrCount() : 0; +} + +bool AttrIterator::Next() { + while (mAttrIdx < mAttrCount) { + const nsAttrName* attr = mAttrs->GetSafeAttrNameAt(mAttrIdx); + mAttrIdx++; + if (attr->NamespaceEquals(kNameSpaceID_None)) { + mAttrAtom = attr->Atom(); + nsDependentAtomString attrStr(mAttrAtom); + if (!StringBeginsWith(attrStr, u"aria-"_ns)) continue; // Not ARIA + + if (mIteratingDefaults) { + if (mOverriddenAttrs.Contains(mAttrAtom)) { + continue; + } + } else { + mOverriddenAttrs.Insert(mAttrAtom); + } + + // AttrCharacteristicsFor has to search for the entry, so cache it here + // rather than having to search again later. + mAttrCharacteristics = aria::AttrCharacteristicsFor(mAttrAtom); + if (mAttrCharacteristics & ATTR_BYPASSOBJ) { + continue; // No need to handle exposing as obj attribute here + } + + if ((mAttrCharacteristics & ATTR_VALTOKEN) && + !nsAccUtils::HasDefinedARIAToken(mAttrs, mAttrAtom)) { + continue; // only expose token based attributes if they are defined + } + + if ((mAttrCharacteristics & ATTR_BYPASSOBJ_IF_FALSE) && + mAttrs->AttrValueIs(kNameSpaceID_None, mAttrAtom, nsGkAtoms::_false, + eCaseMatters)) { + continue; // only expose token based attribute if value is not 'false'. + } + + return true; + } + } + + mAttrCharacteristics = 0; + mAttrAtom = nullptr; + + if (const auto* defaults = nsAccUtils::GetARIADefaults(mElement); + !mIteratingDefaults && defaults) { + mIteratingDefaults = true; + mAttrs = defaults; + mAttrCount = mAttrs->AttrCount(); + mAttrIdx = 0; + return Next(); + } + + return false; +} + +nsAtom* AttrIterator::AttrName() const { return mAttrAtom; } + +void AttrIterator::AttrValue(nsAString& aAttrValue) const { + nsAutoString value; + if (mAttrs->GetAttr(kNameSpaceID_None, mAttrAtom, value)) { + if (mAttrCharacteristics & ATTR_VALTOKEN) { + nsAtom* normalizedValue = + nsAccUtils::NormalizeARIAToken(mAttrs, mAttrAtom); + if (normalizedValue) { + nsDependentAtomString normalizedValueStr(normalizedValue); + aAttrValue.Assign(normalizedValueStr); + return; + } + } + aAttrValue.Assign(value); + } +} + +bool AttrIterator::ExposeAttr(AccAttributes* aTargetAttrs) const { + if (mAttrCharacteristics & ATTR_VALTOKEN) { + nsAtom* normalizedValue = nsAccUtils::NormalizeARIAToken(mAttrs, mAttrAtom); + if (normalizedValue) { + aTargetAttrs->SetAttribute(mAttrAtom, normalizedValue); + return true; + } + } else if (mAttrCharacteristics & ATTR_VALINT) { + int32_t intVal; + if (nsCoreUtils::GetUIntAttrValue(mAttrs->GetAttr(mAttrAtom), &intVal)) { + aTargetAttrs->SetAttribute(mAttrAtom, intVal); + return true; + } + if (mAttrAtom == nsGkAtoms::aria_colcount || + mAttrAtom == nsGkAtoms::aria_rowcount) { + // These attributes allow a value of -1. + if (mAttrs->AttrValueIs(kNameSpaceID_None, mAttrAtom, u"-1"_ns, + eCaseMatters)) { + aTargetAttrs->SetAttribute(mAttrAtom, -1); + return true; + } + } + return false; // Invalid value. + } + nsAutoString value; + if (mAttrs->GetAttr(kNameSpaceID_None, mAttrAtom, value)) { + aTargetAttrs->SetAttribute(mAttrAtom, std::move(value)); + return true; + } + return false; +} |