We assume ARIA roles are case insensitive when setting up the role map entry. In contrast, previously, when falling back to the next valid role due to the criteria for an earlier role not being met, we were using a case sensitive comparison. This resulted in infinite recursion when an invalid role contained upper case characters because we kept trying to process the ARIA role we were already processing. To fix this, use a case sensitive comparison here, making it consistent with other ARIA role checks. Original Revision: https://phabricator.services.mozilla.com/D253617 Differential Revision: https://phabricator.services.mozilla.com/D253753
1794 lines
39 KiB
C++
1794 lines
39 KiB
C++
/* -*- 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 "mozilla/a11y/Role.h"
|
|
#include "States.h"
|
|
|
|
#include "nsAttrName.h"
|
|
#include "nsGenericHTMLElement.h"
|
|
#include "nsWhitespaceTokenizer.h"
|
|
|
|
#include "mozilla/BinarySearch.h"
|
|
#include "mozilla/dom/Document.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".
|
|
*
|
|
* Note: the list must remain alphabetically ordered to support binary search.
|
|
*/
|
|
|
|
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
|
|
},
|
|
{ // definition
|
|
nsGkAtoms::definition,
|
|
roles::DEFINITION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
kGenericAccType,
|
|
},
|
|
{ // 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,
|
|
eDPub,
|
|
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,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-backlink
|
|
nsGkAtoms::docBacklink,
|
|
roles::LINK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eJumpAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::LINKED
|
|
},
|
|
{ // doc-biblioentry
|
|
nsGkAtoms::docBiblioentry,
|
|
roles::LISTITEM,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::READONLY
|
|
},
|
|
{ // doc-bibliography
|
|
nsGkAtoms::docBibliography,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-biblioref
|
|
nsGkAtoms::docBiblioref,
|
|
roles::LINK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eJumpAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::LINKED
|
|
},
|
|
{ // doc-chapter
|
|
nsGkAtoms::docChapter,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-colophon
|
|
nsGkAtoms::docColophon,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-conclusion
|
|
nsGkAtoms::docConclusion,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-cover
|
|
nsGkAtoms::docCover,
|
|
roles::GRAPHIC,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-credit
|
|
nsGkAtoms::docCredit,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-credits
|
|
nsGkAtoms::docCredits,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-dedication
|
|
nsGkAtoms::docDedication,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-endnote
|
|
nsGkAtoms::docEndnote,
|
|
roles::LISTITEM,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::READONLY
|
|
},
|
|
{ // doc-endnotes
|
|
nsGkAtoms::docEndnotes,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-epigraph
|
|
nsGkAtoms::docEpigraph,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-epilogue
|
|
nsGkAtoms::docEpilogue,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-errata
|
|
nsGkAtoms::docErrata,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-example
|
|
nsGkAtoms::docExample,
|
|
roles::FIGURE,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-footnote
|
|
nsGkAtoms::docFootnote,
|
|
roles::FOOTNOTE,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-foreword
|
|
nsGkAtoms::docForeword,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-glossary
|
|
nsGkAtoms::docGlossary,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-glossref
|
|
nsGkAtoms::docGlossref,
|
|
roles::LINK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eJumpAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::LINKED
|
|
},
|
|
{ // doc-index
|
|
nsGkAtoms::docIndex,
|
|
roles::NAVIGATION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-introduction
|
|
nsGkAtoms::docIntroduction,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-noteref
|
|
nsGkAtoms::docNoteref,
|
|
roles::LINK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eJumpAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
states::LINKED
|
|
},
|
|
{ // doc-notice
|
|
nsGkAtoms::docNotice,
|
|
roles::NOTE,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-pagebreak
|
|
nsGkAtoms::docPagebreak,
|
|
roles::SEPARATOR,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-pagefooter
|
|
nsGkAtoms::docPagefooter,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-pageheader
|
|
nsGkAtoms::docPageheader,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-pagelist
|
|
nsGkAtoms::docPagelist,
|
|
roles::NAVIGATION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-part
|
|
nsGkAtoms::docPart,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-preface
|
|
nsGkAtoms::docPreface,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-prologue
|
|
nsGkAtoms::docPrologue,
|
|
roles::LANDMARK,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-pullquote
|
|
nsGkAtoms::docPullquote,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-qna
|
|
nsGkAtoms::docQna,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-subtitle
|
|
nsGkAtoms::docSubtitle,
|
|
roles::HEADING,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-tip
|
|
nsGkAtoms::docTip,
|
|
roles::NOTE,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub,
|
|
kNoReqStates
|
|
},
|
|
{ // doc-toc
|
|
nsGkAtoms::docToc,
|
|
roles::NAVIGATION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
eDPub | eLandmark,
|
|
kNoReqStates
|
|
},
|
|
{ // document
|
|
nsGkAtoms::document,
|
|
roles::NON_NATIVE_DOCUMENT,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
kGenericAccType,
|
|
kNoReqStates,
|
|
eReadonlyUntilEditable
|
|
},
|
|
{ // emphasis
|
|
nsGkAtoms::emphasis,
|
|
roles::EMPHASIS,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
kGenericAccType,
|
|
kNoReqStates
|
|
},
|
|
{ // 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
|
|
},
|
|
{ // generic
|
|
nsGkAtoms::generic,
|
|
roles::SECTION,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
kGenericAccType,
|
|
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::GRID,
|
|
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
|
|
},
|
|
{ // image
|
|
nsGkAtoms::image,
|
|
roles::GRAPHIC,
|
|
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::ROWGROUP,
|
|
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::SEARCHBOX,
|
|
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
|
|
},
|
|
{ // strong
|
|
nsGkAtoms::strong,
|
|
roles::STRONG,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
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
|
|
},
|
|
{ // time
|
|
nsGkAtoms::time,
|
|
roles::TIME,
|
|
kUseMapRole,
|
|
eNoValue,
|
|
eNoAction,
|
|
eNoLiveAttr,
|
|
kNoReqStates
|
|
},
|
|
{ // 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 | ATTR_REFLECT_ELEMENTS },
|
|
{nsGkAtoms::aria_current, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
|
|
{nsGkAtoms::aria_describedby, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
|
|
// 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 | ATTR_REFLECT_ELEMENTS },
|
|
{nsGkAtoms::aria_disabled, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
|
|
{nsGkAtoms::aria_dropeffect, ATTR_VALTOKEN | ATTR_GLOBAL },
|
|
{nsGkAtoms::aria_errormessage, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
|
|
{nsGkAtoms::aria_expanded, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
|
|
{nsGkAtoms::aria_flowto, ATTR_BYPASSOBJ | ATTR_GLOBAL | ATTR_REFLECT_ELEMENTS },
|
|
{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 | ATTR_REFLECT_ELEMENTS },
|
|
{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 | ATTR_REFLECT_ELEMENTS },
|
|
{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::GetFirstValidRoleMapIndexExcluding(
|
|
dom::Element* aEl, std::initializer_list<nsStaticAtom*> aRolesToSkip) {
|
|
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();
|
|
|
|
// Skip any roles that we aren't interested in.
|
|
bool shouldSkip = false;
|
|
for (nsStaticAtom* atomRole : aRolesToSkip) {
|
|
if (role.Equals(atomRole->GetUTF16String(),
|
|
nsCaseInsensitiveStringComparator)) {
|
|
shouldSkip = true;
|
|
break;
|
|
}
|
|
}
|
|
if (shouldSkip) {
|
|
continue;
|
|
}
|
|
|
|
size_t idx;
|
|
auto comparator = [&role](const nsRoleMapEntry& aEntry) {
|
|
return Compare(role, aEntry.ARIARoleString(),
|
|
nsCaseInsensitiveStringComparator);
|
|
};
|
|
if (BinarySearchIf(sWAIRoleMaps, 0, std::size(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;
|
|
}
|
|
|
|
uint8_t aria::GetRoleMapIndex(dom::Element* aEl) {
|
|
// Get the rolemap index of the first valid role, excluding nothing.
|
|
return GetFirstValidRoleMapIndexExcluding(aEl, {});
|
|
}
|
|
|
|
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 < std::size(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 < std::size(gWAIUnivAttrMap); i++) {
|
|
if (gWAIUnivAttrMap[i].attributeName == aAtom) {
|
|
return gWAIUnivAttrMap[i].characteristics;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool aria::IsValidARIAHidden(nsIContent* aContent) {
|
|
return aContent && aContent->IsElement() &&
|
|
nsAccUtils::ARIAAttrValueIs(aContent->AsElement(),
|
|
nsGkAtoms::aria_hidden, nsGkAtoms::_true,
|
|
eCaseMatters) &&
|
|
!ShouldIgnoreARIAHidden(aContent);
|
|
}
|
|
|
|
bool aria::IsValidARIAHidden(DocAccessible* aDocAcc) {
|
|
nsCOMPtr<nsIContent> docContent = aDocAcc->GetContent();
|
|
// First, check if our Doc Accessible has aria-hidden set on its content
|
|
bool isValid = IsValidARIAHidden(docContent);
|
|
|
|
// If our Doc Accessible was created using an element other than the
|
|
// root element, we need to verify the validity of any aria-hidden on
|
|
// the root element as well.
|
|
auto* rootElement = aDocAcc->DocumentNode()->GetRootElement();
|
|
if (docContent != rootElement) {
|
|
isValid |= IsValidARIAHidden(rootElement);
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
bool aria::ShouldIgnoreARIAHidden(nsIContent* aContent) {
|
|
if (!aContent) {
|
|
return false;
|
|
}
|
|
|
|
dom::Document* doc = aContent->OwnerDoc();
|
|
bool isValidElementType = (aContent == doc->GetDocumentElement());
|
|
|
|
if (auto docBody = doc->GetBody()) {
|
|
isValidElementType |= (aContent == docBody->AsContent());
|
|
}
|
|
|
|
return isValidElementType && doc->IsTopLevelContentDocument();
|
|
}
|
|
|
|
const nsRoleMapEntry* aria::GetRoleMap(const nsStaticAtom* aAriaRole) {
|
|
const nsDependentAtomString role(aAriaRole);
|
|
auto comparator = [&role](const nsRoleMapEntry& aEntry) {
|
|
return Compare(role, aEntry.ARIARoleString());
|
|
};
|
|
size_t idx;
|
|
if (BinarySearchIf(sWAIRoleMaps, 0, std::size(sWAIRoleMaps), comparator,
|
|
&idx)) {
|
|
return GetRoleMapFromIndex(idx);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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(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(mAttrAtom, value)) {
|
|
aTargetAttrs->SetAttribute(mAttrAtom, std::move(value));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// AttrWithCharacteristicsIterator class
|
|
bool AttrWithCharacteristicsIterator::Next() {
|
|
for (mIdx++; mIdx < static_cast<int32_t>(std::size(gWAIUnivAttrMap));
|
|
mIdx++) {
|
|
if (gWAIUnivAttrMap[mIdx].characteristics & mCharacteristics) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsStaticAtom* AttrWithCharacteristicsIterator::AttrName() const {
|
|
return mIdx >= 0
|
|
? const_cast<nsStaticAtom*>(gWAIUnivAttrMap[mIdx].attributeName)
|
|
: nullptr;
|
|
}
|