1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=2:tabstop=2:
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 #include "ARIAMap.h"
9 
10 #include "nsAccUtils.h"
11 #include "nsCoreUtils.h"
12 #include "Role.h"
13 #include "States.h"
14 
15 #include "nsAttrName.h"
16 #include "nsWhitespaceTokenizer.h"
17 
18 #include "mozilla/BinarySearch.h"
19 #include "mozilla/dom/Element.h"
20 
21 #include "nsUnicharUtils.h"
22 
23 using namespace mozilla;
24 using namespace mozilla::a11y;
25 using namespace mozilla::a11y::aria;
26 
27 static const uint32_t kGenericAccType = 0;
28 
29 /**
30  *  This list of WAI-defined roles are currently hardcoded.
31  *  Eventually we will most likely be loading an RDF resource that contains this
32  * information Using RDF will also allow for role extensibility. See bug 280138.
33  *
34  *  Definition of nsRoleMapEntry contains comments explaining this table.
35  *
36  *  When no Role enum mapping exists for an ARIA role, the role will be exposed
37  *  via the object attribute "xml-roles".
38  */
39 
40 static const nsRoleMapEntry sWAIRoleMaps[] = {
41     // clang-format off
42   { // alert
43     nsGkAtoms::alert,
44     roles::ALERT,
45     kUseMapRole,
46     eNoValue,
47     eNoAction,
48 #if defined(XP_MACOSX)
49     eAssertiveLiveAttr,
50 #else
51     eNoLiveAttr,
52 #endif
53     eAlert,
54     kNoReqStates
55   },
56   { // alertdialog
57     nsGkAtoms::alertdialog,
58     roles::DIALOG,
59     kUseMapRole,
60     eNoValue,
61     eNoAction,
62     eNoLiveAttr,
63     kGenericAccType,
64     kNoReqStates
65   },
66   { // application
67     nsGkAtoms::application,
68     roles::APPLICATION,
69     kUseMapRole,
70     eNoValue,
71     eNoAction,
72     eNoLiveAttr,
73     eLandmark,
74     kNoReqStates
75   },
76   { // article
77     nsGkAtoms::article,
78     roles::ARTICLE,
79     kUseMapRole,
80     eNoValue,
81     eNoAction,
82     eNoLiveAttr,
83     kGenericAccType,
84     kNoReqStates,
85     eReadonlyUntilEditable
86   },
87   { // banner
88     nsGkAtoms::banner,
89     roles::LANDMARK,
90     kUseMapRole,
91     eNoValue,
92     eNoAction,
93     eNoLiveAttr,
94     eLandmark,
95     kNoReqStates
96   },
97   { // blockquote
98     nsGkAtoms::blockquote,
99     roles::BLOCKQUOTE,
100     kUseMapRole,
101     eNoValue,
102     eNoAction,
103     eNoLiveAttr,
104     kGenericAccType,
105   },
106   { // button
107     nsGkAtoms::button,
108     roles::PUSHBUTTON,
109     kUseMapRole,
110     eNoValue,
111     ePressAction,
112     eNoLiveAttr,
113     eButton,
114     kNoReqStates
115     // eARIAPressed is auto applied on any button
116   },
117   { // caption
118     nsGkAtoms::caption,
119     roles::CAPTION,
120     kUseMapRole,
121     eNoValue,
122     eNoAction,
123     eNoLiveAttr,
124     kGenericAccType,
125   },
126   { // cell
127     nsGkAtoms::cell,
128     roles::CELL,
129     kUseMapRole,
130     eNoValue,
131     eNoAction,
132     eNoLiveAttr,
133     eTableCell,
134     kNoReqStates
135   },
136   { // checkbox
137     nsGkAtoms::checkbox,
138     roles::CHECKBUTTON,
139     kUseMapRole,
140     eNoValue,
141     eCheckUncheckAction,
142     eNoLiveAttr,
143     kGenericAccType,
144     kNoReqStates,
145     eARIACheckableMixed,
146     eARIAReadonly
147   },
148   { // code
149     nsGkAtoms::code,
150     roles::CODE,
151     kUseMapRole,
152     eNoValue,
153     eNoAction,
154     eNoLiveAttr,
155     kGenericAccType,
156   },
157   { // columnheader
158     nsGkAtoms::columnheader,
159     roles::COLUMNHEADER,
160     kUseMapRole,
161     eNoValue,
162     eSortAction,
163     eNoLiveAttr,
164     eTableCell,
165     kNoReqStates,
166     eARIASelectableIfDefined,
167     eARIAReadonly
168   },
169   { // combobox, which consists of text input and popup
170     nsGkAtoms::combobox,
171     roles::EDITCOMBOBOX,
172     kUseMapRole,
173     eNoValue,
174     eOpenCloseAction,
175     eNoLiveAttr,
176     eCombobox,
177     states::COLLAPSED | states::HASPOPUP,
178     eARIAAutoComplete,
179     eARIAReadonly,
180     eARIAOrientation
181   },
182   { // comment
183     nsGkAtoms::comment,
184     roles::COMMENT,
185     kUseMapRole,
186     eNoValue,
187     eNoAction,
188     eNoLiveAttr,
189     kGenericAccType,
190   },
191   { // complementary
192     nsGkAtoms::complementary,
193     roles::LANDMARK,
194     kUseMapRole,
195     eNoValue,
196     eNoAction,
197     eNoLiveAttr,
198     eLandmark,
199     kNoReqStates
200   },
201   { // contentinfo
202     nsGkAtoms::contentinfo,
203     roles::LANDMARK,
204     kUseMapRole,
205     eNoValue,
206     eNoAction,
207     eNoLiveAttr,
208     eLandmark,
209     kNoReqStates
210   },
211   { // deletion
212     nsGkAtoms::deletion,
213     roles::CONTENT_DELETION,
214     kUseMapRole,
215     eNoValue,
216     eNoAction,
217     eNoLiveAttr,
218     kGenericAccType,
219   },
220   { // dialog
221     nsGkAtoms::dialog,
222     roles::DIALOG,
223     kUseMapRole,
224     eNoValue,
225     eNoAction,
226     eNoLiveAttr,
227     kGenericAccType,
228     kNoReqStates
229   },
230   { // directory
231     nsGkAtoms::directory,
232     roles::LIST,
233     kUseMapRole,
234     eNoValue,
235     eNoAction,
236     eNoLiveAttr,
237     eList,
238     states::READONLY
239   },
240   { // doc-abstract
241     nsGkAtoms::docAbstract,
242     roles::SECTION,
243     kUseMapRole,
244     eNoValue,
245     eNoAction,
246     eNoLiveAttr,
247     kGenericAccType,
248     kNoReqStates
249   },
250   { // doc-acknowledgments
251     nsGkAtoms::docAcknowledgments,
252     roles::LANDMARK,
253     kUseMapRole,
254     eNoValue,
255     eNoAction,
256     eNoLiveAttr,
257     eLandmark,
258     kNoReqStates
259   },
260   { // doc-afterword
261     nsGkAtoms::docAfterword,
262     roles::LANDMARK,
263     kUseMapRole,
264     eNoValue,
265     eNoAction,
266     eNoLiveAttr,
267     eLandmark,
268     kNoReqStates
269   },
270   { // doc-appendix
271     nsGkAtoms::docAppendix,
272     roles::LANDMARK,
273     kUseMapRole,
274     eNoValue,
275     eNoAction,
276     eNoLiveAttr,
277     eLandmark,
278     kNoReqStates
279   },
280   { // doc-backlink
281     nsGkAtoms::docBacklink,
282     roles::LINK,
283     kUseMapRole,
284     eNoValue,
285     eJumpAction,
286     eNoLiveAttr,
287     kGenericAccType,
288     states::LINKED
289   },
290   { // doc-biblioentry
291     nsGkAtoms::docBiblioentry,
292     roles::LISTITEM,
293     kUseMapRole,
294     eNoValue,
295     eNoAction,
296     eNoLiveAttr,
297     kGenericAccType,
298     states::READONLY
299   },
300   { // doc-bibliography
301     nsGkAtoms::docBibliography,
302     roles::LANDMARK,
303     kUseMapRole,
304     eNoValue,
305     eNoAction,
306     eNoLiveAttr,
307     eLandmark,
308     kNoReqStates
309   },
310   { // doc-biblioref
311     nsGkAtoms::docBiblioref,
312     roles::LINK,
313     kUseMapRole,
314     eNoValue,
315     eJumpAction,
316     eNoLiveAttr,
317     kGenericAccType,
318     states::LINKED
319   },
320   { // doc-chapter
321     nsGkAtoms::docChapter,
322     roles::LANDMARK,
323     kUseMapRole,
324     eNoValue,
325     eNoAction,
326     eNoLiveAttr,
327     eLandmark,
328     kNoReqStates
329   },
330   { // doc-colophon
331     nsGkAtoms::docColophon,
332     roles::SECTION,
333     kUseMapRole,
334     eNoValue,
335     eNoAction,
336     eNoLiveAttr,
337     kGenericAccType,
338     kNoReqStates
339   },
340   { // doc-conclusion
341     nsGkAtoms::docConclusion,
342     roles::LANDMARK,
343     kUseMapRole,
344     eNoValue,
345     eNoAction,
346     eNoLiveAttr,
347     eLandmark,
348     kNoReqStates
349   },
350   { // doc-cover
351     nsGkAtoms::docCover,
352     roles::GRAPHIC,
353     kUseMapRole,
354     eNoValue,
355     eNoAction,
356     eNoLiveAttr,
357     kGenericAccType,
358     kNoReqStates
359   },
360   { // doc-credit
361     nsGkAtoms::docCredit,
362     roles::SECTION,
363     kUseMapRole,
364     eNoValue,
365     eNoAction,
366     eNoLiveAttr,
367     kGenericAccType,
368     kNoReqStates
369   },
370   { // doc-credits
371     nsGkAtoms::docCredits,
372     roles::LANDMARK,
373     kUseMapRole,
374     eNoValue,
375     eNoAction,
376     eNoLiveAttr,
377     eLandmark,
378     kNoReqStates
379   },
380   { // doc-dedication
381     nsGkAtoms::docDedication,
382     roles::SECTION,
383     kUseMapRole,
384     eNoValue,
385     eNoAction,
386     eNoLiveAttr,
387     kGenericAccType,
388     kNoReqStates
389   },
390   { // doc-endnote
391     nsGkAtoms::docEndnote,
392     roles::LISTITEM,
393     kUseMapRole,
394     eNoValue,
395     eNoAction,
396     eNoLiveAttr,
397     kGenericAccType,
398     states::READONLY
399   },
400   { // doc-endnotes
401     nsGkAtoms::docEndnotes,
402     roles::LANDMARK,
403     kUseMapRole,
404     eNoValue,
405     eNoAction,
406     eNoLiveAttr,
407     eLandmark,
408     kNoReqStates
409   },
410   { // doc-epigraph
411     nsGkAtoms::docEpigraph,
412     roles::SECTION,
413     kUseMapRole,
414     eNoValue,
415     eNoAction,
416     eNoLiveAttr,
417     kGenericAccType,
418     kNoReqStates
419   },
420   { // doc-epilogue
421     nsGkAtoms::docEpilogue,
422     roles::LANDMARK,
423     kUseMapRole,
424     eNoValue,
425     eNoAction,
426     eNoLiveAttr,
427     eLandmark,
428     kNoReqStates
429   },
430   { // doc-errata
431     nsGkAtoms::docErrata,
432     roles::LANDMARK,
433     kUseMapRole,
434     eNoValue,
435     eNoAction,
436     eNoLiveAttr,
437     eLandmark,
438     kNoReqStates
439   },
440   { // doc-example
441     nsGkAtoms::docExample,
442     roles::SECTION,
443     kUseMapRole,
444     eNoValue,
445     eNoAction,
446     eNoLiveAttr,
447     kGenericAccType,
448     kNoReqStates
449   },
450   { // doc-footnote
451     nsGkAtoms::docFootnote,
452     roles::FOOTNOTE,
453     kUseMapRole,
454     eNoValue,
455     eNoAction,
456     eNoLiveAttr,
457     eLandmark,
458     kNoReqStates
459   },
460   { // doc-foreword
461     nsGkAtoms::docForeword,
462     roles::LANDMARK,
463     kUseMapRole,
464     eNoValue,
465     eNoAction,
466     eNoLiveAttr,
467     eLandmark,
468     kNoReqStates
469   },
470   { // doc-glossary
471     nsGkAtoms::docGlossary,
472     roles::LANDMARK,
473     kUseMapRole,
474     eNoValue,
475     eNoAction,
476     eNoLiveAttr,
477     eLandmark,
478     kNoReqStates
479   },
480   { // doc-glossref
481     nsGkAtoms::docGlossref,
482     roles::LINK,
483     kUseMapRole,
484     eNoValue,
485     eJumpAction,
486     eNoLiveAttr,
487     kGenericAccType,
488     states::LINKED
489   },
490   { // doc-index
491     nsGkAtoms::docIndex,
492     roles::NAVIGATION,
493     kUseMapRole,
494     eNoValue,
495     eNoAction,
496     eNoLiveAttr,
497     eLandmark,
498     kNoReqStates
499   },
500   { // doc-introduction
501     nsGkAtoms::docIntroduction,
502     roles::LANDMARK,
503     kUseMapRole,
504     eNoValue,
505     eNoAction,
506     eNoLiveAttr,
507     eLandmark,
508     kNoReqStates
509   },
510   { // doc-noteref
511     nsGkAtoms::docNoteref,
512     roles::LINK,
513     kUseMapRole,
514     eNoValue,
515     eJumpAction,
516     eNoLiveAttr,
517     kGenericAccType,
518     states::LINKED
519   },
520   { // doc-notice
521     nsGkAtoms::docNotice,
522     roles::NOTE,
523     kUseMapRole,
524     eNoValue,
525     eNoAction,
526     eNoLiveAttr,
527     kGenericAccType,
528     kNoReqStates
529   },
530   { // doc-pagebreak
531     nsGkAtoms::docPagebreak,
532     roles::SEPARATOR,
533     kUseMapRole,
534     eNoValue,
535     eNoAction,
536     eNoLiveAttr,
537     kGenericAccType,
538     kNoReqStates
539   },
540   { // doc-pagelist
541     nsGkAtoms::docPagelist,
542     roles::NAVIGATION,
543     kUseMapRole,
544     eNoValue,
545     eNoAction,
546     eNoLiveAttr,
547     eLandmark,
548     kNoReqStates
549   },
550   { // doc-part
551     nsGkAtoms::docPart,
552     roles::LANDMARK,
553     kUseMapRole,
554     eNoValue,
555     eNoAction,
556     eNoLiveAttr,
557     eLandmark,
558     kNoReqStates
559   },
560   { // doc-preface
561     nsGkAtoms::docPreface,
562     roles::LANDMARK,
563     kUseMapRole,
564     eNoValue,
565     eNoAction,
566     eNoLiveAttr,
567     eLandmark,
568     kNoReqStates
569   },
570   { // doc-prologue
571     nsGkAtoms::docPrologue,
572     roles::LANDMARK,
573     kUseMapRole,
574     eNoValue,
575     eNoAction,
576     eNoLiveAttr,
577     eLandmark,
578     kNoReqStates
579   },
580   { // doc-pullquote
581     nsGkAtoms::docPullquote,
582     roles::SECTION,
583     kUseMapRole,
584     eNoValue,
585     eNoAction,
586     eNoLiveAttr,
587     kGenericAccType,
588     kNoReqStates
589   },
590   { // doc-qna
591     nsGkAtoms::docQna,
592     roles::SECTION,
593     kUseMapRole,
594     eNoValue,
595     eNoAction,
596     eNoLiveAttr,
597     kGenericAccType,
598     kNoReqStates
599   },
600   { // doc-subtitle
601     nsGkAtoms::docSubtitle,
602     roles::HEADING,
603     kUseMapRole,
604     eNoValue,
605     eNoAction,
606     eNoLiveAttr,
607     kGenericAccType,
608     kNoReqStates
609   },
610   { // doc-tip
611     nsGkAtoms::docTip,
612     roles::NOTE,
613     kUseMapRole,
614     eNoValue,
615     eNoAction,
616     eNoLiveAttr,
617     kGenericAccType,
618     kNoReqStates
619   },
620   { // doc-toc
621     nsGkAtoms::docToc,
622     roles::NAVIGATION,
623     kUseMapRole,
624     eNoValue,
625     eNoAction,
626     eNoLiveAttr,
627     eLandmark,
628     kNoReqStates
629   },
630   { // document
631     nsGkAtoms::document,
632     roles::NON_NATIVE_DOCUMENT,
633     kUseMapRole,
634     eNoValue,
635     eNoAction,
636     eNoLiveAttr,
637     kGenericAccType,
638     kNoReqStates,
639     eReadonlyUntilEditable
640   },
641   { // feed
642     nsGkAtoms::feed,
643     roles::GROUPING,
644     kUseMapRole,
645     eNoValue,
646     eNoAction,
647     eNoLiveAttr,
648     kGenericAccType,
649     kNoReqStates
650   },
651   { // figure
652     nsGkAtoms::figure,
653     roles::FIGURE,
654     kUseMapRole,
655     eNoValue,
656     eNoAction,
657     eNoLiveAttr,
658     kGenericAccType,
659     kNoReqStates
660   },
661   { // form
662     nsGkAtoms::form,
663     roles::FORM,
664     kUseMapRole,
665     eNoValue,
666     eNoAction,
667     eNoLiveAttr,
668     eLandmark,
669     kNoReqStates
670   },
671   { // graphics-document
672     nsGkAtoms::graphicsDocument,
673     roles::NON_NATIVE_DOCUMENT,
674     kUseMapRole,
675     eNoValue,
676     eNoAction,
677     eNoLiveAttr,
678     kGenericAccType,
679     kNoReqStates,
680     eReadonlyUntilEditable
681   },
682   { // graphics-object
683     nsGkAtoms::graphicsObject,
684     roles::GROUPING,
685     kUseMapRole,
686     eNoValue,
687     eNoAction,
688     eNoLiveAttr,
689     kGenericAccType,
690     kNoReqStates
691   },
692   { // graphics-symbol
693     nsGkAtoms::graphicsSymbol,
694     roles::GRAPHIC,
695     kUseMapRole,
696     eNoValue,
697     eNoAction,
698     eNoLiveAttr,
699     kGenericAccType,
700     kNoReqStates
701   },
702   { // grid
703     nsGkAtoms::grid,
704     roles::TABLE,
705     kUseMapRole,
706     eNoValue,
707     eNoAction,
708     eNoLiveAttr,
709     eSelect | eTable,
710     kNoReqStates,
711     eARIAMultiSelectable,
712     eARIAReadonly,
713     eFocusableUntilDisabled
714   },
715   { // gridcell
716     nsGkAtoms::gridcell,
717     roles::GRID_CELL,
718     kUseMapRole,
719     eNoValue,
720     eNoAction,
721     eNoLiveAttr,
722     eTableCell,
723     kNoReqStates,
724     eARIASelectable,
725     eARIAReadonly
726   },
727   { // group
728     nsGkAtoms::group,
729     roles::GROUPING,
730     kUseMapRole,
731     eNoValue,
732     eNoAction,
733     eNoLiveAttr,
734     kGenericAccType,
735     kNoReqStates
736   },
737   { // heading
738     nsGkAtoms::heading,
739     roles::HEADING,
740     kUseMapRole,
741     eNoValue,
742     eNoAction,
743     eNoLiveAttr,
744     kGenericAccType,
745     kNoReqStates
746   },
747   { // img
748     nsGkAtoms::img,
749     roles::GRAPHIC,
750     kUseMapRole,
751     eNoValue,
752     eNoAction,
753     eNoLiveAttr,
754     kGenericAccType,
755     kNoReqStates
756   },
757   { // insertion
758     nsGkAtoms::insertion,
759     roles::CONTENT_INSERTION,
760     kUseMapRole,
761     eNoValue,
762     eNoAction,
763     eNoLiveAttr,
764     kGenericAccType,
765   },
766   { // key
767     nsGkAtoms::key,
768     roles::KEY,
769     kUseMapRole,
770     eNoValue,
771     ePressAction,
772     eNoLiveAttr,
773     kGenericAccType,
774     kNoReqStates,
775     eARIAPressed
776   },
777   { // link
778     nsGkAtoms::link,
779     roles::LINK,
780     kUseMapRole,
781     eNoValue,
782     eJumpAction,
783     eNoLiveAttr,
784     kGenericAccType,
785     states::LINKED
786   },
787   { // list
788     nsGkAtoms::list_,
789     roles::LIST,
790     kUseMapRole,
791     eNoValue,
792     eNoAction,
793     eNoLiveAttr,
794     eList,
795     states::READONLY
796   },
797   { // listbox
798     nsGkAtoms::listbox,
799     roles::LISTBOX,
800     kUseMapRole,
801     eNoValue,
802     eNoAction,
803     eNoLiveAttr,
804     eListControl | eSelect,
805     states::VERTICAL,
806     eARIAMultiSelectable,
807     eARIAReadonly,
808     eFocusableUntilDisabled,
809     eARIAOrientation
810   },
811   { // listitem
812     nsGkAtoms::listitem,
813     roles::LISTITEM,
814     kUseMapRole,
815     eNoValue,
816     eNoAction, // XXX: should depend on state, parent accessible
817     eNoLiveAttr,
818     kGenericAccType,
819     states::READONLY
820   },
821   { // log
822     nsGkAtoms::log_,
823     roles::NOTHING,
824     kUseNativeRole,
825     eNoValue,
826     eNoAction,
827     ePoliteLiveAttr,
828     kGenericAccType,
829     kNoReqStates
830   },
831   { // main
832     nsGkAtoms::main,
833     roles::LANDMARK,
834     kUseMapRole,
835     eNoValue,
836     eNoAction,
837     eNoLiveAttr,
838     eLandmark,
839     kNoReqStates
840   },
841   { // mark
842     nsGkAtoms::mark,
843     roles::MARK,
844     kUseMapRole,
845     eNoValue,
846     eNoAction,
847     eNoLiveAttr,
848     kGenericAccType,
849   },
850   { // marquee
851     nsGkAtoms::marquee,
852     roles::ANIMATION,
853     kUseMapRole,
854     eNoValue,
855     eNoAction,
856     eOffLiveAttr,
857     kGenericAccType,
858     kNoReqStates
859   },
860   { // math
861     nsGkAtoms::math,
862     roles::FLAT_EQUATION,
863     kUseMapRole,
864     eNoValue,
865     eNoAction,
866     eNoLiveAttr,
867     kGenericAccType,
868     kNoReqStates
869   },
870   { // menu
871     nsGkAtoms::menu,
872     roles::MENUPOPUP,
873     kUseMapRole,
874     eNoValue,
875     eNoAction, // XXX: technically accessibles of menupopup role haven't
876                // any action, but menu can be open or close.
877     eNoLiveAttr,
878     kGenericAccType,
879     states::VERTICAL,
880     eARIAOrientation
881   },
882   { // menubar
883     nsGkAtoms::menubar,
884     roles::MENUBAR,
885     kUseMapRole,
886     eNoValue,
887     eNoAction,
888     eNoLiveAttr,
889     kGenericAccType,
890     states::HORIZONTAL,
891     eARIAOrientation
892   },
893   { // menuitem
894     nsGkAtoms::menuitem,
895     roles::MENUITEM,
896     kUseMapRole,
897     eNoValue,
898     eClickAction,
899     eNoLiveAttr,
900     kGenericAccType,
901     kNoReqStates
902   },
903   { // menuitemcheckbox
904     nsGkAtoms::menuitemcheckbox,
905     roles::CHECK_MENU_ITEM,
906     kUseMapRole,
907     eNoValue,
908     eClickAction,
909     eNoLiveAttr,
910     kGenericAccType,
911     kNoReqStates,
912     eARIACheckableMixed,
913     eARIAReadonly
914   },
915   { // menuitemradio
916     nsGkAtoms::menuitemradio,
917     roles::RADIO_MENU_ITEM,
918     kUseMapRole,
919     eNoValue,
920     eClickAction,
921     eNoLiveAttr,
922     kGenericAccType,
923     kNoReqStates,
924     eARIACheckableBool,
925     eARIAReadonly
926   },
927   { // navigation
928     nsGkAtoms::navigation,
929     roles::LANDMARK,
930     kUseMapRole,
931     eNoValue,
932     eNoAction,
933     eNoLiveAttr,
934     eLandmark,
935     kNoReqStates
936   },
937   { // none
938     nsGkAtoms::none,
939     roles::NOTHING,
940     kUseMapRole,
941     eNoValue,
942     eNoAction,
943     eNoLiveAttr,
944     kGenericAccType,
945     kNoReqStates
946   },
947   { // note
948     nsGkAtoms::note_,
949     roles::NOTE,
950     kUseMapRole,
951     eNoValue,
952     eNoAction,
953     eNoLiveAttr,
954     kGenericAccType,
955     kNoReqStates
956   },
957   { // option
958     nsGkAtoms::option,
959     roles::OPTION,
960     kUseMapRole,
961     eNoValue,
962     eSelectAction,
963     eNoLiveAttr,
964     kGenericAccType,
965     kNoReqStates,
966     eARIASelectable,
967     eARIACheckedMixed
968   },
969   { // paragraph
970     nsGkAtoms::paragraph,
971     roles::PARAGRAPH,
972     kUseMapRole,
973     eNoValue,
974     eNoAction,
975     eNoLiveAttr,
976     kGenericAccType,
977   },
978   { // presentation
979     nsGkAtoms::presentation,
980     roles::NOTHING,
981     kUseMapRole,
982     eNoValue,
983     eNoAction,
984     eNoLiveAttr,
985     kGenericAccType,
986     kNoReqStates
987   },
988   { // progressbar
989     nsGkAtoms::progressbar,
990     roles::PROGRESSBAR,
991     kUseMapRole,
992     eHasValueMinMax,
993     eNoAction,
994     eNoLiveAttr,
995     kGenericAccType,
996     states::READONLY,
997     eIndeterminateIfNoValue
998   },
999   { // radio
1000     nsGkAtoms::radio,
1001     roles::RADIOBUTTON,
1002     kUseMapRole,
1003     eNoValue,
1004     eSelectAction,
1005     eNoLiveAttr,
1006     kGenericAccType,
1007     kNoReqStates,
1008     eARIACheckableBool
1009   },
1010   { // radiogroup
1011     nsGkAtoms::radiogroup,
1012     roles::RADIO_GROUP,
1013     kUseMapRole,
1014     eNoValue,
1015     eNoAction,
1016     eNoLiveAttr,
1017     kGenericAccType,
1018     kNoReqStates,
1019     eARIAOrientation,
1020     eARIAReadonly
1021   },
1022   { // region
1023     nsGkAtoms::region,
1024     roles::REGION,
1025     kUseMapRole,
1026     eNoValue,
1027     eNoAction,
1028     eNoLiveAttr,
1029     eLandmark,
1030     kNoReqStates
1031   },
1032   { // row
1033     nsGkAtoms::row,
1034     roles::ROW,
1035     kUseMapRole,
1036     eNoValue,
1037     eNoAction,
1038     eNoLiveAttr,
1039     eTableRow,
1040     kNoReqStates,
1041     eARIASelectable
1042   },
1043   { // rowgroup
1044     nsGkAtoms::rowgroup,
1045     roles::GROUPING,
1046     kUseMapRole,
1047     eNoValue,
1048     eNoAction,
1049     eNoLiveAttr,
1050     kGenericAccType,
1051     kNoReqStates
1052   },
1053   { // rowheader
1054     nsGkAtoms::rowheader,
1055     roles::ROWHEADER,
1056     kUseMapRole,
1057     eNoValue,
1058     eSortAction,
1059     eNoLiveAttr,
1060     eTableCell,
1061     kNoReqStates,
1062     eARIASelectableIfDefined,
1063     eARIAReadonly
1064   },
1065   { // scrollbar
1066     nsGkAtoms::scrollbar,
1067     roles::SCROLLBAR,
1068     kUseMapRole,
1069     eHasValueMinMax,
1070     eNoAction,
1071     eNoLiveAttr,
1072     kGenericAccType,
1073     states::VERTICAL,
1074     eARIAOrientation,
1075     eARIAReadonly
1076   },
1077   { // search
1078     nsGkAtoms::search,
1079     roles::LANDMARK,
1080     kUseMapRole,
1081     eNoValue,
1082     eNoAction,
1083     eNoLiveAttr,
1084     eLandmark,
1085     kNoReqStates
1086   },
1087   { // searchbox
1088     nsGkAtoms::searchbox,
1089     roles::ENTRY,
1090     kUseMapRole,
1091     eNoValue,
1092     eActivateAction,
1093     eNoLiveAttr,
1094     kGenericAccType,
1095     kNoReqStates,
1096     eARIAAutoComplete,
1097     eARIAMultiline,
1098     eARIAReadonlyOrEditable
1099   },
1100   { // separator
1101     nsGkAtoms::separator_,
1102     roles::SEPARATOR,
1103     kUseMapRole,
1104     eHasValueMinMaxIfFocusable,
1105     eNoAction,
1106     eNoLiveAttr,
1107     kGenericAccType,
1108     states::HORIZONTAL,
1109     eARIAOrientation
1110   },
1111   { // slider
1112     nsGkAtoms::slider,
1113     roles::SLIDER,
1114     kUseMapRole,
1115     eHasValueMinMax,
1116     eNoAction,
1117     eNoLiveAttr,
1118     kGenericAccType,
1119     states::HORIZONTAL,
1120     eARIAOrientation,
1121     eARIAReadonly
1122   },
1123   { // spinbutton
1124     nsGkAtoms::spinbutton,
1125     roles::SPINBUTTON,
1126     kUseMapRole,
1127     eHasValueMinMax,
1128     eNoAction,
1129     eNoLiveAttr,
1130     kGenericAccType,
1131     kNoReqStates,
1132     eARIAReadonly
1133   },
1134   { // status
1135     nsGkAtoms::status,
1136     roles::STATUSBAR,
1137     kUseMapRole,
1138     eNoValue,
1139     eNoAction,
1140     ePoliteLiveAttr,
1141     kGenericAccType,
1142     kNoReqStates
1143   },
1144   { // suggestion
1145     nsGkAtoms::suggestion,
1146     roles::SUGGESTION,
1147     kUseMapRole,
1148     eNoValue,
1149     eNoAction,
1150     eNoLiveAttr,
1151     kGenericAccType,
1152   },
1153   { // switch
1154     nsGkAtoms::svgSwitch,
1155     roles::SWITCH,
1156     kUseMapRole,
1157     eNoValue,
1158     eCheckUncheckAction,
1159     eNoLiveAttr,
1160     kGenericAccType,
1161     kNoReqStates,
1162     eARIACheckableBool,
1163     eARIAReadonly
1164   },
1165   { // tab
1166     nsGkAtoms::tab,
1167     roles::PAGETAB,
1168     kUseMapRole,
1169     eNoValue,
1170     eSwitchAction,
1171     eNoLiveAttr,
1172     kGenericAccType,
1173     kNoReqStates,
1174     eARIASelectable
1175   },
1176   { // table
1177     nsGkAtoms::table,
1178     roles::TABLE,
1179     kUseMapRole,
1180     eNoValue,
1181     eNoAction,
1182     eNoLiveAttr,
1183     eTable,
1184     kNoReqStates,
1185     eARIASelectable
1186   },
1187   { // tablist
1188     nsGkAtoms::tablist,
1189     roles::PAGETABLIST,
1190     kUseMapRole,
1191     eNoValue,
1192     eNoAction,
1193     eNoLiveAttr,
1194     eSelect,
1195     states::HORIZONTAL,
1196     eARIAOrientation,
1197     eARIAMultiSelectable
1198   },
1199   { // tabpanel
1200     nsGkAtoms::tabpanel,
1201     roles::PROPERTYPAGE,
1202     kUseMapRole,
1203     eNoValue,
1204     eNoAction,
1205     eNoLiveAttr,
1206     kGenericAccType,
1207     kNoReqStates
1208   },
1209   { // term
1210     nsGkAtoms::term,
1211     roles::TERM,
1212     kUseMapRole,
1213     eNoValue,
1214     eNoAction,
1215     eNoLiveAttr,
1216     kGenericAccType,
1217     states::READONLY
1218   },
1219   { // textbox
1220     nsGkAtoms::textbox,
1221     roles::ENTRY,
1222     kUseMapRole,
1223     eNoValue,
1224     eActivateAction,
1225     eNoLiveAttr,
1226     kGenericAccType,
1227     kNoReqStates,
1228     eARIAAutoComplete,
1229     eARIAMultiline,
1230     eARIAReadonlyOrEditable
1231   },
1232   { // timer
1233     nsGkAtoms::timer,
1234     roles::NOTHING,
1235     kUseNativeRole,
1236     eNoValue,
1237     eNoAction,
1238     eOffLiveAttr,
1239     kNoReqStates
1240   },
1241   { // toolbar
1242     nsGkAtoms::toolbar,
1243     roles::TOOLBAR,
1244     kUseMapRole,
1245     eNoValue,
1246     eNoAction,
1247     eNoLiveAttr,
1248     kGenericAccType,
1249     states::HORIZONTAL,
1250     eARIAOrientation
1251   },
1252   { // tooltip
1253     nsGkAtoms::tooltip,
1254     roles::TOOLTIP,
1255     kUseMapRole,
1256     eNoValue,
1257     eNoAction,
1258     eNoLiveAttr,
1259     kGenericAccType,
1260     kNoReqStates
1261   },
1262   { // tree
1263     nsGkAtoms::tree,
1264     roles::OUTLINE,
1265     kUseMapRole,
1266     eNoValue,
1267     eNoAction,
1268     eNoLiveAttr,
1269     eSelect,
1270     states::VERTICAL,
1271     eARIAReadonly,
1272     eARIAMultiSelectable,
1273     eFocusableUntilDisabled,
1274     eARIAOrientation
1275   },
1276   { // treegrid
1277     nsGkAtoms::treegrid,
1278     roles::TREE_TABLE,
1279     kUseMapRole,
1280     eNoValue,
1281     eNoAction,
1282     eNoLiveAttr,
1283     eSelect | eTable,
1284     kNoReqStates,
1285     eARIAReadonly,
1286     eARIAMultiSelectable,
1287     eFocusableUntilDisabled,
1288     eARIAOrientation
1289   },
1290   { // treeitem
1291     nsGkAtoms::treeitem,
1292     roles::OUTLINEITEM,
1293     kUseMapRole,
1294     eNoValue,
1295     eActivateAction, // XXX: should expose second 'expand/collapse' action based
1296                      // on states
1297     eNoLiveAttr,
1298     kGenericAccType,
1299     kNoReqStates,
1300     eARIASelectable,
1301     eARIACheckedMixed
1302   }
1303     // clang-format on
1304 };
1305 
1306 static const nsRoleMapEntry sLandmarkRoleMap = {
1307     nsGkAtoms::_empty, roles::NOTHING, kUseNativeRole,  eNoValue,
1308     eNoAction,         eNoLiveAttr,    kGenericAccType, kNoReqStates};
1309 
1310 nsRoleMapEntry aria::gEmptyRoleMap = {
1311     nsGkAtoms::_empty, roles::TEXT_CONTAINER, kUseMapRole,     eNoValue,
1312     eNoAction,         eNoLiveAttr,           kGenericAccType, kNoReqStates};
1313 
1314 /**
1315  * Universal (Global) states:
1316  * The following state rules are applied to any accessible element,
1317  * whether there is an ARIA role or not:
1318  */
1319 static const EStateRule sWAIUnivStateMap[] = {
1320     eARIABusy,     eARIACurrent, eARIADisabled,
1321     eARIAExpanded,  // Currently under spec review but precedent exists
1322     eARIAHasPopup,  // Note this is a tokenised attribute starting in ARIA 1.1
1323     eARIAInvalid,  eARIAModal,
1324     eARIARequired,  // XXX not global, Bug 553117
1325     eARIANone};
1326 
1327 /**
1328  * ARIA attribute map for attribute characteristics.
1329  * @note ARIA attributes that don't have any flags are not included here.
1330  */
1331 
1332 struct AttrCharacteristics {
1333   const nsStaticAtom* const attributeName;
1334   const uint8_t characteristics;
1335 };
1336 
1337 static const AttrCharacteristics gWAIUnivAttrMap[] = {
1338     // clang-format off
1339   {nsGkAtoms::aria_activedescendant,  ATTR_BYPASSOBJ                               },
1340   {nsGkAtoms::aria_atomic,   ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
1341   {nsGkAtoms::aria_busy,                               ATTR_VALTOKEN | ATTR_GLOBAL },
1342   {nsGkAtoms::aria_checked,           ATTR_BYPASSOBJ | ATTR_VALTOKEN               }, /* exposes checkable obj attr */
1343   {nsGkAtoms::aria_controls,          ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1344   {nsGkAtoms::aria_current,  ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
1345   {nsGkAtoms::aria_describedby,       ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1346   {nsGkAtoms::aria_details,           ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1347   {nsGkAtoms::aria_disabled,          ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
1348   {nsGkAtoms::aria_dropeffect,                         ATTR_VALTOKEN | ATTR_GLOBAL },
1349   {nsGkAtoms::aria_errormessage,      ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1350   {nsGkAtoms::aria_expanded,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1351   {nsGkAtoms::aria_flowto,            ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1352   {nsGkAtoms::aria_grabbed,                            ATTR_VALTOKEN | ATTR_GLOBAL },
1353   {nsGkAtoms::aria_haspopup,          ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
1354   {nsGkAtoms::aria_hidden,            ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL }, /* handled special way */
1355   {nsGkAtoms::aria_invalid,           ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
1356   {nsGkAtoms::aria_label,             ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1357   {nsGkAtoms::aria_labelledby,        ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1358   {nsGkAtoms::aria_level,             ATTR_BYPASSOBJ                               }, /* handled via groupPosition */
1359   {nsGkAtoms::aria_live,                               ATTR_VALTOKEN | ATTR_GLOBAL },
1360   {nsGkAtoms::aria_modal,             ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
1361   {nsGkAtoms::aria_multiline,         ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1362   {nsGkAtoms::aria_multiselectable,   ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1363   {nsGkAtoms::aria_owns,              ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1364   {nsGkAtoms::aria_orientation,                        ATTR_VALTOKEN               },
1365   {nsGkAtoms::aria_posinset,          ATTR_BYPASSOBJ                               }, /* handled via groupPosition */
1366   {nsGkAtoms::aria_pressed,           ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1367   {nsGkAtoms::aria_readonly,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1368   {nsGkAtoms::aria_relevant,          ATTR_BYPASSOBJ                 | ATTR_GLOBAL },
1369   {nsGkAtoms::aria_required,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1370   {nsGkAtoms::aria_selected,          ATTR_BYPASSOBJ | ATTR_VALTOKEN               },
1371   {nsGkAtoms::aria_setsize,           ATTR_BYPASSOBJ                               }, /* handled via groupPosition */
1372   {nsGkAtoms::aria_sort,                               ATTR_VALTOKEN               },
1373   {nsGkAtoms::aria_valuenow,          ATTR_BYPASSOBJ                               },
1374   {nsGkAtoms::aria_valuemin,          ATTR_BYPASSOBJ                               },
1375   {nsGkAtoms::aria_valuemax,          ATTR_BYPASSOBJ                               },
1376   {nsGkAtoms::aria_valuetext,         ATTR_BYPASSOBJ                               }
1377     // clang-format on
1378 };
1379 
GetRoleMap(dom::Element * aEl)1380 const nsRoleMapEntry* aria::GetRoleMap(dom::Element* aEl) {
1381   return GetRoleMapFromIndex(GetRoleMapIndex(aEl));
1382 }
1383 
GetRoleMapIndex(dom::Element * aEl)1384 uint8_t aria::GetRoleMapIndex(dom::Element* aEl) {
1385   nsAutoString roles;
1386   if (!aEl || !aEl->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
1387       roles.IsEmpty()) {
1388     // We treat role="" as if the role attribute is absent (per aria spec:8.1.1)
1389     return NO_ROLE_MAP_ENTRY_INDEX;
1390   }
1391 
1392   nsWhitespaceTokenizer tokenizer(roles);
1393   while (tokenizer.hasMoreTokens()) {
1394     // Do a binary search through table for the next role in role list
1395     const nsDependentSubstring role = tokenizer.nextToken();
1396     size_t idx;
1397     auto comparator = [&role](const nsRoleMapEntry& aEntry) {
1398       return Compare(role, aEntry.ARIARoleString(),
1399                      nsCaseInsensitiveStringComparator);
1400     };
1401     if (BinarySearchIf(sWAIRoleMaps, 0, ArrayLength(sWAIRoleMaps), comparator,
1402                        &idx)) {
1403       return idx;
1404     }
1405   }
1406 
1407   // Always use some entry index if there is a non-empty role string
1408   // To ensure an accessible object is created
1409   return LANDMARK_ROLE_MAP_ENTRY_INDEX;
1410 }
1411 
GetRoleMapFromIndex(uint8_t aRoleMapIndex)1412 const nsRoleMapEntry* aria::GetRoleMapFromIndex(uint8_t aRoleMapIndex) {
1413   switch (aRoleMapIndex) {
1414     case NO_ROLE_MAP_ENTRY_INDEX:
1415       return nullptr;
1416     case EMPTY_ROLE_MAP_ENTRY_INDEX:
1417       return &gEmptyRoleMap;
1418     case LANDMARK_ROLE_MAP_ENTRY_INDEX:
1419       return &sLandmarkRoleMap;
1420     default:
1421       return sWAIRoleMaps + aRoleMapIndex;
1422   }
1423 }
1424 
GetIndexFromRoleMap(const nsRoleMapEntry * aRoleMapEntry)1425 uint8_t aria::GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMapEntry) {
1426   if (aRoleMapEntry == nullptr) {
1427     return NO_ROLE_MAP_ENTRY_INDEX;
1428   } else if (aRoleMapEntry == &gEmptyRoleMap) {
1429     return EMPTY_ROLE_MAP_ENTRY_INDEX;
1430   } else if (aRoleMapEntry == &sLandmarkRoleMap) {
1431     return LANDMARK_ROLE_MAP_ENTRY_INDEX;
1432   } else {
1433     return aRoleMapEntry - sWAIRoleMaps;
1434   }
1435 }
1436 
UniversalStatesFor(mozilla::dom::Element * aElement)1437 uint64_t aria::UniversalStatesFor(mozilla::dom::Element* aElement) {
1438   uint64_t state = 0;
1439   uint32_t index = 0;
1440   while (MapToState(sWAIUnivStateMap[index], aElement, &state)) index++;
1441 
1442   return state;
1443 }
1444 
AttrCharacteristicsFor(nsAtom * aAtom)1445 uint8_t aria::AttrCharacteristicsFor(nsAtom* aAtom) {
1446   for (uint32_t i = 0; i < ArrayLength(gWAIUnivAttrMap); i++) {
1447     if (gWAIUnivAttrMap[i].attributeName == aAtom) {
1448       return gWAIUnivAttrMap[i].characteristics;
1449     }
1450   }
1451 
1452   return 0;
1453 }
1454 
HasDefinedARIAHidden(nsIContent * aContent)1455 bool aria::HasDefinedARIAHidden(nsIContent* aContent) {
1456   return aContent && aContent->IsElement() &&
1457          aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
1458                                             nsGkAtoms::aria_hidden,
1459                                             nsGkAtoms::_true, eCaseMatters);
1460 }
1461 
1462 ////////////////////////////////////////////////////////////////////////////////
1463 // AttrIterator class
1464 
AttrIterator(nsIContent * aContent)1465 AttrIterator::AttrIterator(nsIContent* aContent)
1466     : mElement(dom::Element::FromNode(aContent)), mAttrIdx(0) {
1467   mAttrCount = mElement ? mElement->GetAttrCount() : 0;
1468 }
1469 
Next()1470 bool AttrIterator::Next() {
1471   while (mAttrIdx < mAttrCount) {
1472     const nsAttrName* attr = mElement->GetAttrNameAt(mAttrIdx);
1473     mAttrIdx++;
1474     if (attr->NamespaceEquals(kNameSpaceID_None)) {
1475       mAttrAtom = attr->Atom();
1476       nsDependentAtomString attrStr(mAttrAtom);
1477       if (!StringBeginsWith(attrStr, u"aria-"_ns)) continue;  // Not ARIA
1478 
1479       uint8_t attrFlags = aria::AttrCharacteristicsFor(mAttrAtom);
1480       if (attrFlags & ATTR_BYPASSOBJ) {
1481         continue;  // No need to handle exposing as obj attribute here
1482       }
1483 
1484       if ((attrFlags & ATTR_VALTOKEN) &&
1485           !nsAccUtils::HasDefinedARIAToken(mElement, mAttrAtom)) {
1486         continue;  // only expose token based attributes if they are defined
1487       }
1488 
1489       if ((attrFlags & ATTR_BYPASSOBJ_IF_FALSE) &&
1490           mElement->AttrValueIs(kNameSpaceID_None, mAttrAtom, nsGkAtoms::_false,
1491                                 eCaseMatters)) {
1492         continue;  // only expose token based attribute if value is not 'false'.
1493       }
1494 
1495       return true;
1496     }
1497   }
1498 
1499   mAttrAtom = nullptr;
1500 
1501   return false;
1502 }
1503 
AttrName(nsAString & aAttrName) const1504 void AttrIterator::AttrName(nsAString& aAttrName) const {
1505   nsDependentAtomString attrStr(mAttrAtom);
1506   MOZ_ASSERT(StringBeginsWith(attrStr, u"aria-"_ns),
1507              "Stored atom is an aria attribute.");
1508   aAttrName.Assign(Substring(attrStr, 5));
1509 }
1510 
AttrName() const1511 nsAtom* AttrIterator::AttrName() const { return mAttrAtom; }
1512 
AttrValue(nsAString & aAttrValue) const1513 void AttrIterator::AttrValue(nsAString& aAttrValue) const {
1514   nsAutoString value;
1515   if (mElement->GetAttr(kNameSpaceID_None, mAttrAtom, value)) {
1516     if (aria::AttrCharacteristicsFor(mAttrAtom) & ATTR_VALTOKEN) {
1517       nsAtom* normalizedValue =
1518           nsAccUtils::NormalizeARIAToken(mElement, mAttrAtom);
1519       if (normalizedValue) {
1520         nsDependentAtomString normalizedValueStr(normalizedValue);
1521         aAttrValue.Assign(normalizedValueStr);
1522         return;
1523       }
1524     }
1525     aAttrValue.Assign(value);
1526   }
1527 }
1528