From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- accessible/android/AccessibleWrap.cpp | 473 +++ accessible/android/AccessibleWrap.h | 68 + accessible/android/ApplicationAccessibleWrap.h | 20 + accessible/android/DocAccessibleWrap.cpp | 78 + accessible/android/DocAccessibleWrap.h | 33 + accessible/android/Platform.cpp | 233 ++ accessible/android/RootAccessibleWrap.h | 22 + accessible/android/SessionAccessibility.cpp | 941 +++++ accessible/android/SessionAccessibility.h | 145 + accessible/android/TraversalRule.cpp | 288 ++ accessible/android/TraversalRule.h | 58 + accessible/android/moz.build | 35 + accessible/aom/AccessibleNode.cpp | 170 + accessible/aom/AccessibleNode.h | 212 + accessible/aom/moz.build | 44 + accessible/atk/AccessibleWrap.cpp | 1305 ++++++ accessible/atk/AccessibleWrap.h | 93 + accessible/atk/ApplicationAccessibleWrap.cpp | 145 + accessible/atk/ApplicationAccessibleWrap.h | 34 + accessible/atk/DOMtoATK.cpp | 151 + accessible/atk/DOMtoATK.h | 152 + accessible/atk/DocAccessibleWrap.cpp | 36 + accessible/atk/DocAccessibleWrap.h | 33 + accessible/atk/InterfaceInitFuncs.h | 43 + accessible/atk/Platform.cpp | 271 ++ accessible/atk/RootAccessibleWrap.cpp | 24 + accessible/atk/RootAccessibleWrap.h | 32 + accessible/atk/UtilInterface.cpp | 347 ++ accessible/atk/moz.build | 64 + accessible/atk/nsMai.h | 112 + accessible/atk/nsMaiHyperlink.cpp | 216 + accessible/atk/nsMaiHyperlink.h | 49 + accessible/atk/nsMaiInterfaceAction.cpp | 83 + accessible/atk/nsMaiInterfaceComponent.cpp | 186 + accessible/atk/nsMaiInterfaceDocument.cpp | 106 + accessible/atk/nsMaiInterfaceEditableText.cpp | 100 + accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp | 32 + accessible/atk/nsMaiInterfaceHypertext.cpp | 63 + accessible/atk/nsMaiInterfaceImage.cpp | 61 + accessible/atk/nsMaiInterfaceSelection.cpp | 102 + accessible/atk/nsMaiInterfaceTable.cpp | 264 ++ accessible/atk/nsMaiInterfaceTableCell.cpp | 148 + accessible/atk/nsMaiInterfaceText.cpp | 557 +++ accessible/atk/nsMaiInterfaceValue.cpp | 98 + accessible/atk/nsStateMap.h | 116 + accessible/base/ARIAMap.cpp | 1687 ++++++++ accessible/base/ARIAMap.h | 335 ++ accessible/base/ARIAStateMap.cpp | 334 ++ accessible/base/ARIAStateMap.h | 66 + accessible/base/AccAttributes.cpp | 270 ++ accessible/base/AccAttributes.h | 293 ++ accessible/base/AccEvent.cpp | 256 ++ accessible/base/AccEvent.h | 562 +++ accessible/base/AccGroupInfo.cpp | 397 ++ accessible/base/AccGroupInfo.h | 101 + accessible/base/AccIterator.cpp | 360 ++ accessible/base/AccIterator.h | 328 ++ accessible/base/AccTypes.h | 98 + accessible/base/Asserts.cpp | 29 + accessible/base/CacheConstants.h | 255 ++ accessible/base/CachedTableAccessible.cpp | 429 ++ accessible/base/CachedTableAccessible.h | 294 ++ accessible/base/DocManager.cpp | 562 +++ accessible/base/DocManager.h | 193 + accessible/base/EmbeddedObjCollector.cpp | 62 + accessible/base/EmbeddedObjCollector.h | 68 + accessible/base/EventQueue.cpp | 436 ++ accessible/base/EventQueue.h | 83 + accessible/base/EventTree.cpp | 99 + accessible/base/EventTree.h | 61 + accessible/base/Filters.cpp | 25 + accessible/base/Filters.h | 36 + accessible/base/FocusManager.cpp | 462 +++ accessible/base/FocusManager.h | 169 + accessible/base/HTMLMarkupMap.h | 444 ++ accessible/base/IDSet.h | 129 + accessible/base/Logging.cpp | 992 +++++ accessible/base/Logging.h | 236 ++ accessible/base/MathMLMarkupMap.h | 113 + accessible/base/NotificationController.cpp | 1107 +++++ accessible/base/NotificationController.h | 396 ++ accessible/base/Pivot.cpp | 331 ++ accessible/base/Pivot.h | 141 + accessible/base/Platform.h | 136 + accessible/base/Relation.h | 105 + accessible/base/RelationTypeGen.py | 41 + accessible/base/RelationTypeMap.h | 90 + accessible/base/RoleHGen.py | 42 + accessible/base/RoleMap.h | 1546 +++++++ accessible/base/SelectionManager.cpp | 246 ++ accessible/base/SelectionManager.h | 141 + accessible/base/States.h | 305 ++ accessible/base/Statistics.h | 42 + accessible/base/StyleInfo.cpp | 50 + accessible/base/StyleInfo.h | 36 + accessible/base/TextAttrs.cpp | 816 ++++ accessible/base/TextAttrs.h | 440 ++ accessible/base/TextLeafRange.cpp | 1990 +++++++++ accessible/base/TextLeafRange.h | 360 ++ accessible/base/TextRange-inl.h | 25 + accessible/base/TextRange.cpp | 376 ++ accessible/base/TextRange.h | 164 + accessible/base/TextUpdater.cpp | 215 + accessible/base/TextUpdater.h | 95 + accessible/base/TreeWalker.cpp | 348 ++ accessible/base/TreeWalker.h | 142 + accessible/base/XULMap.h | 115 + accessible/base/moz.build | 122 + accessible/base/nsAccCache.h | 24 + accessible/base/nsAccUtils.cpp | 626 +++ accessible/base/nsAccUtils.h | 299 ++ accessible/base/nsAccessibilityService.cpp | 1933 +++++++++ accessible/base/nsAccessibilityService.h | 492 +++ accessible/base/nsCoreUtils.cpp | 622 +++ accessible/base/nsCoreUtils.h | 329 ++ accessible/base/nsEventShell.cpp | 81 + accessible/base/nsEventShell.h | 66 + accessible/base/nsTextEquivUtils.cpp | 360 ++ accessible/base/nsTextEquivUtils.h | 182 + accessible/basetypes/Accessible.cpp | 730 ++++ accessible/basetypes/Accessible.h | 741 ++++ accessible/basetypes/HyperTextAccessibleBase.cpp | 841 ++++ accessible/basetypes/HyperTextAccessibleBase.h | 317 ++ accessible/basetypes/TableAccessible.h | 172 + accessible/basetypes/TableCellAccessible.h | 68 + accessible/basetypes/moz.build | 25 + accessible/docs/Architecture.md | 65 + accessible/docs/ColorsAndHighContrastMode.md | 50 + accessible/docs/DocumentAccessibilityLifecycle.md | 104 + accessible/docs/GeckoViewThreadTopography.md | 51 + accessible/docs/HCMMediaQueries.md | 306 ++ accessible/docs/HCMSettings.md | 45 + accessible/docs/fx_view_fc.jpg | Bin 0 -> 65120 bytes accessible/docs/fxview.jpg | Bin 0 -> 52575 bytes accessible/docs/fxview_ic.jpg | Bin 0 -> 77858 bytes accessible/docs/index.rst | 17 + accessible/generic/ARIAGridAccessible.cpp | 77 + accessible/generic/ARIAGridAccessible.h | 35 + accessible/generic/ApplicationAccessible.cpp | 142 + accessible/generic/ApplicationAccessible.h | 109 + accessible/generic/BaseAccessibles.cpp | 142 + accessible/generic/BaseAccessibles.h | 136 + accessible/generic/DocAccessible-inl.h | 183 + accessible/generic/DocAccessible.cpp | 2847 +++++++++++++ accessible/generic/DocAccessible.h | 825 ++++ accessible/generic/FormControlAccessible.cpp | 83 + accessible/generic/FormControlAccessible.h | 65 + accessible/generic/HyperTextAccessible-inl.h | 48 + accessible/generic/HyperTextAccessible.cpp | 1141 ++++++ accessible/generic/HyperTextAccessible.h | 266 ++ accessible/generic/ImageAccessible.cpp | 260 ++ accessible/generic/ImageAccessible.h | 94 + accessible/generic/LocalAccessible-inl.h | 107 + accessible/generic/LocalAccessible.cpp | 4255 ++++++++++++++++++++ accessible/generic/LocalAccessible.h | 1028 +++++ accessible/generic/OuterDocAccessible.cpp | 224 ++ accessible/generic/OuterDocAccessible.h | 80 + accessible/generic/RootAccessible.cpp | 706 ++++ accessible/generic/RootAccessible.h | 93 + accessible/generic/TextLeafAccessible.cpp | 42 + accessible/generic/TextLeafAccessible.h | 46 + accessible/generic/moz.build | 63 + accessible/html/HTMLCanvasAccessible.cpp | 16 + accessible/html/HTMLCanvasAccessible.h | 35 + accessible/html/HTMLElementAccessibles.cpp | 231 ++ accessible/html/HTMLElementAccessibles.h | 159 + accessible/html/HTMLFormControlAccessible.cpp | 979 +++++ accessible/html/HTMLFormControlAccessible.h | 387 ++ accessible/html/HTMLImageMapAccessible.cpp | 200 + accessible/html/HTMLImageMapAccessible.h | 82 + accessible/html/HTMLLinkAccessible.cpp | 129 + accessible/html/HTMLLinkAccessible.h | 59 + accessible/html/HTMLListAccessible.cpp | 112 + accessible/html/HTMLListAccessible.h | 85 + accessible/html/HTMLSelectAccessible.cpp | 472 +++ accessible/html/HTMLSelectAccessible.h | 216 + accessible/html/HTMLTableAccessible.cpp | 712 ++++ accessible/html/HTMLTableAccessible.h | 177 + accessible/html/moz.build | 52 + accessible/interfaces/ia2/IA2Typelib.idl | 33 + accessible/interfaces/ia2/moz.build | 92 + accessible/interfaces/moz.build | 45 + accessible/interfaces/msaa/AccessibleMarshal.def | 11 + accessible/interfaces/msaa/AccessibleMarshal.rc | 5 + .../interfaces/msaa/AccessibleMarshalThunk.c | 17 + accessible/interfaces/msaa/ISimpleDOM.idl | 22 + accessible/interfaces/msaa/ISimpleDOMDocument.idl | 83 + accessible/interfaces/msaa/ISimpleDOMNode.idl | 175 + accessible/interfaces/msaa/ISimpleDOMText.idl | 78 + accessible/interfaces/msaa/moz.build | 57 + accessible/interfaces/nsIAccessibilityService.idl | 119 + accessible/interfaces/nsIAccessible.idl | 351 ++ .../interfaces/nsIAccessibleAnnouncementEvent.idl | 23 + accessible/interfaces/nsIAccessibleApplication.idl | 34 + .../interfaces/nsIAccessibleCaretMoveEvent.idl | 33 + accessible/interfaces/nsIAccessibleDocument.idl | 70 + .../interfaces/nsIAccessibleEditableText.idl | 57 + accessible/interfaces/nsIAccessibleEvent.idl | 251 ++ accessible/interfaces/nsIAccessibleHideEvent.idl | 28 + accessible/interfaces/nsIAccessibleHyperLink.idl | 86 + accessible/interfaces/nsIAccessibleHyperText.idl | 54 + accessible/interfaces/nsIAccessibleImage.idl | 30 + .../interfaces/nsIAccessibleMacInterface.idl | 87 + .../nsIAccessibleObjectAttributeChangedEvent.idl | 19 + accessible/interfaces/nsIAccessiblePivot.idl | 96 + accessible/interfaces/nsIAccessibleRelation.idl | 183 + accessible/interfaces/nsIAccessibleRole.idl | 802 ++++ .../interfaces/nsIAccessibleScrollingEvent.idl | 34 + accessible/interfaces/nsIAccessibleSelectable.idl | 59 + .../interfaces/nsIAccessibleStateChangeEvent.idl | 29 + accessible/interfaces/nsIAccessibleStates.idl | 76 + accessible/interfaces/nsIAccessibleTable.idl | 234 ++ .../interfaces/nsIAccessibleTableChangeEvent.idl | 20 + accessible/interfaces/nsIAccessibleText.idl | 220 + .../interfaces/nsIAccessibleTextChangeEvent.idl | 33 + .../interfaces/nsIAccessibleTextLeafRange.idl | 43 + accessible/interfaces/nsIAccessibleTextRange.idl | 57 + .../nsIAccessibleTextSelectionChangeEvent.idl | 21 + accessible/interfaces/nsIAccessibleTypes.idl | 80 + accessible/interfaces/nsIAccessibleValue.idl | 35 + accessible/ipc/DocAccessibleChild.cpp | 431 ++ accessible/ipc/DocAccessibleChild.h | 176 + accessible/ipc/DocAccessibleParent.cpp | 1266 ++++++ accessible/ipc/DocAccessibleParent.h | 400 ++ accessible/ipc/DocAccessibleTypes.ipdlh | 19 + accessible/ipc/IPCTypes.h | 189 + accessible/ipc/PDocAccessible.ipdl | 164 + accessible/ipc/RemoteAccessible.cpp | 2092 ++++++++++ accessible/ipc/RemoteAccessible.h | 511 +++ accessible/ipc/moz.build | 62 + accessible/mac/.clang-format | 11 + accessible/mac/AccessibleWrap.h | 95 + accessible/mac/AccessibleWrap.mm | 281 ++ accessible/mac/ApplicationAccessibleWrap.h | 21 + accessible/mac/DocAccessibleWrap.h | 46 + accessible/mac/DocAccessibleWrap.mm | 105 + accessible/mac/GeckoTextMarker.h | 138 + accessible/mac/GeckoTextMarker.mm | 514 +++ accessible/mac/MOXAccessibleBase.h | 143 + accessible/mac/MOXAccessibleBase.mm | 584 +++ accessible/mac/MOXAccessibleProtocol.h | 538 +++ accessible/mac/MOXLandmarkAccessibles.h | 15 + accessible/mac/MOXLandmarkAccessibles.mm | 15 + accessible/mac/MOXMathAccessibles.h | 64 + accessible/mac/MOXMathAccessibles.mm | 117 + accessible/mac/MOXSearchInfo.h | 43 + accessible/mac/MOXSearchInfo.mm | 374 ++ accessible/mac/MOXTextMarkerDelegate.h | 169 + accessible/mac/MOXTextMarkerDelegate.mm | 527 +++ accessible/mac/MOXWebAreaAccessible.h | 105 + accessible/mac/MOXWebAreaAccessible.mm | 276 ++ accessible/mac/MacUtils.h | 62 + accessible/mac/MacUtils.mm | 169 + accessible/mac/Platform.mm | 268 ++ accessible/mac/PlatformExtTypes.h | 25 + accessible/mac/RootAccessibleWrap.h | 40 + accessible/mac/RootAccessibleWrap.mm | 51 + accessible/mac/RotorRules.h | 144 + accessible/mac/RotorRules.mm | 390 ++ accessible/mac/SelectorMapGen.py | 61 + accessible/mac/moz.build | 70 + accessible/mac/mozAccessible.h | 285 ++ accessible/mac/mozAccessible.mm | 1003 +++++ accessible/mac/mozAccessibleProtocol.h | 65 + accessible/mac/mozActionElements.h | 108 + accessible/mac/mozActionElements.mm | 228 ++ accessible/mac/mozHTMLAccessible.h | 44 + accessible/mac/mozHTMLAccessible.mm | 83 + accessible/mac/mozRootAccessible.h | 58 + accessible/mac/mozRootAccessible.mm | 84 + accessible/mac/mozSelectableElements.h | 128 + accessible/mac/mozSelectableElements.mm | 330 ++ accessible/mac/mozTableAccessible.h | 177 + accessible/mac/mozTableAccessible.mm | 630 +++ accessible/mac/mozTextAccessible.h | 114 + accessible/mac/mozTextAccessible.mm | 423 ++ accessible/moz.build | 62 + accessible/other/AccessibleWrap.cpp | 19 + accessible/other/AccessibleWrap.h | 28 + accessible/other/ApplicationAccessibleWrap.h | 20 + accessible/other/DocAccessibleWrap.h | 23 + accessible/other/Platform.cpp | 38 + accessible/other/RootAccessibleWrap.h | 23 + accessible/other/moz.build | 23 + accessible/tests/browser/.eslintrc.js | 28 + accessible/tests/browser/Common.sys.mjs | 451 +++ accessible/tests/browser/Layout.sys.mjs | 178 + accessible/tests/browser/atk/a11y_setup.py | 64 + accessible/tests/browser/atk/browser.toml | 16 + accessible/tests/browser/atk/browser_role.js | 33 + accessible/tests/browser/atk/browser_table.js | 54 + accessible/tests/browser/atk/head.js | 18 + accessible/tests/browser/bounds/browser.toml | 34 + .../browser/bounds/browser_accessible_moved.js | 49 + .../tests/browser/bounds/browser_caret_rect.js | 140 + .../tests/browser/bounds/browser_position.js | 135 + .../bounds/browser_test_display_contents.js | 44 + .../bounds/browser_test_iframe_transform.js | 209 + .../browser/bounds/browser_test_resolution.js | 72 + .../bounds/browser_test_simple_transform.js | 225 ++ .../tests/browser/bounds/browser_test_zoom.js | 65 + .../tests/browser/bounds/browser_test_zoom_text.js | 86 + .../tests/browser/bounds/browser_zero_area.js | 118 + accessible/tests/browser/bounds/head.js | 19 + accessible/tests/browser/browser.toml | 52 + .../browser/browser_shutdown_acc_reference.js | 64 + .../browser/browser_shutdown_doc_acc_reference.js | 56 + .../browser_shutdown_multi_acc_reference_doc.js | 76 + .../browser_shutdown_multi_acc_reference_obj.js | 76 + ...owser_shutdown_multi_proxy_acc_reference_doc.js | 84 + ...owser_shutdown_multi_proxy_acc_reference_obj.js | 84 + .../browser/browser_shutdown_multi_reference.js | 58 + .../browser_shutdown_parent_own_reference.js | 96 + accessible/tests/browser/browser_shutdown_pref.js | 72 + .../browser_shutdown_proxy_acc_reference.js | 70 + .../browser_shutdown_proxy_doc_acc_reference.js | 72 + .../browser_shutdown_remote_no_reference.js | 135 + .../tests/browser/browser_shutdown_remote_only.js | 53 + .../browser_shutdown_remote_own_reference.js | 175 + .../browser/browser_shutdown_scope_lifecycle.js | 28 + .../browser/browser_shutdown_start_restart.js | 51 + accessible/tests/browser/e10s/browser.toml | 125 + .../tests/browser/e10s/browser_caching_actions.js | 295 ++ .../browser/e10s/browser_caching_attributes.js | 763 ++++ .../browser/e10s/browser_caching_description.js | 280 ++ .../browser/e10s/browser_caching_document_props.js | 80 + .../browser/e10s/browser_caching_domnodeid.js | 32 + .../browser/e10s/browser_caching_hyperlink.js | 228 ++ .../browser/e10s/browser_caching_innerHTML.js | 48 + .../browser/e10s/browser_caching_interfaces.js | 59 + .../browser/e10s/browser_caching_large_update.js | 66 + .../tests/browser/e10s/browser_caching_name.js | 542 +++ .../tests/browser/e10s/browser_caching_position.js | 194 + .../browser/e10s/browser_caching_relations.js | 291 ++ .../browser/e10s/browser_caching_relations_002.js | 366 ++ .../tests/browser/e10s/browser_caching_states.js | 552 +++ .../tests/browser/e10s/browser_caching_table.js | 665 +++ .../browser/e10s/browser_caching_text_bounds.js | 737 ++++ .../tests/browser/e10s/browser_caching_uniqueid.js | 30 + .../tests/browser/e10s/browser_caching_value.js | 457 +++ .../browser/e10s/browser_events_announcement.js | 30 + .../tests/browser/e10s/browser_events_caretmove.js | 22 + .../tests/browser/e10s/browser_events_hide.js | 44 + .../tests/browser/e10s/browser_events_show.js | 22 + .../browser/e10s/browser_events_statechange.js | 71 + .../browser/e10s/browser_events_textchange.js | 119 + .../tests/browser/e10s/browser_file_input.js | 77 + accessible/tests/browser/e10s/browser_language.js | 29 + accessible/tests/browser/e10s/browser_obj_group.js | 430 ++ .../tests/browser/e10s/browser_obj_group_002.js | 390 ++ .../browser/e10s/browser_treeupdate_ariadialog.js | 45 + .../browser/e10s/browser_treeupdate_ariaowns.js | 457 +++ .../browser/e10s/browser_treeupdate_canvas.js | 28 + .../browser_treeupdate_csscontentvisibility.js | 105 + .../browser/e10s/browser_treeupdate_cssoverflow.js | 60 + .../tests/browser/e10s/browser_treeupdate_doc.js | 320 ++ .../browser/e10s/browser_treeupdate_gencontent.js | 94 + .../browser/e10s/browser_treeupdate_hidden.js | 32 + .../tests/browser/e10s/browser_treeupdate_image.js | 192 + .../browser/e10s/browser_treeupdate_imagemap.js | 190 + .../tests/browser/e10s/browser_treeupdate_list.js | 52 + .../e10s/browser_treeupdate_list_editabledoc.js | 48 + .../browser/e10s/browser_treeupdate_listener.js | 38 + .../tests/browser/e10s/browser_treeupdate_move.js | 84 + .../browser/e10s/browser_treeupdate_optgroup.js | 100 + .../browser/e10s/browser_treeupdate_removal.js | 58 + .../e10s/browser_treeupdate_select_dropdown.js | 73 + .../tests/browser/e10s/browser_treeupdate_table.js | 48 + .../browser/e10s/browser_treeupdate_textleaf.js | 38 + .../browser/e10s/browser_treeupdate_visibility.js | 342 ++ .../browser/e10s/browser_treeupdate_whitespace.js | 69 + .../browser/e10s/doc_treeupdate_ariadialog.html | 23 + .../browser/e10s/doc_treeupdate_ariaowns.html | 44 + .../browser/e10s/doc_treeupdate_imagemap.html | 21 + .../browser/e10s/doc_treeupdate_removal.xhtml | 11 + .../browser/e10s/doc_treeupdate_visibility.html | 78 + .../browser/e10s/doc_treeupdate_whitespace.html | 10 + accessible/tests/browser/e10s/fonts/Ahem.sjs | 241 ++ accessible/tests/browser/e10s/head.js | 192 + accessible/tests/browser/events/browser.toml | 33 + accessible/tests/browser/events/browser_alert.js | 46 + .../events/browser_test_A11yUtils_announce.js | 57 + .../events/browser_test_caret_move_granularity.js | 102 + .../tests/browser/events/browser_test_docload.js | 128 + .../browser/events/browser_test_focus_browserui.js | 57 + .../browser/events/browser_test_focus_dialog.js | 76 + .../browser/events/browser_test_focus_urlbar.js | 438 ++ .../tests/browser/events/browser_test_panel.js | 54 + .../tests/browser/events/browser_test_scrolling.js | 153 + .../events/browser_test_selection_urlbar.js | 61 + .../tests/browser/events/browser_test_textcaret.js | 58 + accessible/tests/browser/events/head.js | 18 + accessible/tests/browser/fission/browser.toml | 24 + .../tests/browser/fission/browser_content_tree.js | 75 + .../tests/browser/fission/browser_hidden_iframe.js | 70 + .../tests/browser/fission/browser_nested_iframe.js | 164 + .../tests/browser/fission/browser_reframe_root.js | 95 + .../browser/fission/browser_reframe_visibility.js | 116 + .../tests/browser/fission/browser_src_change.js | 62 + .../tests/browser/fission/browser_take_focus.js | 73 + accessible/tests/browser/fission/head.js | 18 + accessible/tests/browser/general/browser.toml | 13 + .../browser/general/browser_test_doc_creation.js | 55 + .../tests/browser/general/browser_test_urlbar.js | 40 + accessible/tests/browser/general/head.js | 71 + accessible/tests/browser/head.js | 120 + accessible/tests/browser/hittest/browser.toml | 24 + .../tests/browser/hittest/browser_test_browser.js | 68 + .../tests/browser/hittest/browser_test_general.js | 425 ++ .../browser/hittest/browser_test_scroll_hittest.js | 105 + .../browser/hittest/browser_test_shadowroot.js | 60 + .../tests/browser/hittest/browser_test_text.js | 84 + .../tests/browser/hittest/browser_test_zoom.js | 38 + .../browser/hittest/browser_test_zoom_text.js | 64 + accessible/tests/browser/hittest/head.js | 113 + accessible/tests/browser/mac/browser.toml | 95 + accessible/tests/browser/mac/browser_app.js | 355 ++ accessible/tests/browser/mac/browser_aria_busy.js | 44 + .../browser/mac/browser_aria_controls_flowto.js | 92 + .../tests/browser/mac/browser_aria_current.js | 58 + .../tests/browser/mac/browser_aria_expanded.js | 45 + .../tests/browser/mac/browser_aria_haspopup.js | 320 ++ .../tests/browser/mac/browser_aria_setsize.js | 47 + .../tests/browser/mac/browser_attributed_text.js | 165 + accessible/tests/browser/mac/browser_bounds.js | 77 + .../tests/browser/mac/browser_details_summary.js | 69 + accessible/tests/browser/mac/browser_focus.js | 44 + accessible/tests/browser/mac/browser_heading.js | 77 + accessible/tests/browser/mac/browser_hierarchy.js | 75 + accessible/tests/browser/mac/browser_input.js | 225 ++ .../tests/browser/mac/browser_label_title.js | 111 + accessible/tests/browser/mac/browser_link.js | 222 + .../tests/browser/mac/browser_live_regions.js | 165 + accessible/tests/browser/mac/browser_mathml.js | 146 + accessible/tests/browser/mac/browser_menulist.js | 103 + accessible/tests/browser/mac/browser_navigate.js | 394 ++ accessible/tests/browser/mac/browser_outline.js | 566 +++ .../tests/browser/mac/browser_outline_xul.js | 274 ++ .../tests/browser/mac/browser_popupbutton.js | 166 + .../tests/browser/mac/browser_radio_position.js | 321 ++ accessible/tests/browser/mac/browser_range.js | 190 + accessible/tests/browser/mac/browser_required.js | 175 + .../tests/browser/mac/browser_rich_listbox.js | 73 + .../tests/browser/mac/browser_roles_elements.js | 334 ++ accessible/tests/browser/mac/browser_rootgroup.js | 246 ++ accessible/tests/browser/mac/browser_rotor.js | 1752 ++++++++ .../tests/browser/mac/browser_selectables.js | 363 ++ accessible/tests/browser/mac/browser_table.js | 629 +++ .../tests/browser/mac/browser_text_basics.js | 426 ++ accessible/tests/browser/mac/browser_text_input.js | 657 +++ accessible/tests/browser/mac/browser_text_leaf.js | 83 + .../tests/browser/mac/browser_text_selection.js | 187 + .../browser/mac/browser_toggle_radio_check.js | 304 ++ accessible/tests/browser/mac/browser_webarea.js | 77 + accessible/tests/browser/mac/doc_aria_tabs.html | 95 + accessible/tests/browser/mac/doc_menulist.xhtml | 19 + .../tests/browser/mac/doc_rich_listbox.xhtml | 22 + .../tests/browser/mac/doc_textmarker_test.html | 2427 +++++++++++ accessible/tests/browser/mac/doc_tree.xhtml | 59 + accessible/tests/browser/mac/head.js | 133 + accessible/tests/browser/pivot/browser.toml | 10 + accessible/tests/browser/pivot/browser_pivot.js | 103 + accessible/tests/browser/pivot/head.js | 122 + accessible/tests/browser/python_runner_wsh.py | 88 + accessible/tests/browser/role/browser.toml | 15 + .../tests/browser/role/browser_computedARIARole.js | 88 + .../tests/browser/role/browser_minimumRole.js | 59 + accessible/tests/browser/role/head.js | 18 + accessible/tests/browser/scroll/browser.toml | 19 + .../tests/browser/scroll/browser_scrollToPoint.js | 31 + .../tests/browser/scroll/browser_test_scrollTo.js | 53 + .../browser/scroll/browser_test_scroll_bounds.js | 662 +++ .../scroll/browser_test_scroll_substring.js | 67 + .../tests/browser/scroll/browser_test_zoom_text.js | 145 + accessible/tests/browser/scroll/head.js | 18 + accessible/tests/browser/selectable/browser.toml | 13 + .../browser/selectable/browser_test_aria_select.js | 164 + .../browser/selectable/browser_test_select.js | 329 ++ accessible/tests/browser/selectable/head.js | 88 + accessible/tests/browser/shared-head.js | 973 +++++ accessible/tests/browser/states/browser.toml | 22 + .../tests/browser/states/browser_test_link.js | 44 + .../states/browser_test_select_visibility.js | 76 + .../browser/states/browser_test_visibility.js | 181 + .../browser/states/browser_test_visibility_2.js | 131 + accessible/tests/browser/states/head.js | 91 + accessible/tests/browser/telemetry/browser.toml | 6 + .../browser/telemetry/browser_HCM_telemetry.js | 407 ++ accessible/tests/browser/text/browser.toml | 24 + .../tests/browser/text/browser_editabletext.js | 173 + accessible/tests/browser/text/browser_text.js | 326 ++ .../tests/browser/text/browser_text_caret.js | 452 +++ .../text/browser_text_paragraph_boundary.js | 22 + .../tests/browser/text/browser_text_selection.js | 344 ++ .../tests/browser/text/browser_text_spelling.js | 151 + .../tests/browser/text/browser_textleafpoint.js | 524 +++ accessible/tests/browser/text/head.js | 276 ++ accessible/tests/browser/tree/browser.toml | 31 + accessible/tests/browser/tree/browser_aria_owns.js | 278 ++ .../tests/browser/tree/browser_browser_element.js | 16 + .../browser/tree/browser_css_content_visibility.js | 121 + accessible/tests/browser/tree/browser_general.js | 128 + accessible/tests/browser/tree/browser_lazy_tabs.js | 43 + accessible/tests/browser/tree/browser_link.js | 208 + accessible/tests/browser/tree/browser_searchbar.js | 96 + accessible/tests/browser/tree/browser_shadowdom.js | 98 + .../tree/browser_test_nsIAccessibleDocument_URL.js | 54 + accessible/tests/browser/tree/head.js | 33 + accessible/tests/browser/windows/a11y_setup.py | 145 + .../browser/windows/a11y_setup_requirements.txt | 1 + accessible/tests/browser/windows/ia2/browser.toml | 9 + .../tests/browser/windows/ia2/browser_role.js | 39 + accessible/tests/browser/windows/ia2/head.js | 18 + accessible/tests/browser/windows/uia/browser.toml | 11 + .../browser/windows/uia/browser_controlType.js | 30 + .../windows/uia/browser_elementFromPoint.js | 34 + accessible/tests/browser/windows/uia/head.js | 18 + accessible/tests/crashtests/1072792.xhtml | 5 + accessible/tests/crashtests/1380199.html | 15 + accessible/tests/crashtests/1402999.html | 11 + accessible/tests/crashtests/1415667.html | 1 + accessible/tests/crashtests/1463962.html | 4 + accessible/tests/crashtests/1472024-1.html | 7 + accessible/tests/crashtests/1472024-2.html | 20 + accessible/tests/crashtests/1484778.html | 26 + accessible/tests/crashtests/1494707.html | 15 + accessible/tests/crashtests/1503964.html | 4 + accessible/tests/crashtests/1572811.html | 9 + accessible/tests/crashtests/1578282.html | 21 + accessible/tests/crashtests/1585851.html | 21 + accessible/tests/crashtests/1655983.html | 6 + accessible/tests/crashtests/1838250.html | 6 + accessible/tests/crashtests/448064.xhtml | 70 + accessible/tests/crashtests/884202.html | 21 + accessible/tests/crashtests/890760.html | 14 + accessible/tests/crashtests/893515.html | 15 + accessible/tests/crashtests/crashtests.list | 23 + .../crashtests/last_test_to_unload_testsuite.xhtml | 36 + accessible/tests/mochitest/.eslintrc.js | 24 + accessible/tests/mochitest/a11y.toml | 27 + accessible/tests/mochitest/actions.js | 231 ++ accessible/tests/mochitest/actions/a11y.toml | 26 + .../tests/mochitest/actions/test_anchors.html | 146 + accessible/tests/mochitest/actions/test_aria.html | 200 + .../tests/mochitest/actions/test_controls.html | 107 + .../tests/mochitest/actions/test_general.html | 105 + .../tests/mochitest/actions/test_general.xhtml | 167 + accessible/tests/mochitest/actions/test_keys.html | 57 + accessible/tests/mochitest/actions/test_keys.xhtml | 126 + accessible/tests/mochitest/actions/test_media.html | 131 + .../tests/mochitest/actions/test_select.html | 67 + accessible/tests/mochitest/actions/test_tree.xhtml | 127 + .../tests/mochitest/actions/test_treegrid.xhtml | 190 + accessible/tests/mochitest/aom/a11y.toml | 3 + accessible/tests/mochitest/aom/test_general.html | 208 + accessible/tests/mochitest/attributes.js | 516 +++ accessible/tests/mochitest/attributes/a11y.toml | 22 + .../attributes/test_dpub_aria_xml-roles.html | 120 + .../attributes/test_graphics_aria_xml-roles.html | 48 + .../tests/mochitest/attributes/test_listbox.html | 82 + .../tests/mochitest/attributes/test_obj.html | 292 ++ .../tests/mochitest/attributes/test_obj_css.html | 225 ++ .../tests/mochitest/attributes/test_obj_group.html | 564 +++ .../mochitest/attributes/test_obj_group.xhtml | 215 + .../mochitest/attributes/test_obj_group_tree.xhtml | 84 + .../tests/mochitest/attributes/test_tag.html | 80 + .../tests/mochitest/attributes/test_xml-roles.html | 267 ++ accessible/tests/mochitest/autocomplete.js | 198 + accessible/tests/mochitest/bounds/a11y.toml | 4 + accessible/tests/mochitest/bounds/test_list.html | 78 + accessible/tests/mochitest/browser.js | 156 + accessible/tests/mochitest/common.js | 1048 +++++ accessible/tests/mochitest/dumbfile.zip | Bin 0 -> 22 bytes accessible/tests/mochitest/elm/a11y.toml | 19 + accessible/tests/mochitest/elm/test_HTMLSpec.html | 2029 ++++++++++ .../tests/mochitest/elm/test_MathMLSpec.html | 616 +++ accessible/tests/mochitest/elm/test_figure.html | 60 + accessible/tests/mochitest/elm/test_listbox.xhtml | 73 + .../tests/mochitest/elm/test_nsApplicationAcc.html | 67 + .../tests/mochitest/elm/test_shadowroot.html | 35 + .../mochitest/elm/test_shadowroot_subframe.html | 68 + accessible/tests/mochitest/events.js | 2660 ++++++++++++ accessible/tests/mochitest/events/a11y.toml | 128 + .../tests/mochitest/events/docload/a11y.toml | 21 + .../mochitest/events/docload/docload_wnd.html | 37 + .../events/docload/test_docload_aria.html | 75 + .../events/docload/test_docload_busy.html | 83 + .../events/docload/test_docload_embedded.html | 85 + .../events/docload/test_docload_iframe.html | 99 + .../events/docload/test_docload_root.html | 125 + .../events/docload/test_docload_shutdown.html | 142 + accessible/tests/mochitest/events/focus.html | 10 + accessible/tests/mochitest/events/scroll.html | 181 + accessible/tests/mochitest/events/slow_image.sjs | 55 + .../tests/mochitest/events/test_announcement.html | 61 + .../tests/mochitest/events/test_aria_alert.html | 84 + .../tests/mochitest/events/test_aria_menu.html | 267 ++ .../tests/mochitest/events/test_aria_objattr.html | 68 + .../tests/mochitest/events/test_aria_owns.html | 122 + .../mochitest/events/test_aria_statechange.html | 231 ++ .../tests/mochitest/events/test_attrchange.html | 107 + accessible/tests/mochitest/events/test_attrs.html | 85 + .../tests/mochitest/events/test_bug1322593-2.html | 77 + .../tests/mochitest/events/test_bug1322593.html | 74 + .../tests/mochitest/events/test_caretmove.html | 142 + .../tests/mochitest/events/test_coalescence.html | 817 ++++ .../tests/mochitest/events/test_contextmenu.html | 133 + .../tests/mochitest/events/test_descrchange.html | 142 + .../tests/mochitest/events/test_dragndrop.html | 106 + accessible/tests/mochitest/events/test_flush.html | 74 + .../events/test_focus_aria_activedescendant.html | 327 ++ .../mochitest/events/test_focus_autocomplete.html | 83 + .../mochitest/events/test_focus_autocomplete.xhtml | 507 +++ .../tests/mochitest/events/test_focus_canvas.html | 58 + .../mochitest/events/test_focus_contextmenu.xhtml | 98 + .../mochitest/events/test_focus_controls.html | 76 + .../tests/mochitest/events/test_focus_doc.html | 92 + .../tests/mochitest/events/test_focus_general.html | 176 + .../mochitest/events/test_focus_general.xhtml | 124 + .../mochitest/events/test_focus_listcontrols.xhtml | 153 + .../tests/mochitest/events/test_focus_menu.xhtml | 117 + .../tests/mochitest/events/test_focus_name.html | 116 + .../tests/mochitest/events/test_focus_removal.html | 95 + .../tests/mochitest/events/test_focus_selects.html | 190 + .../tests/mochitest/events/test_focus_tabbox.xhtml | 102 + .../tests/mochitest/events/test_focus_tree.xhtml | 117 + .../events/test_focusable_statechange.html | 122 + .../tests/mochitest/events/test_fromUserInput.html | 112 + accessible/tests/mochitest/events/test_label.xhtml | 178 + accessible/tests/mochitest/events/test_menu.xhtml | 200 + .../tests/mochitest/events/test_mutation.html | 580 +++ .../tests/mochitest/events/test_namechange.html | 185 + .../tests/mochitest/events/test_namechange.xhtml | 90 + .../tests/mochitest/events/test_scroll.xhtml | 107 + .../tests/mochitest/events/test_scroll_caret.xhtml | 91 + .../tests/mochitest/events/test_selection.html | 109 + .../tests/mochitest/events/test_selection.xhtml | 254 ++ .../mochitest/events/test_selection_aria.html | 122 + .../tests/mochitest/events/test_statechange.html | 565 +++ .../tests/mochitest/events/test_statechange.xhtml | 117 + accessible/tests/mochitest/events/test_text.html | 310 ++ .../tests/mochitest/events/test_text_alg.html | 249 ++ .../mochitest/events/test_textattrchange.html | 107 + .../tests/mochitest/events/test_textselchange.html | 82 + accessible/tests/mochitest/events/test_tree.xhtml | 358 ++ .../tests/mochitest/events/test_valuechange.html | 315 ++ accessible/tests/mochitest/focus/a11y.toml | 10 + .../tests/mochitest/focus/test_focus_radio.xhtml | 84 + .../tests/mochitest/focus/test_focusedChild.html | 81 + .../tests/mochitest/focus/test_takeFocus.html | 109 + .../tests/mochitest/focus/test_takeFocus.xhtml | 104 + accessible/tests/mochitest/formimage.png | Bin 0 -> 20105 bytes accessible/tests/mochitest/grid.js | 142 + accessible/tests/mochitest/hittest/a11y.toml | 19 + .../tests/mochitest/hittest/test_browser.html | 61 + .../tests/mochitest/hittest/test_general.html | 110 + accessible/tests/mochitest/hittest/test_menu.xhtml | 133 + .../tests/mochitest/hittest/test_shadowroot.html | 35 + .../hittest/test_shadowroot_subframe.html | 57 + accessible/tests/mochitest/hittest/test_zoom.html | 59 + .../tests/mochitest/hittest/test_zoom_text.html | 57 + .../tests/mochitest/hittest/test_zoom_tree.xhtml | 97 + accessible/tests/mochitest/hittest/zoom_tree.xhtml | 18 + accessible/tests/mochitest/hyperlink/a11y.toml | 8 + accessible/tests/mochitest/hyperlink/hyperlink.js | 46 + .../tests/mochitest/hyperlink/test_general.html | 278 ++ .../tests/mochitest/hyperlink/test_general.xhtml | 98 + accessible/tests/mochitest/hypertext/a11y.toml | 8 + .../tests/mochitest/hypertext/test_general.html | 150 + .../tests/mochitest/hypertext/test_update.html | 214 + accessible/tests/mochitest/layout.js | 390 ++ accessible/tests/mochitest/letters.gif | Bin 0 -> 5596 bytes accessible/tests/mochitest/longdesc_src.html | 8 + accessible/tests/mochitest/moz.build | 35 + accessible/tests/mochitest/moz.png | Bin 0 -> 1991 bytes accessible/tests/mochitest/name.js | 38 + accessible/tests/mochitest/name/a11y.toml | 29 + accessible/tests/mochitest/name/markup.js | 425 ++ accessible/tests/mochitest/name/markuprules.xml | 367 ++ .../mochitest/name/test_ARIACore_examples.html | 90 + .../tests/mochitest/name/test_browserui.xhtml | 85 + .../tests/mochitest/name/test_counterstyle.html | 150 + accessible/tests/mochitest/name/test_general.html | 780 ++++ accessible/tests/mochitest/name/test_general.xhtml | 345 ++ accessible/tests/mochitest/name/test_link.html | 87 + accessible/tests/mochitest/name/test_list.html | 103 + accessible/tests/mochitest/name/test_markup.html | 58 + accessible/tests/mochitest/name/test_svg.html | 53 + accessible/tests/mochitest/name/test_tree.xhtml | 207 + accessible/tests/mochitest/promisified-events.js | 328 ++ accessible/tests/mochitest/relations.js | 204 + accessible/tests/mochitest/relations/a11y.toml | 23 + .../tests/mochitest/relations/test_embeds.xhtml | 128 + .../tests/mochitest/relations/test_general.html | 456 +++ .../tests/mochitest/relations/test_general.xhtml | 237 ++ .../mochitest/relations/test_groupInfoUpdate.html | 57 + .../tests/mochitest/relations/test_shadowdom.html | 58 + .../mochitest/relations/test_tabbrowser.xhtml | 109 + .../tests/mochitest/relations/test_tree.xhtml | 105 + .../mochitest/relations/test_ui_modalprompt.html | 111 + .../tests/mochitest/relations/test_update.html | 213 + accessible/tests/mochitest/role.js | 198 + accessible/tests/mochitest/role/a11y.toml | 19 + .../mochitest/role/chrome_body_role_alert.xhtml | 6 + accessible/tests/mochitest/role/test_aria.html | 729 ++++ accessible/tests/mochitest/role/test_aria.xhtml | 65 + .../tests/mochitest/role/test_dpub_aria.html | 114 + accessible/tests/mochitest/role/test_general.html | 201 + accessible/tests/mochitest/role/test_general.xhtml | 59 + .../tests/mochitest/role/test_graphics_aria.html | 42 + accessible/tests/mochitest/role/test_svg.html | 93 + accessible/tests/mochitest/scroll/a11y.toml | 4 + accessible/tests/mochitest/scroll/test_zoom.html | 145 + accessible/tests/mochitest/selectable.js | 138 + accessible/tests/mochitest/selectable/a11y.toml | 14 + .../tests/mochitest/selectable/test_listbox.xhtml | 144 + .../tests/mochitest/selectable/test_menu.xhtml | 77 + .../tests/mochitest/selectable/test_menulist.xhtml | 95 + .../tests/mochitest/selectable/test_tabs.xhtml | 93 + .../tests/mochitest/selectable/test_tree.xhtml | 171 + accessible/tests/mochitest/states.js | 365 ++ accessible/tests/mochitest/states/a11y.toml | 60 + accessible/tests/mochitest/states/test_aria.html | 652 +++ accessible/tests/mochitest/states/test_aria.xhtml | 70 + .../tests/mochitest/states/test_aria_imgmap.html | 75 + .../mochitest/states/test_aria_widgetitems.html | 152 + .../tests/mochitest/states/test_buttons.html | 83 + .../tests/mochitest/states/test_controls.html | 51 + .../tests/mochitest/states/test_controls.xhtml | 153 + accessible/tests/mochitest/states/test_doc.html | 95 + .../tests/mochitest/states/test_doc_busy.html | 130 + .../tests/mochitest/states/test_docarticle.html | 78 + .../tests/mochitest/states/test_editablebody.html | 44 + .../tests/mochitest/states/test_expandable.xhtml | 112 + accessible/tests/mochitest/states/test_frames.html | 93 + accessible/tests/mochitest/states/test_inputs.html | 267 ++ accessible/tests/mochitest/states/test_link.html | 85 + accessible/tests/mochitest/states/test_popup.xhtml | 54 + .../tests/mochitest/states/test_selects.html | 166 + accessible/tests/mochitest/states/test_stale.html | 108 + accessible/tests/mochitest/states/test_tabs.xhtml | 66 + .../tests/mochitest/states/test_textbox.xhtml | 78 + accessible/tests/mochitest/states/test_tree.xhtml | 146 + .../tests/mochitest/states/test_visibility.html | 75 + .../tests/mochitest/states/test_visibility.xhtml | 162 + accessible/tests/mochitest/states/z_frames.html | 11 + .../tests/mochitest/states/z_frames_article.html | 11 + .../tests/mochitest/states/z_frames_checkbox.html | 11 + .../tests/mochitest/states/z_frames_textbox.html | 11 + .../tests/mochitest/states/z_frames_update.html | 21 + accessible/tests/mochitest/table.js | 851 ++++ accessible/tests/mochitest/table/a11y.toml | 42 + .../tests/mochitest/table/test_css_tables.html | 114 + .../mochitest/table/test_headers_ariagrid.html | 183 + .../mochitest/table/test_headers_ariatable.html | 94 + .../tests/mochitest/table/test_headers_table.html | 756 ++++ .../tests/mochitest/table/test_headers_tree.xhtml | 100 + .../mochitest/table/test_indexes_ariagrid.html | 159 + .../tests/mochitest/table/test_indexes_table.html | 481 +++ .../tests/mochitest/table/test_indexes_tree.xhtml | 70 + .../tests/mochitest/table/test_layoutguess.html | 567 +++ accessible/tests/mochitest/table/test_mtable.html | 160 + .../tests/mochitest/table/test_sels_ariagrid.html | 147 + .../tests/mochitest/table/test_sels_table.html | 155 + .../tests/mochitest/table/test_sels_tree.xhtml | 76 + .../mochitest/table/test_struct_ariagrid.html | 163 + .../mochitest/table/test_struct_ariatreegrid.html | 74 + .../tests/mochitest/table/test_struct_table.html | 217 + .../tests/mochitest/table/test_struct_tree.xhtml | 73 + accessible/tests/mochitest/table/test_table_1.html | 107 + accessible/tests/mochitest/table/test_table_2.html | 87 + .../tests/mochitest/table/test_table_mutation.html | 100 + .../tests/mochitest/test_OuterDocAccessible.html | 87 + .../tests/mochitest/test_aria_token_attrs.html | 417 ++ accessible/tests/mochitest/test_bug420863.html | 99 + ...test_custom_element_accessibility_defaults.html | 382 ++ accessible/tests/mochitest/test_descr.html | 134 + .../mochitest/test_nsIAccessibleDocument.html | 94 + .../tests/mochitest/test_nsIAccessibleImage.html | 198 + accessible/tests/mochitest/text.js | 814 ++++ accessible/tests/mochitest/text/a11y.toml | 33 + accessible/tests/mochitest/text/doc.html | 9 + .../tests/mochitest/text/test_atcaretoffset.html | 425 ++ .../tests/mochitest/text/test_charboundary.html | 138 + accessible/tests/mochitest/text/test_doc.html | 40 + accessible/tests/mochitest/text/test_dynamic.html | 80 + accessible/tests/mochitest/text/test_general.xhtml | 79 + accessible/tests/mochitest/text/test_gettext.html | 135 + .../tests/mochitest/text/test_hypertext.html | 150 + .../tests/mochitest/text/test_lineboundary.html | 422 ++ .../mochitest/text/test_paragraphboundary.html | 148 + .../tests/mochitest/text/test_passwords.html | 72 + .../tests/mochitest/text/test_selection.html | 119 + .../mochitest/text/test_settext_input_event.html | 38 + .../tests/mochitest/text/test_textBounds.html | 36 + .../tests/mochitest/text/test_wordboundary.html | 361 ++ accessible/tests/mochitest/text/test_words.html | 154 + accessible/tests/mochitest/textattrs/a11y.toml | 17 + .../tests/mochitest/textattrs/test_general.html | 813 ++++ .../tests/mochitest/textattrs/test_general.xhtml | 51 + .../tests/mochitest/textattrs/test_invalid.html | 59 + .../tests/mochitest/textattrs/test_mathml.html | 54 + .../tests/mochitest/textattrs/test_spelling.html | 52 + accessible/tests/mochitest/textattrs/test_svg.html | 56 + accessible/tests/mochitest/textcaret/a11y.toml | 4 + .../tests/mochitest/textcaret/test_general.html | 174 + accessible/tests/mochitest/textrange/a11y.toml | 8 + .../tests/mochitest/textrange/test_general.html | 70 + .../tests/mochitest/textrange/test_selection.html | 144 + accessible/tests/mochitest/textselection/a11y.toml | 6 + .../mochitest/textselection/test_general.html | 221 + .../mochitest/textselection/test_userinput.html | 76 + accessible/tests/mochitest/tree/a11y.toml | 105 + accessible/tests/mochitest/tree/dockids.html | 32 + .../tests/mochitest/tree/test_applicationacc.xhtml | 73 + .../mochitest/tree/test_aria_display_contents.html | 173 + .../tests/mochitest/tree/test_aria_globals.html | 127 + .../tests/mochitest/tree/test_aria_grid.html | 318 ++ .../tests/mochitest/tree/test_aria_imgmap.html | 104 + .../tests/mochitest/tree/test_aria_list.html | 90 + .../tests/mochitest/tree/test_aria_menu.html | 91 + .../tests/mochitest/tree/test_aria_owns.html | 197 + .../mochitest/tree/test_aria_presentation.html | 176 + .../tests/mochitest/tree/test_aria_table.html | 101 + .../tests/mochitest/tree/test_brokencontext.html | 214 + accessible/tests/mochitest/tree/test_button.xhtml | 83 + accessible/tests/mochitest/tree/test_canvas.html | 53 + .../tests/mochitest/tree/test_combobox.xhtml | 107 + .../tests/mochitest/tree/test_cssflexbox.html | 78 + .../tests/mochitest/tree/test_cssoverflow.html | 135 + .../mochitest/tree/test_display_contents.html | 92 + accessible/tests/mochitest/tree/test_divs.html | 351 ++ .../tests/mochitest/tree/test_dochierarchy.html | 84 + accessible/tests/mochitest/tree/test_dockids.html | 62 + accessible/tests/mochitest/tree/test_filectrl.html | 47 + accessible/tests/mochitest/tree/test_formctrl.html | 125 + .../tests/mochitest/tree/test_formctrl.xhtml | 129 + .../tests/mochitest/tree/test_gencontent.html | 69 + .../tests/mochitest/tree/test_groupbox.xhtml | 63 + .../tests/mochitest/tree/test_html_in_mathml.html | 61 + accessible/tests/mochitest/tree/test_iframe.html | 50 + accessible/tests/mochitest/tree/test_image.xhtml | 58 + accessible/tests/mochitest/tree/test_img.html | 84 + .../tests/mochitest/tree/test_invalid_img.xhtml | 48 + .../mochitest/tree/test_invalidationlist.html | 56 + accessible/tests/mochitest/tree/test_list.html | 346 ++ accessible/tests/mochitest/tree/test_map.html | 81 + accessible/tests/mochitest/tree/test_media.html | 127 + accessible/tests/mochitest/tree/test_select.html | 125 + accessible/tests/mochitest/tree/test_svg.html | 127 + accessible/tests/mochitest/tree/test_tabbox.xhtml | 108 + .../tests/mochitest/tree/test_tabbrowser.xhtml | 261 ++ accessible/tests/mochitest/tree/test_table.html | 507 +++ accessible/tests/mochitest/tree/test_table_2.html | 242 ++ accessible/tests/mochitest/tree/test_table_3.html | 244 ++ accessible/tests/mochitest/tree/test_tree.xhtml | 182 + accessible/tests/mochitest/tree/test_txtcntr.html | 234 ++ accessible/tests/mochitest/tree/test_txtctrl.html | 171 + accessible/tests/mochitest/tree/test_txtctrl.xhtml | 86 + accessible/tests/mochitest/tree/wnd.xhtml | 8 + accessible/tests/mochitest/treeupdate/a11y.toml | 84 + .../mochitest/treeupdate/test_ariadialog.html | 113 + .../mochitest/treeupdate/test_ariahidden.html | 118 + .../tests/mochitest/treeupdate/test_ariaowns.html | 851 ++++ .../mochitest/treeupdate/test_bug1040735.html | 40 + .../mochitest/treeupdate/test_bug1175913.html | 95 + .../mochitest/treeupdate/test_bug1189277.html | 82 + .../mochitest/treeupdate/test_bug1276857.html | 131 + .../treeupdate/test_bug1276857_subframe.html | 33 + .../mochitest/treeupdate/test_bug852150.xhtml | 57 + .../mochitest/treeupdate/test_bug883708.xhtml | 31 + .../mochitest/treeupdate/test_bug884251.xhtml | 19 + .../tests/mochitest/treeupdate/test_bug895082.html | 49 + .../tests/mochitest/treeupdate/test_canvas.html | 87 + .../mochitest/treeupdate/test_contextmenu.xhtml | 315 ++ .../mochitest/treeupdate/test_cssoverflow.html | 149 + .../tests/mochitest/treeupdate/test_deck.xhtml | 154 + .../mochitest/treeupdate/test_delayed_removal.html | 500 +++ .../tests/mochitest/treeupdate/test_doc.html | 415 ++ .../mochitest/treeupdate/test_gencontent.html | 187 + .../tests/mochitest/treeupdate/test_general.html | 174 + .../tests/mochitest/treeupdate/test_hidden.html | 125 + .../tests/mochitest/treeupdate/test_imagemap.html | 402 ++ .../tests/mochitest/treeupdate/test_inert.html | 132 + .../mochitest/treeupdate/test_inner_reorder.html | 148 + .../tests/mochitest/treeupdate/test_list.html | 139 + .../treeupdate/test_list_editabledoc.html | 100 + .../mochitest/treeupdate/test_list_style.html | 181 + .../tests/mochitest/treeupdate/test_listbox.xhtml | 181 + .../tests/mochitest/treeupdate/test_menu.xhtml | 127 + .../mochitest/treeupdate/test_menubutton.xhtml | 141 + .../tests/mochitest/treeupdate/test_optgroup.html | 122 + .../mochitest/treeupdate/test_recreation.html | 93 + .../tests/mochitest/treeupdate/test_select.html | 191 + .../mochitest/treeupdate/test_shadow_slots.html | 554 +++ .../tests/mochitest/treeupdate/test_shutdown.xhtml | 131 + .../tests/mochitest/treeupdate/test_table.html | 74 + .../tests/mochitest/treeupdate/test_textleaf.html | 167 + .../tests/mochitest/treeupdate/test_tooltip.xhtml | 75 + .../mochitest/treeupdate/test_visibility.html | 411 ++ .../mochitest/treeupdate/test_whitespace.html | 200 + accessible/tests/mochitest/treeview.css | 15 + accessible/tests/mochitest/treeview.js | 273 ++ accessible/tests/mochitest/value.js | 52 + accessible/tests/mochitest/value/a11y.toml | 16 + .../tests/mochitest/value/test_ariavalue.html | 85 + .../tests/mochitest/value/test_datetime.html | 76 + accessible/tests/mochitest/value/test_general.html | 159 + accessible/tests/mochitest/value/test_meter.html | 82 + accessible/tests/mochitest/value/test_number.html | 56 + .../tests/mochitest/value/test_progress.html | 61 + accessible/tests/mochitest/value/test_range.html | 59 + accessible/windows/ia2/ia2Accessible.cpp | 563 +++ accessible/windows/ia2/ia2Accessible.h | 120 + accessible/windows/ia2/ia2AccessibleAction.cpp | 152 + accessible/windows/ia2/ia2AccessibleAction.h | 85 + .../windows/ia2/ia2AccessibleApplication.cpp | 94 + accessible/windows/ia2/ia2AccessibleApplication.h | 49 + accessible/windows/ia2/ia2AccessibleComponent.cpp | 106 + accessible/windows/ia2/ia2AccessibleComponent.h | 40 + .../windows/ia2/ia2AccessibleEditableText.cpp | 106 + accessible/windows/ia2/ia2AccessibleEditableText.h | 59 + accessible/windows/ia2/ia2AccessibleHyperlink.cpp | 164 + accessible/windows/ia2/ia2AccessibleHyperlink.h | 55 + accessible/windows/ia2/ia2AccessibleHypertext.cpp | 142 + accessible/windows/ia2/ia2AccessibleHypertext.h | 70 + accessible/windows/ia2/ia2AccessibleImage.cpp | 81 + accessible/windows/ia2/ia2AccessibleImage.h | 51 + accessible/windows/ia2/ia2AccessibleRelation.cpp | 94 + accessible/windows/ia2/ia2AccessibleRelation.h | 80 + accessible/windows/ia2/ia2AccessibleTable.cpp | 534 +++ accessible/windows/ia2/ia2AccessibleTable.h | 178 + accessible/windows/ia2/ia2AccessibleTableCell.cpp | 186 + accessible/windows/ia2/ia2AccessibleTableCell.h | 71 + accessible/windows/ia2/ia2AccessibleText.cpp | 466 +++ accessible/windows/ia2/ia2AccessibleText.h | 246 ++ .../ia2/ia2AccessibleTextSelectionContainer.cpp | 126 + .../ia2/ia2AccessibleTextSelectionContainer.h | 43 + accessible/windows/ia2/ia2AccessibleValue.cpp | 125 + accessible/windows/ia2/ia2AccessibleValue.h | 44 + accessible/windows/ia2/moz.build | 61 + accessible/windows/moz.build | 7 + accessible/windows/msaa/AccessibleWrap.cpp | 140 + accessible/windows/msaa/AccessibleWrap.h | 75 + .../windows/msaa/ApplicationAccessibleWrap.cpp | 43 + .../windows/msaa/ApplicationAccessibleWrap.h | 31 + accessible/windows/msaa/Compatibility.cpp | 181 + accessible/windows/msaa/Compatibility.h | 124 + accessible/windows/msaa/CompatibilityUIA.cpp | 347 ++ accessible/windows/msaa/DocAccessibleWrap.cpp | 100 + accessible/windows/msaa/DocAccessibleWrap.h | 39 + accessible/windows/msaa/EnumVariant.cpp | 90 + accessible/windows/msaa/EnumVariant.h | 62 + accessible/windows/msaa/IUnknownImpl.cpp | 36 + accessible/windows/msaa/IUnknownImpl.h | 173 + accessible/windows/msaa/LazyInstantiator.cpp | 779 ++++ accessible/windows/msaa/LazyInstantiator.h | 142 + accessible/windows/msaa/MsaaAccessible.cpp | 1368 +++++++ accessible/windows/msaa/MsaaAccessible.h | 204 + accessible/windows/msaa/MsaaDocAccessible.cpp | 133 + accessible/windows/msaa/MsaaDocAccessible.h | 71 + accessible/windows/msaa/MsaaIdGenerator.cpp | 87 + accessible/windows/msaa/MsaaIdGenerator.h | 47 + accessible/windows/msaa/MsaaRootAccessible.cpp | 67 + accessible/windows/msaa/MsaaRootAccessible.h | 50 + accessible/windows/msaa/MsaaXULMenuAccessible.cpp | 83 + accessible/windows/msaa/MsaaXULMenuAccessible.h | 30 + accessible/windows/msaa/NtUndoc.h | 85 + accessible/windows/msaa/Platform.cpp | 264 ++ accessible/windows/msaa/RootAccessibleWrap.cpp | 44 + accessible/windows/msaa/RootAccessibleWrap.h | 33 + accessible/windows/msaa/ServiceProvider.cpp | 106 + accessible/windows/msaa/ServiceProvider.h | 37 + accessible/windows/msaa/moz.build | 73 + accessible/windows/msaa/nsEventMap.h | 57 + accessible/windows/msaa/nsWinUtils.cpp | 167 + accessible/windows/msaa/nsWinUtils.h | 105 + accessible/windows/sdn/moz.build | 24 + accessible/windows/sdn/sdnAccessible-inl.h | 49 + accessible/windows/sdn/sdnAccessible.cpp | 522 +++ accessible/windows/sdn/sdnAccessible.h | 149 + accessible/windows/sdn/sdnDocAccessible.cpp | 117 + accessible/windows/sdn/sdnDocAccessible.h | 52 + accessible/windows/sdn/sdnTextAccessible.cpp | 166 + accessible/windows/sdn/sdnTextAccessible.h | 69 + accessible/windows/uia/moz.build | 22 + accessible/windows/uia/uiaRawElmProvider.cpp | 206 + accessible/windows/uia/uiaRawElmProvider.h | 75 + accessible/xpcom/AccEventGen.py | 256 ++ accessible/xpcom/AccEvents.conf | 20 + accessible/xpcom/moz.build | 80 + accessible/xpcom/nsAccessibleRelation.cpp | 59 + accessible/xpcom/nsAccessibleRelation.h | 46 + accessible/xpcom/xpcAccessibilityService.cpp | 307 ++ accessible/xpcom/xpcAccessibilityService.h | 68 + accessible/xpcom/xpcAccessible.cpp | 665 +++ accessible/xpcom/xpcAccessible.h | 112 + accessible/xpcom/xpcAccessibleApplication.cpp | 60 + accessible/xpcom/xpcAccessibleApplication.h | 47 + accessible/xpcom/xpcAccessibleDocument.cpp | 179 + accessible/xpcom/xpcAccessibleDocument.h | 136 + accessible/xpcom/xpcAccessibleGeneric.cpp | 61 + accessible/xpcom/xpcAccessibleGeneric.h | 96 + accessible/xpcom/xpcAccessibleHyperLink.cpp | 92 + accessible/xpcom/xpcAccessibleHyperLink.h | 48 + accessible/xpcom/xpcAccessibleHyperText.cpp | 477 +++ accessible/xpcom/xpcAccessibleHyperText.h | 57 + accessible/xpcom/xpcAccessibleImage.cpp | 49 + accessible/xpcom/xpcAccessibleImage.h | 40 + accessible/xpcom/xpcAccessibleMacInterface.h | 104 + accessible/xpcom/xpcAccessibleMacInterface.mm | 581 +++ accessible/xpcom/xpcAccessiblePivot.cpp | 155 + accessible/xpcom/xpcAccessiblePivot.h | 47 + accessible/xpcom/xpcAccessibleSelectable.cpp | 119 + accessible/xpcom/xpcAccessibleSelectable.h | 50 + accessible/xpcom/xpcAccessibleTable.cpp | 362 ++ accessible/xpcom/xpcAccessibleTable.h | 74 + accessible/xpcom/xpcAccessibleTableCell.cpp | 140 + accessible/xpcom/xpcAccessibleTableCell.h | 52 + accessible/xpcom/xpcAccessibleTextLeafRange.cpp | 83 + accessible/xpcom/xpcAccessibleTextLeafRange.h | 45 + accessible/xpcom/xpcAccessibleTextRange.cpp | 125 + accessible/xpcom/xpcAccessibleTextRange.h | 78 + accessible/xpcom/xpcAccessibleValue.cpp | 99 + accessible/xpcom/xpcAccessibleValue.h | 42 + accessible/xul/XULAlertAccessible.cpp | 44 + accessible/xul/XULAlertAccessible.h | 40 + accessible/xul/XULComboboxAccessible.cpp | 142 + accessible/xul/XULComboboxAccessible.h | 43 + accessible/xul/XULElementAccessibles.cpp | 205 + accessible/xul/XULElementAccessibles.h | 108 + accessible/xul/XULFormControlAccessible.cpp | 446 ++ accessible/xul/XULFormControlAccessible.h | 186 + accessible/xul/XULListboxAccessible.cpp | 456 +++ accessible/xul/XULListboxAccessible.h | 136 + accessible/xul/XULMenuAccessible.cpp | 477 +++ accessible/xul/XULMenuAccessible.h | 113 + accessible/xul/XULSelectControlAccessible.cpp | 253 ++ accessible/xul/XULSelectControlAccessible.h | 47 + accessible/xul/XULTabAccessible.cpp | 217 + accessible/xul/XULTabAccessible.h | 98 + accessible/xul/XULTreeAccessible.cpp | 995 +++++ accessible/xul/XULTreeAccessible.h | 265 ++ accessible/xul/XULTreeGridAccessible.cpp | 666 +++ accessible/xul/XULTreeGridAccessible.h | 193 + accessible/xul/moz.build | 56 + 1046 files changed, 192276 insertions(+) create mode 100644 accessible/android/AccessibleWrap.cpp create mode 100644 accessible/android/AccessibleWrap.h create mode 100644 accessible/android/ApplicationAccessibleWrap.h create mode 100644 accessible/android/DocAccessibleWrap.cpp create mode 100644 accessible/android/DocAccessibleWrap.h create mode 100644 accessible/android/Platform.cpp create mode 100644 accessible/android/RootAccessibleWrap.h create mode 100644 accessible/android/SessionAccessibility.cpp create mode 100644 accessible/android/SessionAccessibility.h create mode 100644 accessible/android/TraversalRule.cpp create mode 100644 accessible/android/TraversalRule.h create mode 100644 accessible/android/moz.build create mode 100644 accessible/aom/AccessibleNode.cpp create mode 100644 accessible/aom/AccessibleNode.h create mode 100644 accessible/aom/moz.build create mode 100644 accessible/atk/AccessibleWrap.cpp create mode 100644 accessible/atk/AccessibleWrap.h create mode 100644 accessible/atk/ApplicationAccessibleWrap.cpp create mode 100644 accessible/atk/ApplicationAccessibleWrap.h create mode 100644 accessible/atk/DOMtoATK.cpp create mode 100644 accessible/atk/DOMtoATK.h create mode 100644 accessible/atk/DocAccessibleWrap.cpp create mode 100644 accessible/atk/DocAccessibleWrap.h create mode 100644 accessible/atk/InterfaceInitFuncs.h create mode 100644 accessible/atk/Platform.cpp create mode 100644 accessible/atk/RootAccessibleWrap.cpp create mode 100644 accessible/atk/RootAccessibleWrap.h create mode 100644 accessible/atk/UtilInterface.cpp create mode 100644 accessible/atk/moz.build create mode 100644 accessible/atk/nsMai.h create mode 100644 accessible/atk/nsMaiHyperlink.cpp create mode 100644 accessible/atk/nsMaiHyperlink.h create mode 100644 accessible/atk/nsMaiInterfaceAction.cpp create mode 100644 accessible/atk/nsMaiInterfaceComponent.cpp create mode 100644 accessible/atk/nsMaiInterfaceDocument.cpp create mode 100644 accessible/atk/nsMaiInterfaceEditableText.cpp create mode 100644 accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp create mode 100644 accessible/atk/nsMaiInterfaceHypertext.cpp create mode 100644 accessible/atk/nsMaiInterfaceImage.cpp create mode 100644 accessible/atk/nsMaiInterfaceSelection.cpp create mode 100644 accessible/atk/nsMaiInterfaceTable.cpp create mode 100644 accessible/atk/nsMaiInterfaceTableCell.cpp create mode 100644 accessible/atk/nsMaiInterfaceText.cpp create mode 100644 accessible/atk/nsMaiInterfaceValue.cpp create mode 100644 accessible/atk/nsStateMap.h create mode 100644 accessible/base/ARIAMap.cpp create mode 100644 accessible/base/ARIAMap.h create mode 100644 accessible/base/ARIAStateMap.cpp create mode 100644 accessible/base/ARIAStateMap.h create mode 100644 accessible/base/AccAttributes.cpp create mode 100644 accessible/base/AccAttributes.h create mode 100644 accessible/base/AccEvent.cpp create mode 100644 accessible/base/AccEvent.h create mode 100644 accessible/base/AccGroupInfo.cpp create mode 100644 accessible/base/AccGroupInfo.h create mode 100644 accessible/base/AccIterator.cpp create mode 100644 accessible/base/AccIterator.h create mode 100644 accessible/base/AccTypes.h create mode 100644 accessible/base/Asserts.cpp create mode 100644 accessible/base/CacheConstants.h create mode 100644 accessible/base/CachedTableAccessible.cpp create mode 100644 accessible/base/CachedTableAccessible.h create mode 100644 accessible/base/DocManager.cpp create mode 100644 accessible/base/DocManager.h create mode 100644 accessible/base/EmbeddedObjCollector.cpp create mode 100644 accessible/base/EmbeddedObjCollector.h create mode 100644 accessible/base/EventQueue.cpp create mode 100644 accessible/base/EventQueue.h create mode 100644 accessible/base/EventTree.cpp create mode 100644 accessible/base/EventTree.h create mode 100644 accessible/base/Filters.cpp create mode 100644 accessible/base/Filters.h create mode 100644 accessible/base/FocusManager.cpp create mode 100644 accessible/base/FocusManager.h create mode 100644 accessible/base/HTMLMarkupMap.h create mode 100644 accessible/base/IDSet.h create mode 100644 accessible/base/Logging.cpp create mode 100644 accessible/base/Logging.h create mode 100644 accessible/base/MathMLMarkupMap.h create mode 100644 accessible/base/NotificationController.cpp create mode 100644 accessible/base/NotificationController.h create mode 100644 accessible/base/Pivot.cpp create mode 100644 accessible/base/Pivot.h create mode 100644 accessible/base/Platform.h create mode 100644 accessible/base/Relation.h create mode 100644 accessible/base/RelationTypeGen.py create mode 100644 accessible/base/RelationTypeMap.h create mode 100644 accessible/base/RoleHGen.py create mode 100644 accessible/base/RoleMap.h create mode 100644 accessible/base/SelectionManager.cpp create mode 100644 accessible/base/SelectionManager.h create mode 100644 accessible/base/States.h create mode 100644 accessible/base/Statistics.h create mode 100644 accessible/base/StyleInfo.cpp create mode 100644 accessible/base/StyleInfo.h create mode 100644 accessible/base/TextAttrs.cpp create mode 100644 accessible/base/TextAttrs.h create mode 100644 accessible/base/TextLeafRange.cpp create mode 100644 accessible/base/TextLeafRange.h create mode 100644 accessible/base/TextRange-inl.h create mode 100644 accessible/base/TextRange.cpp create mode 100644 accessible/base/TextRange.h create mode 100644 accessible/base/TextUpdater.cpp create mode 100644 accessible/base/TextUpdater.h create mode 100644 accessible/base/TreeWalker.cpp create mode 100644 accessible/base/TreeWalker.h create mode 100644 accessible/base/XULMap.h create mode 100644 accessible/base/moz.build create mode 100644 accessible/base/nsAccCache.h create mode 100644 accessible/base/nsAccUtils.cpp create mode 100644 accessible/base/nsAccUtils.h create mode 100644 accessible/base/nsAccessibilityService.cpp create mode 100644 accessible/base/nsAccessibilityService.h create mode 100644 accessible/base/nsCoreUtils.cpp create mode 100644 accessible/base/nsCoreUtils.h create mode 100644 accessible/base/nsEventShell.cpp create mode 100644 accessible/base/nsEventShell.h create mode 100644 accessible/base/nsTextEquivUtils.cpp create mode 100644 accessible/base/nsTextEquivUtils.h create mode 100644 accessible/basetypes/Accessible.cpp create mode 100644 accessible/basetypes/Accessible.h create mode 100644 accessible/basetypes/HyperTextAccessibleBase.cpp create mode 100644 accessible/basetypes/HyperTextAccessibleBase.h create mode 100644 accessible/basetypes/TableAccessible.h create mode 100644 accessible/basetypes/TableCellAccessible.h create mode 100644 accessible/basetypes/moz.build create mode 100644 accessible/docs/Architecture.md create mode 100644 accessible/docs/ColorsAndHighContrastMode.md create mode 100644 accessible/docs/DocumentAccessibilityLifecycle.md create mode 100644 accessible/docs/GeckoViewThreadTopography.md create mode 100644 accessible/docs/HCMMediaQueries.md create mode 100644 accessible/docs/HCMSettings.md create mode 100644 accessible/docs/fx_view_fc.jpg create mode 100644 accessible/docs/fxview.jpg create mode 100644 accessible/docs/fxview_ic.jpg create mode 100644 accessible/docs/index.rst create mode 100644 accessible/generic/ARIAGridAccessible.cpp create mode 100644 accessible/generic/ARIAGridAccessible.h create mode 100644 accessible/generic/ApplicationAccessible.cpp create mode 100644 accessible/generic/ApplicationAccessible.h create mode 100644 accessible/generic/BaseAccessibles.cpp create mode 100644 accessible/generic/BaseAccessibles.h create mode 100644 accessible/generic/DocAccessible-inl.h create mode 100644 accessible/generic/DocAccessible.cpp create mode 100644 accessible/generic/DocAccessible.h create mode 100644 accessible/generic/FormControlAccessible.cpp create mode 100644 accessible/generic/FormControlAccessible.h create mode 100644 accessible/generic/HyperTextAccessible-inl.h create mode 100644 accessible/generic/HyperTextAccessible.cpp create mode 100644 accessible/generic/HyperTextAccessible.h create mode 100644 accessible/generic/ImageAccessible.cpp create mode 100644 accessible/generic/ImageAccessible.h create mode 100644 accessible/generic/LocalAccessible-inl.h create mode 100644 accessible/generic/LocalAccessible.cpp create mode 100644 accessible/generic/LocalAccessible.h create mode 100644 accessible/generic/OuterDocAccessible.cpp create mode 100644 accessible/generic/OuterDocAccessible.h create mode 100644 accessible/generic/RootAccessible.cpp create mode 100644 accessible/generic/RootAccessible.h create mode 100644 accessible/generic/TextLeafAccessible.cpp create mode 100644 accessible/generic/TextLeafAccessible.h create mode 100644 accessible/generic/moz.build create mode 100644 accessible/html/HTMLCanvasAccessible.cpp create mode 100644 accessible/html/HTMLCanvasAccessible.h create mode 100644 accessible/html/HTMLElementAccessibles.cpp create mode 100644 accessible/html/HTMLElementAccessibles.h create mode 100644 accessible/html/HTMLFormControlAccessible.cpp create mode 100644 accessible/html/HTMLFormControlAccessible.h create mode 100644 accessible/html/HTMLImageMapAccessible.cpp create mode 100644 accessible/html/HTMLImageMapAccessible.h create mode 100644 accessible/html/HTMLLinkAccessible.cpp create mode 100644 accessible/html/HTMLLinkAccessible.h create mode 100644 accessible/html/HTMLListAccessible.cpp create mode 100644 accessible/html/HTMLListAccessible.h create mode 100644 accessible/html/HTMLSelectAccessible.cpp create mode 100644 accessible/html/HTMLSelectAccessible.h create mode 100644 accessible/html/HTMLTableAccessible.cpp create mode 100644 accessible/html/HTMLTableAccessible.h create mode 100644 accessible/html/moz.build create mode 100644 accessible/interfaces/ia2/IA2Typelib.idl create mode 100644 accessible/interfaces/ia2/moz.build create mode 100644 accessible/interfaces/moz.build create mode 100644 accessible/interfaces/msaa/AccessibleMarshal.def create mode 100644 accessible/interfaces/msaa/AccessibleMarshal.rc create mode 100644 accessible/interfaces/msaa/AccessibleMarshalThunk.c create mode 100644 accessible/interfaces/msaa/ISimpleDOM.idl create mode 100644 accessible/interfaces/msaa/ISimpleDOMDocument.idl create mode 100644 accessible/interfaces/msaa/ISimpleDOMNode.idl create mode 100644 accessible/interfaces/msaa/ISimpleDOMText.idl create mode 100644 accessible/interfaces/msaa/moz.build create mode 100644 accessible/interfaces/nsIAccessibilityService.idl create mode 100644 accessible/interfaces/nsIAccessible.idl create mode 100644 accessible/interfaces/nsIAccessibleAnnouncementEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleApplication.idl create mode 100644 accessible/interfaces/nsIAccessibleCaretMoveEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleDocument.idl create mode 100644 accessible/interfaces/nsIAccessibleEditableText.idl create mode 100644 accessible/interfaces/nsIAccessibleEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleHideEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleHyperLink.idl create mode 100644 accessible/interfaces/nsIAccessibleHyperText.idl create mode 100644 accessible/interfaces/nsIAccessibleImage.idl create mode 100644 accessible/interfaces/nsIAccessibleMacInterface.idl create mode 100644 accessible/interfaces/nsIAccessibleObjectAttributeChangedEvent.idl create mode 100644 accessible/interfaces/nsIAccessiblePivot.idl create mode 100644 accessible/interfaces/nsIAccessibleRelation.idl create mode 100644 accessible/interfaces/nsIAccessibleRole.idl create mode 100644 accessible/interfaces/nsIAccessibleScrollingEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleSelectable.idl create mode 100644 accessible/interfaces/nsIAccessibleStateChangeEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleStates.idl create mode 100644 accessible/interfaces/nsIAccessibleTable.idl create mode 100644 accessible/interfaces/nsIAccessibleTableChangeEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleText.idl create mode 100644 accessible/interfaces/nsIAccessibleTextChangeEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleTextLeafRange.idl create mode 100644 accessible/interfaces/nsIAccessibleTextRange.idl create mode 100644 accessible/interfaces/nsIAccessibleTextSelectionChangeEvent.idl create mode 100644 accessible/interfaces/nsIAccessibleTypes.idl create mode 100644 accessible/interfaces/nsIAccessibleValue.idl create mode 100644 accessible/ipc/DocAccessibleChild.cpp create mode 100644 accessible/ipc/DocAccessibleChild.h create mode 100644 accessible/ipc/DocAccessibleParent.cpp create mode 100644 accessible/ipc/DocAccessibleParent.h create mode 100644 accessible/ipc/DocAccessibleTypes.ipdlh create mode 100644 accessible/ipc/IPCTypes.h create mode 100644 accessible/ipc/PDocAccessible.ipdl create mode 100644 accessible/ipc/RemoteAccessible.cpp create mode 100644 accessible/ipc/RemoteAccessible.h create mode 100644 accessible/ipc/moz.build create mode 100644 accessible/mac/.clang-format create mode 100644 accessible/mac/AccessibleWrap.h create mode 100644 accessible/mac/AccessibleWrap.mm create mode 100644 accessible/mac/ApplicationAccessibleWrap.h create mode 100644 accessible/mac/DocAccessibleWrap.h create mode 100644 accessible/mac/DocAccessibleWrap.mm create mode 100644 accessible/mac/GeckoTextMarker.h create mode 100644 accessible/mac/GeckoTextMarker.mm create mode 100644 accessible/mac/MOXAccessibleBase.h create mode 100644 accessible/mac/MOXAccessibleBase.mm create mode 100644 accessible/mac/MOXAccessibleProtocol.h create mode 100644 accessible/mac/MOXLandmarkAccessibles.h create mode 100644 accessible/mac/MOXLandmarkAccessibles.mm create mode 100644 accessible/mac/MOXMathAccessibles.h create mode 100644 accessible/mac/MOXMathAccessibles.mm create mode 100644 accessible/mac/MOXSearchInfo.h create mode 100644 accessible/mac/MOXSearchInfo.mm create mode 100644 accessible/mac/MOXTextMarkerDelegate.h create mode 100644 accessible/mac/MOXTextMarkerDelegate.mm create mode 100644 accessible/mac/MOXWebAreaAccessible.h create mode 100644 accessible/mac/MOXWebAreaAccessible.mm create mode 100644 accessible/mac/MacUtils.h create mode 100644 accessible/mac/MacUtils.mm create mode 100644 accessible/mac/Platform.mm create mode 100644 accessible/mac/PlatformExtTypes.h create mode 100644 accessible/mac/RootAccessibleWrap.h create mode 100644 accessible/mac/RootAccessibleWrap.mm create mode 100644 accessible/mac/RotorRules.h create mode 100644 accessible/mac/RotorRules.mm create mode 100755 accessible/mac/SelectorMapGen.py create mode 100644 accessible/mac/moz.build create mode 100644 accessible/mac/mozAccessible.h create mode 100644 accessible/mac/mozAccessible.mm create mode 100644 accessible/mac/mozAccessibleProtocol.h create mode 100644 accessible/mac/mozActionElements.h create mode 100644 accessible/mac/mozActionElements.mm create mode 100644 accessible/mac/mozHTMLAccessible.h create mode 100644 accessible/mac/mozHTMLAccessible.mm create mode 100644 accessible/mac/mozRootAccessible.h create mode 100644 accessible/mac/mozRootAccessible.mm create mode 100644 accessible/mac/mozSelectableElements.h create mode 100644 accessible/mac/mozSelectableElements.mm create mode 100644 accessible/mac/mozTableAccessible.h create mode 100644 accessible/mac/mozTableAccessible.mm create mode 100644 accessible/mac/mozTextAccessible.h create mode 100644 accessible/mac/mozTextAccessible.mm create mode 100644 accessible/moz.build create mode 100644 accessible/other/AccessibleWrap.cpp create mode 100644 accessible/other/AccessibleWrap.h create mode 100644 accessible/other/ApplicationAccessibleWrap.h create mode 100644 accessible/other/DocAccessibleWrap.h create mode 100644 accessible/other/Platform.cpp create mode 100644 accessible/other/RootAccessibleWrap.h create mode 100644 accessible/other/moz.build create mode 100644 accessible/tests/browser/.eslintrc.js create mode 100644 accessible/tests/browser/Common.sys.mjs create mode 100644 accessible/tests/browser/Layout.sys.mjs create mode 100644 accessible/tests/browser/atk/a11y_setup.py create mode 100644 accessible/tests/browser/atk/browser.toml create mode 100644 accessible/tests/browser/atk/browser_role.js create mode 100644 accessible/tests/browser/atk/browser_table.js create mode 100644 accessible/tests/browser/atk/head.js create mode 100644 accessible/tests/browser/bounds/browser.toml create mode 100644 accessible/tests/browser/bounds/browser_accessible_moved.js create mode 100644 accessible/tests/browser/bounds/browser_caret_rect.js create mode 100644 accessible/tests/browser/bounds/browser_position.js create mode 100644 accessible/tests/browser/bounds/browser_test_display_contents.js create mode 100644 accessible/tests/browser/bounds/browser_test_iframe_transform.js create mode 100644 accessible/tests/browser/bounds/browser_test_resolution.js create mode 100644 accessible/tests/browser/bounds/browser_test_simple_transform.js create mode 100644 accessible/tests/browser/bounds/browser_test_zoom.js create mode 100644 accessible/tests/browser/bounds/browser_test_zoom_text.js create mode 100644 accessible/tests/browser/bounds/browser_zero_area.js create mode 100644 accessible/tests/browser/bounds/head.js create mode 100644 accessible/tests/browser/browser.toml create mode 100644 accessible/tests/browser/browser_shutdown_acc_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_doc_acc_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js create mode 100644 accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js create mode 100644 accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js create mode 100644 accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js create mode 100644 accessible/tests/browser/browser_shutdown_multi_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_parent_own_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_pref.js create mode 100644 accessible/tests/browser/browser_shutdown_proxy_acc_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_remote_no_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_remote_only.js create mode 100644 accessible/tests/browser/browser_shutdown_remote_own_reference.js create mode 100644 accessible/tests/browser/browser_shutdown_scope_lifecycle.js create mode 100644 accessible/tests/browser/browser_shutdown_start_restart.js create mode 100644 accessible/tests/browser/e10s/browser.toml create mode 100644 accessible/tests/browser/e10s/browser_caching_actions.js create mode 100644 accessible/tests/browser/e10s/browser_caching_attributes.js create mode 100644 accessible/tests/browser/e10s/browser_caching_description.js create mode 100644 accessible/tests/browser/e10s/browser_caching_document_props.js create mode 100644 accessible/tests/browser/e10s/browser_caching_domnodeid.js create mode 100644 accessible/tests/browser/e10s/browser_caching_hyperlink.js create mode 100644 accessible/tests/browser/e10s/browser_caching_innerHTML.js create mode 100644 accessible/tests/browser/e10s/browser_caching_interfaces.js create mode 100644 accessible/tests/browser/e10s/browser_caching_large_update.js create mode 100644 accessible/tests/browser/e10s/browser_caching_name.js create mode 100644 accessible/tests/browser/e10s/browser_caching_position.js create mode 100644 accessible/tests/browser/e10s/browser_caching_relations.js create mode 100644 accessible/tests/browser/e10s/browser_caching_relations_002.js create mode 100644 accessible/tests/browser/e10s/browser_caching_states.js create mode 100644 accessible/tests/browser/e10s/browser_caching_table.js create mode 100644 accessible/tests/browser/e10s/browser_caching_text_bounds.js create mode 100644 accessible/tests/browser/e10s/browser_caching_uniqueid.js create mode 100644 accessible/tests/browser/e10s/browser_caching_value.js create mode 100644 accessible/tests/browser/e10s/browser_events_announcement.js create mode 100644 accessible/tests/browser/e10s/browser_events_caretmove.js create mode 100644 accessible/tests/browser/e10s/browser_events_hide.js create mode 100644 accessible/tests/browser/e10s/browser_events_show.js create mode 100644 accessible/tests/browser/e10s/browser_events_statechange.js create mode 100644 accessible/tests/browser/e10s/browser_events_textchange.js create mode 100644 accessible/tests/browser/e10s/browser_file_input.js create mode 100644 accessible/tests/browser/e10s/browser_language.js create mode 100644 accessible/tests/browser/e10s/browser_obj_group.js create mode 100644 accessible/tests/browser/e10s/browser_obj_group_002.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_canvas.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_csscontentvisibility.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_doc.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_gencontent.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_hidden.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_image.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_imagemap.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_list.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_listener.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_move.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_optgroup.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_removal.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_table.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_textleaf.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_visibility.js create mode 100644 accessible/tests/browser/e10s/browser_treeupdate_whitespace.js create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_imagemap.html create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_visibility.html create mode 100644 accessible/tests/browser/e10s/doc_treeupdate_whitespace.html create mode 100644 accessible/tests/browser/e10s/fonts/Ahem.sjs create mode 100644 accessible/tests/browser/e10s/head.js create mode 100644 accessible/tests/browser/events/browser.toml create mode 100644 accessible/tests/browser/events/browser_alert.js create mode 100644 accessible/tests/browser/events/browser_test_A11yUtils_announce.js create mode 100644 accessible/tests/browser/events/browser_test_caret_move_granularity.js create mode 100644 accessible/tests/browser/events/browser_test_docload.js create mode 100644 accessible/tests/browser/events/browser_test_focus_browserui.js create mode 100644 accessible/tests/browser/events/browser_test_focus_dialog.js create mode 100644 accessible/tests/browser/events/browser_test_focus_urlbar.js create mode 100644 accessible/tests/browser/events/browser_test_panel.js create mode 100644 accessible/tests/browser/events/browser_test_scrolling.js create mode 100644 accessible/tests/browser/events/browser_test_selection_urlbar.js create mode 100644 accessible/tests/browser/events/browser_test_textcaret.js create mode 100644 accessible/tests/browser/events/head.js create mode 100644 accessible/tests/browser/fission/browser.toml create mode 100644 accessible/tests/browser/fission/browser_content_tree.js create mode 100644 accessible/tests/browser/fission/browser_hidden_iframe.js create mode 100644 accessible/tests/browser/fission/browser_nested_iframe.js create mode 100644 accessible/tests/browser/fission/browser_reframe_root.js create mode 100644 accessible/tests/browser/fission/browser_reframe_visibility.js create mode 100644 accessible/tests/browser/fission/browser_src_change.js create mode 100644 accessible/tests/browser/fission/browser_take_focus.js create mode 100644 accessible/tests/browser/fission/head.js create mode 100644 accessible/tests/browser/general/browser.toml create mode 100644 accessible/tests/browser/general/browser_test_doc_creation.js create mode 100644 accessible/tests/browser/general/browser_test_urlbar.js create mode 100644 accessible/tests/browser/general/head.js create mode 100644 accessible/tests/browser/head.js create mode 100644 accessible/tests/browser/hittest/browser.toml create mode 100644 accessible/tests/browser/hittest/browser_test_browser.js create mode 100644 accessible/tests/browser/hittest/browser_test_general.js create mode 100644 accessible/tests/browser/hittest/browser_test_scroll_hittest.js create mode 100644 accessible/tests/browser/hittest/browser_test_shadowroot.js create mode 100644 accessible/tests/browser/hittest/browser_test_text.js create mode 100644 accessible/tests/browser/hittest/browser_test_zoom.js create mode 100644 accessible/tests/browser/hittest/browser_test_zoom_text.js create mode 100644 accessible/tests/browser/hittest/head.js create mode 100644 accessible/tests/browser/mac/browser.toml create mode 100644 accessible/tests/browser/mac/browser_app.js create mode 100644 accessible/tests/browser/mac/browser_aria_busy.js create mode 100644 accessible/tests/browser/mac/browser_aria_controls_flowto.js create mode 100644 accessible/tests/browser/mac/browser_aria_current.js create mode 100644 accessible/tests/browser/mac/browser_aria_expanded.js create mode 100644 accessible/tests/browser/mac/browser_aria_haspopup.js create mode 100644 accessible/tests/browser/mac/browser_aria_setsize.js create mode 100644 accessible/tests/browser/mac/browser_attributed_text.js create mode 100644 accessible/tests/browser/mac/browser_bounds.js create mode 100644 accessible/tests/browser/mac/browser_details_summary.js create mode 100644 accessible/tests/browser/mac/browser_focus.js create mode 100644 accessible/tests/browser/mac/browser_heading.js create mode 100644 accessible/tests/browser/mac/browser_hierarchy.js create mode 100644 accessible/tests/browser/mac/browser_input.js create mode 100644 accessible/tests/browser/mac/browser_label_title.js create mode 100644 accessible/tests/browser/mac/browser_link.js create mode 100644 accessible/tests/browser/mac/browser_live_regions.js create mode 100644 accessible/tests/browser/mac/browser_mathml.js create mode 100644 accessible/tests/browser/mac/browser_menulist.js create mode 100644 accessible/tests/browser/mac/browser_navigate.js create mode 100644 accessible/tests/browser/mac/browser_outline.js create mode 100644 accessible/tests/browser/mac/browser_outline_xul.js create mode 100644 accessible/tests/browser/mac/browser_popupbutton.js create mode 100644 accessible/tests/browser/mac/browser_radio_position.js create mode 100644 accessible/tests/browser/mac/browser_range.js create mode 100644 accessible/tests/browser/mac/browser_required.js create mode 100644 accessible/tests/browser/mac/browser_rich_listbox.js create mode 100644 accessible/tests/browser/mac/browser_roles_elements.js create mode 100644 accessible/tests/browser/mac/browser_rootgroup.js create mode 100644 accessible/tests/browser/mac/browser_rotor.js create mode 100644 accessible/tests/browser/mac/browser_selectables.js create mode 100644 accessible/tests/browser/mac/browser_table.js create mode 100644 accessible/tests/browser/mac/browser_text_basics.js create mode 100644 accessible/tests/browser/mac/browser_text_input.js create mode 100644 accessible/tests/browser/mac/browser_text_leaf.js create mode 100644 accessible/tests/browser/mac/browser_text_selection.js create mode 100644 accessible/tests/browser/mac/browser_toggle_radio_check.js create mode 100644 accessible/tests/browser/mac/browser_webarea.js create mode 100644 accessible/tests/browser/mac/doc_aria_tabs.html create mode 100644 accessible/tests/browser/mac/doc_menulist.xhtml create mode 100644 accessible/tests/browser/mac/doc_rich_listbox.xhtml create mode 100644 accessible/tests/browser/mac/doc_textmarker_test.html create mode 100644 accessible/tests/browser/mac/doc_tree.xhtml create mode 100644 accessible/tests/browser/mac/head.js create mode 100644 accessible/tests/browser/pivot/browser.toml create mode 100644 accessible/tests/browser/pivot/browser_pivot.js create mode 100644 accessible/tests/browser/pivot/head.js create mode 100644 accessible/tests/browser/python_runner_wsh.py create mode 100644 accessible/tests/browser/role/browser.toml create mode 100644 accessible/tests/browser/role/browser_computedARIARole.js create mode 100644 accessible/tests/browser/role/browser_minimumRole.js create mode 100644 accessible/tests/browser/role/head.js create mode 100644 accessible/tests/browser/scroll/browser.toml create mode 100644 accessible/tests/browser/scroll/browser_scrollToPoint.js create mode 100644 accessible/tests/browser/scroll/browser_test_scrollTo.js create mode 100644 accessible/tests/browser/scroll/browser_test_scroll_bounds.js create mode 100644 accessible/tests/browser/scroll/browser_test_scroll_substring.js create mode 100644 accessible/tests/browser/scroll/browser_test_zoom_text.js create mode 100644 accessible/tests/browser/scroll/head.js create mode 100644 accessible/tests/browser/selectable/browser.toml create mode 100644 accessible/tests/browser/selectable/browser_test_aria_select.js create mode 100644 accessible/tests/browser/selectable/browser_test_select.js create mode 100644 accessible/tests/browser/selectable/head.js create mode 100644 accessible/tests/browser/shared-head.js create mode 100644 accessible/tests/browser/states/browser.toml create mode 100644 accessible/tests/browser/states/browser_test_link.js create mode 100644 accessible/tests/browser/states/browser_test_select_visibility.js create mode 100644 accessible/tests/browser/states/browser_test_visibility.js create mode 100644 accessible/tests/browser/states/browser_test_visibility_2.js create mode 100644 accessible/tests/browser/states/head.js create mode 100644 accessible/tests/browser/telemetry/browser.toml create mode 100644 accessible/tests/browser/telemetry/browser_HCM_telemetry.js create mode 100644 accessible/tests/browser/text/browser.toml create mode 100644 accessible/tests/browser/text/browser_editabletext.js create mode 100644 accessible/tests/browser/text/browser_text.js create mode 100644 accessible/tests/browser/text/browser_text_caret.js create mode 100644 accessible/tests/browser/text/browser_text_paragraph_boundary.js create mode 100644 accessible/tests/browser/text/browser_text_selection.js create mode 100644 accessible/tests/browser/text/browser_text_spelling.js create mode 100644 accessible/tests/browser/text/browser_textleafpoint.js create mode 100644 accessible/tests/browser/text/head.js create mode 100644 accessible/tests/browser/tree/browser.toml create mode 100644 accessible/tests/browser/tree/browser_aria_owns.js create mode 100644 accessible/tests/browser/tree/browser_browser_element.js create mode 100644 accessible/tests/browser/tree/browser_css_content_visibility.js create mode 100644 accessible/tests/browser/tree/browser_general.js create mode 100644 accessible/tests/browser/tree/browser_lazy_tabs.js create mode 100644 accessible/tests/browser/tree/browser_link.js create mode 100644 accessible/tests/browser/tree/browser_searchbar.js create mode 100644 accessible/tests/browser/tree/browser_shadowdom.js create mode 100644 accessible/tests/browser/tree/browser_test_nsIAccessibleDocument_URL.js create mode 100644 accessible/tests/browser/tree/head.js create mode 100644 accessible/tests/browser/windows/a11y_setup.py create mode 100644 accessible/tests/browser/windows/a11y_setup_requirements.txt create mode 100644 accessible/tests/browser/windows/ia2/browser.toml create mode 100644 accessible/tests/browser/windows/ia2/browser_role.js create mode 100644 accessible/tests/browser/windows/ia2/head.js create mode 100644 accessible/tests/browser/windows/uia/browser.toml create mode 100644 accessible/tests/browser/windows/uia/browser_controlType.js create mode 100644 accessible/tests/browser/windows/uia/browser_elementFromPoint.js create mode 100644 accessible/tests/browser/windows/uia/head.js create mode 100644 accessible/tests/crashtests/1072792.xhtml create mode 100644 accessible/tests/crashtests/1380199.html create mode 100644 accessible/tests/crashtests/1402999.html create mode 100644 accessible/tests/crashtests/1415667.html create mode 100644 accessible/tests/crashtests/1463962.html create mode 100644 accessible/tests/crashtests/1472024-1.html create mode 100644 accessible/tests/crashtests/1472024-2.html create mode 100644 accessible/tests/crashtests/1484778.html create mode 100644 accessible/tests/crashtests/1494707.html create mode 100644 accessible/tests/crashtests/1503964.html create mode 100644 accessible/tests/crashtests/1572811.html create mode 100644 accessible/tests/crashtests/1578282.html create mode 100644 accessible/tests/crashtests/1585851.html create mode 100644 accessible/tests/crashtests/1655983.html create mode 100644 accessible/tests/crashtests/1838250.html create mode 100644 accessible/tests/crashtests/448064.xhtml create mode 100644 accessible/tests/crashtests/884202.html create mode 100644 accessible/tests/crashtests/890760.html create mode 100644 accessible/tests/crashtests/893515.html create mode 100644 accessible/tests/crashtests/crashtests.list create mode 100644 accessible/tests/crashtests/last_test_to_unload_testsuite.xhtml create mode 100644 accessible/tests/mochitest/.eslintrc.js create mode 100644 accessible/tests/mochitest/a11y.toml create mode 100644 accessible/tests/mochitest/actions.js create mode 100644 accessible/tests/mochitest/actions/a11y.toml create mode 100644 accessible/tests/mochitest/actions/test_anchors.html create mode 100644 accessible/tests/mochitest/actions/test_aria.html create mode 100644 accessible/tests/mochitest/actions/test_controls.html create mode 100644 accessible/tests/mochitest/actions/test_general.html create mode 100644 accessible/tests/mochitest/actions/test_general.xhtml create mode 100644 accessible/tests/mochitest/actions/test_keys.html create mode 100644 accessible/tests/mochitest/actions/test_keys.xhtml create mode 100644 accessible/tests/mochitest/actions/test_media.html create mode 100644 accessible/tests/mochitest/actions/test_select.html create mode 100644 accessible/tests/mochitest/actions/test_tree.xhtml create mode 100644 accessible/tests/mochitest/actions/test_treegrid.xhtml create mode 100644 accessible/tests/mochitest/aom/a11y.toml create mode 100644 accessible/tests/mochitest/aom/test_general.html create mode 100644 accessible/tests/mochitest/attributes.js create mode 100644 accessible/tests/mochitest/attributes/a11y.toml create mode 100644 accessible/tests/mochitest/attributes/test_dpub_aria_xml-roles.html create mode 100644 accessible/tests/mochitest/attributes/test_graphics_aria_xml-roles.html create mode 100644 accessible/tests/mochitest/attributes/test_listbox.html create mode 100644 accessible/tests/mochitest/attributes/test_obj.html create mode 100644 accessible/tests/mochitest/attributes/test_obj_css.html create mode 100644 accessible/tests/mochitest/attributes/test_obj_group.html create mode 100644 accessible/tests/mochitest/attributes/test_obj_group.xhtml create mode 100644 accessible/tests/mochitest/attributes/test_obj_group_tree.xhtml create mode 100644 accessible/tests/mochitest/attributes/test_tag.html create mode 100644 accessible/tests/mochitest/attributes/test_xml-roles.html create mode 100644 accessible/tests/mochitest/autocomplete.js create mode 100644 accessible/tests/mochitest/bounds/a11y.toml create mode 100644 accessible/tests/mochitest/bounds/test_list.html create mode 100644 accessible/tests/mochitest/browser.js create mode 100644 accessible/tests/mochitest/common.js create mode 100644 accessible/tests/mochitest/dumbfile.zip create mode 100644 accessible/tests/mochitest/elm/a11y.toml create mode 100644 accessible/tests/mochitest/elm/test_HTMLSpec.html create mode 100644 accessible/tests/mochitest/elm/test_MathMLSpec.html create mode 100644 accessible/tests/mochitest/elm/test_figure.html create mode 100644 accessible/tests/mochitest/elm/test_listbox.xhtml create mode 100644 accessible/tests/mochitest/elm/test_nsApplicationAcc.html create mode 100644 accessible/tests/mochitest/elm/test_shadowroot.html create mode 100644 accessible/tests/mochitest/elm/test_shadowroot_subframe.html create mode 100644 accessible/tests/mochitest/events.js create mode 100644 accessible/tests/mochitest/events/a11y.toml create mode 100644 accessible/tests/mochitest/events/docload/a11y.toml create mode 100644 accessible/tests/mochitest/events/docload/docload_wnd.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_aria.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_busy.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_embedded.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_iframe.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_root.html create mode 100644 accessible/tests/mochitest/events/docload/test_docload_shutdown.html create mode 100644 accessible/tests/mochitest/events/focus.html create mode 100644 accessible/tests/mochitest/events/scroll.html create mode 100644 accessible/tests/mochitest/events/slow_image.sjs create mode 100644 accessible/tests/mochitest/events/test_announcement.html create mode 100644 accessible/tests/mochitest/events/test_aria_alert.html create mode 100644 accessible/tests/mochitest/events/test_aria_menu.html create mode 100644 accessible/tests/mochitest/events/test_aria_objattr.html create mode 100644 accessible/tests/mochitest/events/test_aria_owns.html create mode 100644 accessible/tests/mochitest/events/test_aria_statechange.html create mode 100644 accessible/tests/mochitest/events/test_attrchange.html create mode 100644 accessible/tests/mochitest/events/test_attrs.html create mode 100644 accessible/tests/mochitest/events/test_bug1322593-2.html create mode 100644 accessible/tests/mochitest/events/test_bug1322593.html create mode 100644 accessible/tests/mochitest/events/test_caretmove.html create mode 100644 accessible/tests/mochitest/events/test_coalescence.html create mode 100644 accessible/tests/mochitest/events/test_contextmenu.html create mode 100644 accessible/tests/mochitest/events/test_descrchange.html create mode 100644 accessible/tests/mochitest/events/test_dragndrop.html create mode 100644 accessible/tests/mochitest/events/test_flush.html create mode 100644 accessible/tests/mochitest/events/test_focus_aria_activedescendant.html create mode 100644 accessible/tests/mochitest/events/test_focus_autocomplete.html create mode 100644 accessible/tests/mochitest/events/test_focus_autocomplete.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_canvas.html create mode 100644 accessible/tests/mochitest/events/test_focus_contextmenu.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_controls.html create mode 100644 accessible/tests/mochitest/events/test_focus_doc.html create mode 100644 accessible/tests/mochitest/events/test_focus_general.html create mode 100644 accessible/tests/mochitest/events/test_focus_general.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_listcontrols.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_menu.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_name.html create mode 100644 accessible/tests/mochitest/events/test_focus_removal.html create mode 100644 accessible/tests/mochitest/events/test_focus_selects.html create mode 100644 accessible/tests/mochitest/events/test_focus_tabbox.xhtml create mode 100644 accessible/tests/mochitest/events/test_focus_tree.xhtml create mode 100644 accessible/tests/mochitest/events/test_focusable_statechange.html create mode 100644 accessible/tests/mochitest/events/test_fromUserInput.html create mode 100644 accessible/tests/mochitest/events/test_label.xhtml create mode 100644 accessible/tests/mochitest/events/test_menu.xhtml create mode 100644 accessible/tests/mochitest/events/test_mutation.html create mode 100644 accessible/tests/mochitest/events/test_namechange.html create mode 100644 accessible/tests/mochitest/events/test_namechange.xhtml create mode 100644 accessible/tests/mochitest/events/test_scroll.xhtml create mode 100644 accessible/tests/mochitest/events/test_scroll_caret.xhtml create mode 100644 accessible/tests/mochitest/events/test_selection.html create mode 100644 accessible/tests/mochitest/events/test_selection.xhtml create mode 100644 accessible/tests/mochitest/events/test_selection_aria.html create mode 100644 accessible/tests/mochitest/events/test_statechange.html create mode 100644 accessible/tests/mochitest/events/test_statechange.xhtml create mode 100644 accessible/tests/mochitest/events/test_text.html create mode 100644 accessible/tests/mochitest/events/test_text_alg.html create mode 100644 accessible/tests/mochitest/events/test_textattrchange.html create mode 100644 accessible/tests/mochitest/events/test_textselchange.html create mode 100644 accessible/tests/mochitest/events/test_tree.xhtml create mode 100644 accessible/tests/mochitest/events/test_valuechange.html create mode 100644 accessible/tests/mochitest/focus/a11y.toml create mode 100644 accessible/tests/mochitest/focus/test_focus_radio.xhtml create mode 100644 accessible/tests/mochitest/focus/test_focusedChild.html create mode 100644 accessible/tests/mochitest/focus/test_takeFocus.html create mode 100644 accessible/tests/mochitest/focus/test_takeFocus.xhtml create mode 100644 accessible/tests/mochitest/formimage.png create mode 100644 accessible/tests/mochitest/grid.js create mode 100644 accessible/tests/mochitest/hittest/a11y.toml create mode 100644 accessible/tests/mochitest/hittest/test_browser.html create mode 100644 accessible/tests/mochitest/hittest/test_general.html create mode 100644 accessible/tests/mochitest/hittest/test_menu.xhtml create mode 100644 accessible/tests/mochitest/hittest/test_shadowroot.html create mode 100644 accessible/tests/mochitest/hittest/test_shadowroot_subframe.html create mode 100644 accessible/tests/mochitest/hittest/test_zoom.html create mode 100644 accessible/tests/mochitest/hittest/test_zoom_text.html create mode 100644 accessible/tests/mochitest/hittest/test_zoom_tree.xhtml create mode 100644 accessible/tests/mochitest/hittest/zoom_tree.xhtml create mode 100644 accessible/tests/mochitest/hyperlink/a11y.toml create mode 100644 accessible/tests/mochitest/hyperlink/hyperlink.js create mode 100644 accessible/tests/mochitest/hyperlink/test_general.html create mode 100644 accessible/tests/mochitest/hyperlink/test_general.xhtml create mode 100644 accessible/tests/mochitest/hypertext/a11y.toml create mode 100644 accessible/tests/mochitest/hypertext/test_general.html create mode 100644 accessible/tests/mochitest/hypertext/test_update.html create mode 100644 accessible/tests/mochitest/layout.js create mode 100644 accessible/tests/mochitest/letters.gif create mode 100644 accessible/tests/mochitest/longdesc_src.html create mode 100644 accessible/tests/mochitest/moz.build create mode 100644 accessible/tests/mochitest/moz.png create mode 100644 accessible/tests/mochitest/name.js create mode 100644 accessible/tests/mochitest/name/a11y.toml create mode 100644 accessible/tests/mochitest/name/markup.js create mode 100644 accessible/tests/mochitest/name/markuprules.xml create mode 100644 accessible/tests/mochitest/name/test_ARIACore_examples.html create mode 100644 accessible/tests/mochitest/name/test_browserui.xhtml create mode 100644 accessible/tests/mochitest/name/test_counterstyle.html create mode 100644 accessible/tests/mochitest/name/test_general.html create mode 100644 accessible/tests/mochitest/name/test_general.xhtml create mode 100644 accessible/tests/mochitest/name/test_link.html create mode 100644 accessible/tests/mochitest/name/test_list.html create mode 100644 accessible/tests/mochitest/name/test_markup.html create mode 100644 accessible/tests/mochitest/name/test_svg.html create mode 100644 accessible/tests/mochitest/name/test_tree.xhtml create mode 100644 accessible/tests/mochitest/promisified-events.js create mode 100644 accessible/tests/mochitest/relations.js create mode 100644 accessible/tests/mochitest/relations/a11y.toml create mode 100644 accessible/tests/mochitest/relations/test_embeds.xhtml create mode 100644 accessible/tests/mochitest/relations/test_general.html create mode 100644 accessible/tests/mochitest/relations/test_general.xhtml create mode 100644 accessible/tests/mochitest/relations/test_groupInfoUpdate.html create mode 100644 accessible/tests/mochitest/relations/test_shadowdom.html create mode 100644 accessible/tests/mochitest/relations/test_tabbrowser.xhtml create mode 100644 accessible/tests/mochitest/relations/test_tree.xhtml create mode 100644 accessible/tests/mochitest/relations/test_ui_modalprompt.html create mode 100644 accessible/tests/mochitest/relations/test_update.html create mode 100644 accessible/tests/mochitest/role.js create mode 100644 accessible/tests/mochitest/role/a11y.toml create mode 100644 accessible/tests/mochitest/role/chrome_body_role_alert.xhtml create mode 100644 accessible/tests/mochitest/role/test_aria.html create mode 100644 accessible/tests/mochitest/role/test_aria.xhtml create mode 100644 accessible/tests/mochitest/role/test_dpub_aria.html create mode 100644 accessible/tests/mochitest/role/test_general.html create mode 100644 accessible/tests/mochitest/role/test_general.xhtml create mode 100644 accessible/tests/mochitest/role/test_graphics_aria.html create mode 100644 accessible/tests/mochitest/role/test_svg.html create mode 100644 accessible/tests/mochitest/scroll/a11y.toml create mode 100644 accessible/tests/mochitest/scroll/test_zoom.html create mode 100644 accessible/tests/mochitest/selectable.js create mode 100644 accessible/tests/mochitest/selectable/a11y.toml create mode 100644 accessible/tests/mochitest/selectable/test_listbox.xhtml create mode 100644 accessible/tests/mochitest/selectable/test_menu.xhtml create mode 100644 accessible/tests/mochitest/selectable/test_menulist.xhtml create mode 100644 accessible/tests/mochitest/selectable/test_tabs.xhtml create mode 100644 accessible/tests/mochitest/selectable/test_tree.xhtml create mode 100644 accessible/tests/mochitest/states.js create mode 100644 accessible/tests/mochitest/states/a11y.toml create mode 100644 accessible/tests/mochitest/states/test_aria.html create mode 100644 accessible/tests/mochitest/states/test_aria.xhtml create mode 100644 accessible/tests/mochitest/states/test_aria_imgmap.html create mode 100644 accessible/tests/mochitest/states/test_aria_widgetitems.html create mode 100644 accessible/tests/mochitest/states/test_buttons.html create mode 100644 accessible/tests/mochitest/states/test_controls.html create mode 100644 accessible/tests/mochitest/states/test_controls.xhtml create mode 100644 accessible/tests/mochitest/states/test_doc.html create mode 100644 accessible/tests/mochitest/states/test_doc_busy.html create mode 100644 accessible/tests/mochitest/states/test_docarticle.html create mode 100644 accessible/tests/mochitest/states/test_editablebody.html create mode 100644 accessible/tests/mochitest/states/test_expandable.xhtml create mode 100644 accessible/tests/mochitest/states/test_frames.html create mode 100644 accessible/tests/mochitest/states/test_inputs.html create mode 100644 accessible/tests/mochitest/states/test_link.html create mode 100644 accessible/tests/mochitest/states/test_popup.xhtml create mode 100644 accessible/tests/mochitest/states/test_selects.html create mode 100644 accessible/tests/mochitest/states/test_stale.html create mode 100644 accessible/tests/mochitest/states/test_tabs.xhtml create mode 100644 accessible/tests/mochitest/states/test_textbox.xhtml create mode 100644 accessible/tests/mochitest/states/test_tree.xhtml create mode 100644 accessible/tests/mochitest/states/test_visibility.html create mode 100644 accessible/tests/mochitest/states/test_visibility.xhtml create mode 100644 accessible/tests/mochitest/states/z_frames.html create mode 100644 accessible/tests/mochitest/states/z_frames_article.html create mode 100644 accessible/tests/mochitest/states/z_frames_checkbox.html create mode 100644 accessible/tests/mochitest/states/z_frames_textbox.html create mode 100644 accessible/tests/mochitest/states/z_frames_update.html create mode 100644 accessible/tests/mochitest/table.js create mode 100644 accessible/tests/mochitest/table/a11y.toml create mode 100644 accessible/tests/mochitest/table/test_css_tables.html create mode 100644 accessible/tests/mochitest/table/test_headers_ariagrid.html create mode 100644 accessible/tests/mochitest/table/test_headers_ariatable.html create mode 100644 accessible/tests/mochitest/table/test_headers_table.html create mode 100644 accessible/tests/mochitest/table/test_headers_tree.xhtml create mode 100644 accessible/tests/mochitest/table/test_indexes_ariagrid.html create mode 100644 accessible/tests/mochitest/table/test_indexes_table.html create mode 100644 accessible/tests/mochitest/table/test_indexes_tree.xhtml create mode 100644 accessible/tests/mochitest/table/test_layoutguess.html create mode 100644 accessible/tests/mochitest/table/test_mtable.html create mode 100644 accessible/tests/mochitest/table/test_sels_ariagrid.html create mode 100644 accessible/tests/mochitest/table/test_sels_table.html create mode 100644 accessible/tests/mochitest/table/test_sels_tree.xhtml create mode 100644 accessible/tests/mochitest/table/test_struct_ariagrid.html create mode 100644 accessible/tests/mochitest/table/test_struct_ariatreegrid.html create mode 100644 accessible/tests/mochitest/table/test_struct_table.html create mode 100644 accessible/tests/mochitest/table/test_struct_tree.xhtml create mode 100644 accessible/tests/mochitest/table/test_table_1.html create mode 100644 accessible/tests/mochitest/table/test_table_2.html create mode 100644 accessible/tests/mochitest/table/test_table_mutation.html create mode 100644 accessible/tests/mochitest/test_OuterDocAccessible.html create mode 100644 accessible/tests/mochitest/test_aria_token_attrs.html create mode 100644 accessible/tests/mochitest/test_bug420863.html create mode 100644 accessible/tests/mochitest/test_custom_element_accessibility_defaults.html create mode 100644 accessible/tests/mochitest/test_descr.html create mode 100644 accessible/tests/mochitest/test_nsIAccessibleDocument.html create mode 100644 accessible/tests/mochitest/test_nsIAccessibleImage.html create mode 100644 accessible/tests/mochitest/text.js create mode 100644 accessible/tests/mochitest/text/a11y.toml create mode 100644 accessible/tests/mochitest/text/doc.html create mode 100644 accessible/tests/mochitest/text/test_atcaretoffset.html create mode 100644 accessible/tests/mochitest/text/test_charboundary.html create mode 100644 accessible/tests/mochitest/text/test_doc.html create mode 100644 accessible/tests/mochitest/text/test_dynamic.html create mode 100644 accessible/tests/mochitest/text/test_general.xhtml create mode 100644 accessible/tests/mochitest/text/test_gettext.html create mode 100644 accessible/tests/mochitest/text/test_hypertext.html create mode 100644 accessible/tests/mochitest/text/test_lineboundary.html create mode 100644 accessible/tests/mochitest/text/test_paragraphboundary.html create mode 100644 accessible/tests/mochitest/text/test_passwords.html create mode 100644 accessible/tests/mochitest/text/test_selection.html create mode 100644 accessible/tests/mochitest/text/test_settext_input_event.html create mode 100644 accessible/tests/mochitest/text/test_textBounds.html create mode 100644 accessible/tests/mochitest/text/test_wordboundary.html create mode 100644 accessible/tests/mochitest/text/test_words.html create mode 100644 accessible/tests/mochitest/textattrs/a11y.toml create mode 100644 accessible/tests/mochitest/textattrs/test_general.html create mode 100644 accessible/tests/mochitest/textattrs/test_general.xhtml create mode 100644 accessible/tests/mochitest/textattrs/test_invalid.html create mode 100644 accessible/tests/mochitest/textattrs/test_mathml.html create mode 100644 accessible/tests/mochitest/textattrs/test_spelling.html create mode 100644 accessible/tests/mochitest/textattrs/test_svg.html create mode 100644 accessible/tests/mochitest/textcaret/a11y.toml create mode 100644 accessible/tests/mochitest/textcaret/test_general.html create mode 100644 accessible/tests/mochitest/textrange/a11y.toml create mode 100644 accessible/tests/mochitest/textrange/test_general.html create mode 100644 accessible/tests/mochitest/textrange/test_selection.html create mode 100644 accessible/tests/mochitest/textselection/a11y.toml create mode 100644 accessible/tests/mochitest/textselection/test_general.html create mode 100644 accessible/tests/mochitest/textselection/test_userinput.html create mode 100644 accessible/tests/mochitest/tree/a11y.toml create mode 100644 accessible/tests/mochitest/tree/dockids.html create mode 100644 accessible/tests/mochitest/tree/test_applicationacc.xhtml create mode 100644 accessible/tests/mochitest/tree/test_aria_display_contents.html create mode 100644 accessible/tests/mochitest/tree/test_aria_globals.html create mode 100644 accessible/tests/mochitest/tree/test_aria_grid.html create mode 100644 accessible/tests/mochitest/tree/test_aria_imgmap.html create mode 100644 accessible/tests/mochitest/tree/test_aria_list.html create mode 100644 accessible/tests/mochitest/tree/test_aria_menu.html create mode 100644 accessible/tests/mochitest/tree/test_aria_owns.html create mode 100644 accessible/tests/mochitest/tree/test_aria_presentation.html create mode 100644 accessible/tests/mochitest/tree/test_aria_table.html create mode 100644 accessible/tests/mochitest/tree/test_brokencontext.html create mode 100644 accessible/tests/mochitest/tree/test_button.xhtml create mode 100644 accessible/tests/mochitest/tree/test_canvas.html create mode 100644 accessible/tests/mochitest/tree/test_combobox.xhtml create mode 100644 accessible/tests/mochitest/tree/test_cssflexbox.html create mode 100644 accessible/tests/mochitest/tree/test_cssoverflow.html create mode 100644 accessible/tests/mochitest/tree/test_display_contents.html create mode 100644 accessible/tests/mochitest/tree/test_divs.html create mode 100644 accessible/tests/mochitest/tree/test_dochierarchy.html create mode 100644 accessible/tests/mochitest/tree/test_dockids.html create mode 100644 accessible/tests/mochitest/tree/test_filectrl.html create mode 100644 accessible/tests/mochitest/tree/test_formctrl.html create mode 100644 accessible/tests/mochitest/tree/test_formctrl.xhtml create mode 100644 accessible/tests/mochitest/tree/test_gencontent.html create mode 100644 accessible/tests/mochitest/tree/test_groupbox.xhtml create mode 100644 accessible/tests/mochitest/tree/test_html_in_mathml.html create mode 100644 accessible/tests/mochitest/tree/test_iframe.html create mode 100644 accessible/tests/mochitest/tree/test_image.xhtml create mode 100644 accessible/tests/mochitest/tree/test_img.html create mode 100644 accessible/tests/mochitest/tree/test_invalid_img.xhtml create mode 100644 accessible/tests/mochitest/tree/test_invalidationlist.html create mode 100644 accessible/tests/mochitest/tree/test_list.html create mode 100644 accessible/tests/mochitest/tree/test_map.html create mode 100644 accessible/tests/mochitest/tree/test_media.html create mode 100644 accessible/tests/mochitest/tree/test_select.html create mode 100644 accessible/tests/mochitest/tree/test_svg.html create mode 100644 accessible/tests/mochitest/tree/test_tabbox.xhtml create mode 100644 accessible/tests/mochitest/tree/test_tabbrowser.xhtml create mode 100644 accessible/tests/mochitest/tree/test_table.html create mode 100644 accessible/tests/mochitest/tree/test_table_2.html create mode 100644 accessible/tests/mochitest/tree/test_table_3.html create mode 100644 accessible/tests/mochitest/tree/test_tree.xhtml create mode 100644 accessible/tests/mochitest/tree/test_txtcntr.html create mode 100644 accessible/tests/mochitest/tree/test_txtctrl.html create mode 100644 accessible/tests/mochitest/tree/test_txtctrl.xhtml create mode 100644 accessible/tests/mochitest/tree/wnd.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/a11y.toml create mode 100644 accessible/tests/mochitest/treeupdate/test_ariadialog.html create mode 100644 accessible/tests/mochitest/treeupdate/test_ariahidden.html create mode 100644 accessible/tests/mochitest/treeupdate/test_ariaowns.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1040735.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1175913.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1189277.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1276857.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html create mode 100644 accessible/tests/mochitest/treeupdate/test_bug852150.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_bug883708.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_bug884251.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_bug895082.html create mode 100644 accessible/tests/mochitest/treeupdate/test_canvas.html create mode 100644 accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_cssoverflow.html create mode 100644 accessible/tests/mochitest/treeupdate/test_deck.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_delayed_removal.html create mode 100644 accessible/tests/mochitest/treeupdate/test_doc.html create mode 100644 accessible/tests/mochitest/treeupdate/test_gencontent.html create mode 100644 accessible/tests/mochitest/treeupdate/test_general.html create mode 100644 accessible/tests/mochitest/treeupdate/test_hidden.html create mode 100644 accessible/tests/mochitest/treeupdate/test_imagemap.html create mode 100644 accessible/tests/mochitest/treeupdate/test_inert.html create mode 100644 accessible/tests/mochitest/treeupdate/test_inner_reorder.html create mode 100644 accessible/tests/mochitest/treeupdate/test_list.html create mode 100644 accessible/tests/mochitest/treeupdate/test_list_editabledoc.html create mode 100644 accessible/tests/mochitest/treeupdate/test_list_style.html create mode 100644 accessible/tests/mochitest/treeupdate/test_listbox.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_menu.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_menubutton.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_optgroup.html create mode 100644 accessible/tests/mochitest/treeupdate/test_recreation.html create mode 100644 accessible/tests/mochitest/treeupdate/test_select.html create mode 100644 accessible/tests/mochitest/treeupdate/test_shadow_slots.html create mode 100644 accessible/tests/mochitest/treeupdate/test_shutdown.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_table.html create mode 100644 accessible/tests/mochitest/treeupdate/test_textleaf.html create mode 100644 accessible/tests/mochitest/treeupdate/test_tooltip.xhtml create mode 100644 accessible/tests/mochitest/treeupdate/test_visibility.html create mode 100644 accessible/tests/mochitest/treeupdate/test_whitespace.html create mode 100644 accessible/tests/mochitest/treeview.css create mode 100644 accessible/tests/mochitest/treeview.js create mode 100644 accessible/tests/mochitest/value.js create mode 100644 accessible/tests/mochitest/value/a11y.toml create mode 100644 accessible/tests/mochitest/value/test_ariavalue.html create mode 100644 accessible/tests/mochitest/value/test_datetime.html create mode 100644 accessible/tests/mochitest/value/test_general.html create mode 100644 accessible/tests/mochitest/value/test_meter.html create mode 100644 accessible/tests/mochitest/value/test_number.html create mode 100644 accessible/tests/mochitest/value/test_progress.html create mode 100644 accessible/tests/mochitest/value/test_range.html create mode 100644 accessible/windows/ia2/ia2Accessible.cpp create mode 100644 accessible/windows/ia2/ia2Accessible.h create mode 100644 accessible/windows/ia2/ia2AccessibleAction.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleAction.h create mode 100644 accessible/windows/ia2/ia2AccessibleApplication.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleApplication.h create mode 100644 accessible/windows/ia2/ia2AccessibleComponent.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleComponent.h create mode 100644 accessible/windows/ia2/ia2AccessibleEditableText.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleEditableText.h create mode 100644 accessible/windows/ia2/ia2AccessibleHyperlink.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleHyperlink.h create mode 100644 accessible/windows/ia2/ia2AccessibleHypertext.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleHypertext.h create mode 100644 accessible/windows/ia2/ia2AccessibleImage.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleImage.h create mode 100644 accessible/windows/ia2/ia2AccessibleRelation.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleRelation.h create mode 100644 accessible/windows/ia2/ia2AccessibleTable.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleTable.h create mode 100644 accessible/windows/ia2/ia2AccessibleTableCell.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleTableCell.h create mode 100644 accessible/windows/ia2/ia2AccessibleText.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleText.h create mode 100644 accessible/windows/ia2/ia2AccessibleTextSelectionContainer.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleTextSelectionContainer.h create mode 100644 accessible/windows/ia2/ia2AccessibleValue.cpp create mode 100644 accessible/windows/ia2/ia2AccessibleValue.h create mode 100644 accessible/windows/ia2/moz.build create mode 100644 accessible/windows/moz.build create mode 100644 accessible/windows/msaa/AccessibleWrap.cpp create mode 100644 accessible/windows/msaa/AccessibleWrap.h create mode 100644 accessible/windows/msaa/ApplicationAccessibleWrap.cpp create mode 100644 accessible/windows/msaa/ApplicationAccessibleWrap.h create mode 100644 accessible/windows/msaa/Compatibility.cpp create mode 100644 accessible/windows/msaa/Compatibility.h create mode 100644 accessible/windows/msaa/CompatibilityUIA.cpp create mode 100644 accessible/windows/msaa/DocAccessibleWrap.cpp create mode 100644 accessible/windows/msaa/DocAccessibleWrap.h create mode 100644 accessible/windows/msaa/EnumVariant.cpp create mode 100644 accessible/windows/msaa/EnumVariant.h create mode 100644 accessible/windows/msaa/IUnknownImpl.cpp create mode 100644 accessible/windows/msaa/IUnknownImpl.h create mode 100644 accessible/windows/msaa/LazyInstantiator.cpp create mode 100644 accessible/windows/msaa/LazyInstantiator.h create mode 100644 accessible/windows/msaa/MsaaAccessible.cpp create mode 100644 accessible/windows/msaa/MsaaAccessible.h create mode 100644 accessible/windows/msaa/MsaaDocAccessible.cpp create mode 100644 accessible/windows/msaa/MsaaDocAccessible.h create mode 100644 accessible/windows/msaa/MsaaIdGenerator.cpp create mode 100644 accessible/windows/msaa/MsaaIdGenerator.h create mode 100644 accessible/windows/msaa/MsaaRootAccessible.cpp create mode 100644 accessible/windows/msaa/MsaaRootAccessible.h create mode 100644 accessible/windows/msaa/MsaaXULMenuAccessible.cpp create mode 100644 accessible/windows/msaa/MsaaXULMenuAccessible.h create mode 100644 accessible/windows/msaa/NtUndoc.h create mode 100644 accessible/windows/msaa/Platform.cpp create mode 100644 accessible/windows/msaa/RootAccessibleWrap.cpp create mode 100644 accessible/windows/msaa/RootAccessibleWrap.h create mode 100644 accessible/windows/msaa/ServiceProvider.cpp create mode 100644 accessible/windows/msaa/ServiceProvider.h create mode 100644 accessible/windows/msaa/moz.build create mode 100644 accessible/windows/msaa/nsEventMap.h create mode 100644 accessible/windows/msaa/nsWinUtils.cpp create mode 100644 accessible/windows/msaa/nsWinUtils.h create mode 100644 accessible/windows/sdn/moz.build create mode 100644 accessible/windows/sdn/sdnAccessible-inl.h create mode 100644 accessible/windows/sdn/sdnAccessible.cpp create mode 100644 accessible/windows/sdn/sdnAccessible.h create mode 100644 accessible/windows/sdn/sdnDocAccessible.cpp create mode 100644 accessible/windows/sdn/sdnDocAccessible.h create mode 100644 accessible/windows/sdn/sdnTextAccessible.cpp create mode 100644 accessible/windows/sdn/sdnTextAccessible.h create mode 100644 accessible/windows/uia/moz.build create mode 100644 accessible/windows/uia/uiaRawElmProvider.cpp create mode 100644 accessible/windows/uia/uiaRawElmProvider.h create mode 100755 accessible/xpcom/AccEventGen.py create mode 100644 accessible/xpcom/AccEvents.conf create mode 100644 accessible/xpcom/moz.build create mode 100644 accessible/xpcom/nsAccessibleRelation.cpp create mode 100644 accessible/xpcom/nsAccessibleRelation.h create mode 100644 accessible/xpcom/xpcAccessibilityService.cpp create mode 100644 accessible/xpcom/xpcAccessibilityService.h create mode 100644 accessible/xpcom/xpcAccessible.cpp create mode 100644 accessible/xpcom/xpcAccessible.h create mode 100644 accessible/xpcom/xpcAccessibleApplication.cpp create mode 100644 accessible/xpcom/xpcAccessibleApplication.h create mode 100644 accessible/xpcom/xpcAccessibleDocument.cpp create mode 100644 accessible/xpcom/xpcAccessibleDocument.h create mode 100644 accessible/xpcom/xpcAccessibleGeneric.cpp create mode 100644 accessible/xpcom/xpcAccessibleGeneric.h create mode 100644 accessible/xpcom/xpcAccessibleHyperLink.cpp create mode 100644 accessible/xpcom/xpcAccessibleHyperLink.h create mode 100644 accessible/xpcom/xpcAccessibleHyperText.cpp create mode 100644 accessible/xpcom/xpcAccessibleHyperText.h create mode 100644 accessible/xpcom/xpcAccessibleImage.cpp create mode 100644 accessible/xpcom/xpcAccessibleImage.h create mode 100644 accessible/xpcom/xpcAccessibleMacInterface.h create mode 100644 accessible/xpcom/xpcAccessibleMacInterface.mm create mode 100644 accessible/xpcom/xpcAccessiblePivot.cpp create mode 100644 accessible/xpcom/xpcAccessiblePivot.h create mode 100644 accessible/xpcom/xpcAccessibleSelectable.cpp create mode 100644 accessible/xpcom/xpcAccessibleSelectable.h create mode 100644 accessible/xpcom/xpcAccessibleTable.cpp create mode 100644 accessible/xpcom/xpcAccessibleTable.h create mode 100644 accessible/xpcom/xpcAccessibleTableCell.cpp create mode 100644 accessible/xpcom/xpcAccessibleTableCell.h create mode 100644 accessible/xpcom/xpcAccessibleTextLeafRange.cpp create mode 100644 accessible/xpcom/xpcAccessibleTextLeafRange.h create mode 100644 accessible/xpcom/xpcAccessibleTextRange.cpp create mode 100644 accessible/xpcom/xpcAccessibleTextRange.h create mode 100644 accessible/xpcom/xpcAccessibleValue.cpp create mode 100644 accessible/xpcom/xpcAccessibleValue.h create mode 100644 accessible/xul/XULAlertAccessible.cpp create mode 100644 accessible/xul/XULAlertAccessible.h create mode 100644 accessible/xul/XULComboboxAccessible.cpp create mode 100644 accessible/xul/XULComboboxAccessible.h create mode 100644 accessible/xul/XULElementAccessibles.cpp create mode 100644 accessible/xul/XULElementAccessibles.h create mode 100644 accessible/xul/XULFormControlAccessible.cpp create mode 100644 accessible/xul/XULFormControlAccessible.h create mode 100644 accessible/xul/XULListboxAccessible.cpp create mode 100644 accessible/xul/XULListboxAccessible.h create mode 100644 accessible/xul/XULMenuAccessible.cpp create mode 100644 accessible/xul/XULMenuAccessible.h create mode 100644 accessible/xul/XULSelectControlAccessible.cpp create mode 100644 accessible/xul/XULSelectControlAccessible.h create mode 100644 accessible/xul/XULTabAccessible.cpp create mode 100644 accessible/xul/XULTabAccessible.h create mode 100644 accessible/xul/XULTreeAccessible.cpp create mode 100644 accessible/xul/XULTreeAccessible.h create mode 100644 accessible/xul/XULTreeGridAccessible.cpp create mode 100644 accessible/xul/XULTreeGridAccessible.h create mode 100644 accessible/xul/moz.build (limited to 'accessible') diff --git a/accessible/android/AccessibleWrap.cpp b/accessible/android/AccessibleWrap.cpp new file mode 100644 index 0000000000..4bccc2dddd --- /dev/null +++ b/accessible/android/AccessibleWrap.cpp @@ -0,0 +1,473 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 "AccessibleWrap.h" + +#include "JavaBuiltins.h" +#include "LocalAccessible-inl.h" +#include "HyperTextAccessible-inl.h" +#include "AccAttributes.h" +#include "AccEvent.h" +#include "AndroidInputType.h" +#include "DocAccessibleWrap.h" +#include "SessionAccessibility.h" +#include "TextLeafAccessible.h" +#include "TraversalRule.h" +#include "Pivot.h" +#include "Platform.h" +#include "nsAccessibilityService.h" +#include "nsEventShell.h" +#include "nsIAccessibleAnnouncementEvent.h" +#include "nsIAccessiblePivot.h" +#include "nsAccUtils.h" +#include "nsTextEquivUtils.h" +#include "nsWhitespaceTokenizer.h" +#include "RootAccessible.h" +#include "TextLeafRange.h" + +#include "mozilla/a11y/PDocAccessibleChild.h" +#include "mozilla/jni/GeckoBundleUtils.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/Maybe.h" + +// icu TRUE conflicting with java::sdk::Boolean::TRUE() +// https://searchfox.org/mozilla-central/rev/ce02064d8afc8673cef83c92896ee873bd35e7ae/intl/icu/source/common/unicode/umachine.h#265 +// https://searchfox.org/mozilla-central/source/__GENERATED__/widget/android/bindings/JavaBuiltins.h#78 +#ifdef TRUE +# undef TRUE +#endif + +using namespace mozilla::a11y; +using mozilla::Maybe; + +//----------------------------------------------------- +// construction +//----------------------------------------------------- +AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) + : LocalAccessible(aContent, aDoc), mID(SessionAccessibility::kUnsetID) { + if (!IPCAccessibilityActive()) { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + SessionAccessibility::RegisterAccessible(this); + } +} + +//----------------------------------------------------- +// destruction +//----------------------------------------------------- +AccessibleWrap::~AccessibleWrap() {} + +nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { + auto accessible = static_cast(aEvent->GetAccessible()); + NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE); + + nsresult rv = LocalAccessible::HandleAccEvent(aEvent); + NS_ENSURE_SUCCESS(rv, rv); + + accessible->HandleLiveRegionEvent(aEvent); + + return NS_OK; +} + +void AccessibleWrap::Shutdown() { + if (!IPCAccessibilityActive()) { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + SessionAccessibility::UnregisterAccessible(this); + } + LocalAccessible::Shutdown(); +} + +bool AccessibleWrap::DoAction(uint8_t aIndex) const { + if (ActionCount()) { + return LocalAccessible::DoAction(aIndex); + } + + if (mContent) { + // We still simulate a click on an accessible even if there is no + // known actions. For the sake of bad markup. + DoCommand(); + return true; + } + + return false; +} + +Accessible* AccessibleWrap::DoPivot(Accessible* aAccessible, + int32_t aGranularity, bool aForward, + bool aInclusive) { + Accessible* pivotRoot = nullptr; + if (aAccessible->IsRemote()) { + // If this is a remote accessible provide the top level + // remote doc as the pivot root for thread safety reasons. + DocAccessibleParent* doc = aAccessible->AsRemote()->Document(); + while (doc && !doc->IsTopLevel()) { + doc = doc->ParentDoc(); + } + MOZ_ASSERT(doc, "Failed to get top level DocAccessibleParent"); + pivotRoot = doc; + } + a11y::Pivot pivot(pivotRoot); + // Depending on the start accessible, the pivot rule will either traverse + // local or remote accessibles exclusively. + TraversalRule rule(aGranularity, aAccessible->IsLocal()); + Accessible* result = aForward ? pivot.Next(aAccessible, rule, aInclusive) + : pivot.Prev(aAccessible, rule, aInclusive); + + if (result && (result != aAccessible || aInclusive)) { + return result; + } + + return nullptr; +} + +Accessible* AccessibleWrap::ExploreByTouch(Accessible* aAccessible, float aX, + float aY) { + Accessible* root; + if (LocalAccessible* local = aAccessible->AsLocal()) { + root = local->RootAccessible(); + } else { + // If this is a RemoteAccessible, provide the top level + // remote doc as the pivot root for thread safety reasons. + DocAccessibleParent* doc = aAccessible->AsRemote()->Document(); + while (doc && !doc->IsTopLevel()) { + doc = doc->ParentDoc(); + } + MOZ_ASSERT(doc, "Failed to get top level DocAccessibleParent"); + root = doc; + } + a11y::Pivot pivot(root); + TraversalRule rule(java::SessionAccessibility::HTML_GRANULARITY_DEFAULT, + aAccessible->IsLocal()); + Accessible* result = pivot.AtPoint(aX, aY, rule); + if (result == aAccessible) { + return nullptr; + } + return result; +} + +static TextLeafPoint ToTextLeafPoint(Accessible* aAccessible, int32_t aOffset) { + if (HyperTextAccessibleBase* ht = aAccessible->AsHyperTextBase()) { + return ht->ToTextLeafPoint(aOffset); + } + + return TextLeafPoint(aAccessible, aOffset); +} + +Maybe> AccessibleWrap::NavigateText( + Accessible* aAccessible, int32_t aGranularity, int32_t aStartOffset, + int32_t aEndOffset, bool aForward, bool aSelect) { + int32_t startOffset = aStartOffset; + int32_t endOffset = aEndOffset; + if (startOffset == -1) { + MOZ_ASSERT(endOffset == -1, + "When start offset is unset, end offset should be too"); + startOffset = aForward ? 0 : nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT; + endOffset = aForward ? 0 : nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT; + } + + // If the accessible is an editable, set the virtual cursor position + // to its caret offset. Otherwise use the document's virtual cursor + // position as a starting offset. + if (aAccessible->State() & states::EDITABLE) { + startOffset = endOffset = aAccessible->AsHyperTextBase()->CaretOffset(); + } + + TextLeafRange currentRange = + TextLeafRange(ToTextLeafPoint(aAccessible, startOffset), + ToTextLeafPoint(aAccessible, endOffset)); + uint16_t startBoundaryType = nsIAccessibleText::BOUNDARY_LINE_START; + uint16_t endBoundaryType = nsIAccessibleText::BOUNDARY_LINE_END; + switch (aGranularity) { + case 1: // MOVEMENT_GRANULARITY_CHARACTER + startBoundaryType = nsIAccessibleText::BOUNDARY_CHAR; + endBoundaryType = nsIAccessibleText::BOUNDARY_CHAR; + break; + case 2: // MOVEMENT_GRANULARITY_WORD + startBoundaryType = nsIAccessibleText::BOUNDARY_WORD_START; + endBoundaryType = nsIAccessibleText::BOUNDARY_WORD_END; + break; + default: + break; + } + + TextLeafRange resultRange; + + if (aForward) { + resultRange.SetEnd( + currentRange.End().FindBoundary(endBoundaryType, eDirNext)); + resultRange.SetStart( + resultRange.End().FindBoundary(startBoundaryType, eDirPrevious)); + } else { + resultRange.SetStart( + currentRange.Start().FindBoundary(startBoundaryType, eDirPrevious)); + resultRange.SetEnd( + resultRange.Start().FindBoundary(endBoundaryType, eDirNext)); + } + + if (!resultRange.Crop(aAccessible)) { + // If the new range does not intersect at all with the given + // accessible/container this navigation has failed or reached an edge. + return Nothing(); + } + + if (resultRange == currentRange || resultRange.Start() == resultRange.End()) { + // If the result range equals the current range, or if the result range is + // collapsed, we failed or reached an edge. + return Nothing(); + } + + if (HyperTextAccessibleBase* ht = aAccessible->AsHyperTextBase()) { + DebugOnly ok = false; + std::tie(ok, startOffset) = ht->TransformOffset( + resultRange.Start().mAcc, resultRange.Start().mOffset, false); + MOZ_ASSERT(ok, "Accessible of range start should be in container."); + + std::tie(ok, endOffset) = ht->TransformOffset( + resultRange.End().mAcc, resultRange.End().mOffset, false); + MOZ_ASSERT(ok, "Accessible range end should be in container."); + } else { + startOffset = resultRange.Start().mOffset; + endOffset = resultRange.End().mOffset; + } + + return Some(std::make_pair(startOffset, endOffset)); +} + +uint32_t AccessibleWrap::GetFlags(role aRole, uint64_t aState, + uint8_t aActionCount) { + uint32_t flags = 0; + if (aState & states::CHECKABLE) { + flags |= java::SessionAccessibility::FLAG_CHECKABLE; + } + + if (aState & states::CHECKED) { + flags |= java::SessionAccessibility::FLAG_CHECKED; + } + + if (aState & states::INVALID) { + flags |= java::SessionAccessibility::FLAG_CONTENT_INVALID; + } + + if (aState & states::EDITABLE) { + flags |= java::SessionAccessibility::FLAG_EDITABLE; + } + + if (aActionCount && aRole != roles::TEXT_LEAF) { + flags |= java::SessionAccessibility::FLAG_CLICKABLE; + } + + if (aState & states::ENABLED) { + flags |= java::SessionAccessibility::FLAG_ENABLED; + } + + if (aState & states::FOCUSABLE) { + flags |= java::SessionAccessibility::FLAG_FOCUSABLE; + } + + if (aState & states::FOCUSED) { + flags |= java::SessionAccessibility::FLAG_FOCUSED; + } + + if (aState & states::MULTI_LINE) { + flags |= java::SessionAccessibility::FLAG_MULTI_LINE; + } + + if (aState & states::SELECTABLE) { + flags |= java::SessionAccessibility::FLAG_SELECTABLE; + } + + if (aState & states::SELECTED) { + flags |= java::SessionAccessibility::FLAG_SELECTED; + } + + if (aState & states::EXPANDABLE) { + flags |= java::SessionAccessibility::FLAG_EXPANDABLE; + } + + if (aState & states::EXPANDED) { + flags |= java::SessionAccessibility::FLAG_EXPANDED; + } + + if ((aState & (states::INVISIBLE | states::OFFSCREEN)) == 0) { + flags |= java::SessionAccessibility::FLAG_VISIBLE_TO_USER; + } + + if (aRole == roles::PASSWORD_TEXT) { + flags |= java::SessionAccessibility::FLAG_PASSWORD; + } + + return flags; +} + +void AccessibleWrap::GetRoleDescription(role aRole, AccAttributes* aAttributes, + nsAString& aGeckoRole, + nsAString& aRoleDescription) { + if (aRole == roles::HEADING && aAttributes) { + // The heading level is an attribute, so we need that. + nsAutoString headingLevel; + if (aAttributes->GetAttribute(nsGkAtoms::level, headingLevel)) { + nsAutoString token(u"heading-"); + token.Append(headingLevel); + if (LocalizeString(token, aRoleDescription)) { + return; + } + } + } + + if ((aRole == roles::LANDMARK || aRole == roles::REGION) && aAttributes) { + nsAutoString xmlRoles; + if (aAttributes->GetAttribute(nsGkAtoms::xmlroles, xmlRoles)) { + nsWhitespaceTokenizer tokenizer(xmlRoles); + while (tokenizer.hasMoreTokens()) { + if (LocalizeString(tokenizer.nextToken(), aRoleDescription)) { + return; + } + } + } + } + + GetAccService()->GetStringRole(aRole, aGeckoRole); + LocalizeString(aGeckoRole, aRoleDescription); +} + +int32_t AccessibleWrap::AndroidClass(Accessible* aAccessible) { + return GetVirtualViewID(aAccessible) == SessionAccessibility::kNoID + ? java::SessionAccessibility::CLASSNAME_WEBVIEW + : GetAndroidClass(aAccessible->Role()); +} + +int32_t AccessibleWrap::GetVirtualViewID(Accessible* aAccessible) { + if (aAccessible->IsLocal()) { + return static_cast(aAccessible)->mID; + } + + return static_cast(aAccessible->AsRemote()->GetWrapper()); +} + +void AccessibleWrap::SetVirtualViewID(Accessible* aAccessible, + int32_t aVirtualViewID) { + if (aAccessible->IsLocal()) { + static_cast(aAccessible)->mID = aVirtualViewID; + } else { + aAccessible->AsRemote()->SetWrapper(static_cast(aVirtualViewID)); + } +} + +int32_t AccessibleWrap::GetAndroidClass(role aRole) { +#define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ + msaaRole, ia2Role, androidClass, nameRule) \ + case roles::geckoRole: \ + return androidClass; + + switch (aRole) { +#include "RoleMap.h" + default: + return java::SessionAccessibility::CLASSNAME_VIEW; + } + +#undef ROLE +} + +int32_t AccessibleWrap::GetInputType(const nsString& aInputTypeAttr) { + if (aInputTypeAttr.EqualsIgnoreCase("email")) { + return java::sdk::InputType::TYPE_CLASS_TEXT | + java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; + } + + if (aInputTypeAttr.EqualsIgnoreCase("number")) { + return java::sdk::InputType::TYPE_CLASS_NUMBER; + } + + if (aInputTypeAttr.EqualsIgnoreCase("password")) { + return java::sdk::InputType::TYPE_CLASS_TEXT | + java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_PASSWORD; + } + + if (aInputTypeAttr.EqualsIgnoreCase("tel")) { + return java::sdk::InputType::TYPE_CLASS_PHONE; + } + + if (aInputTypeAttr.EqualsIgnoreCase("text")) { + return java::sdk::InputType::TYPE_CLASS_TEXT | + java::sdk::InputType::TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; + } + + if (aInputTypeAttr.EqualsIgnoreCase("url")) { + return java::sdk::InputType::TYPE_CLASS_TEXT | + java::sdk::InputType::TYPE_TEXT_VARIATION_URI; + } + + return 0; +} + +void AccessibleWrap::GetTextEquiv(nsString& aText) { + if (nsTextEquivUtils::HasNameRule(this, eNameFromSubtreeIfReqRule)) { + // This is an accessible that normally doesn't get its name from its + // subtree, so we collect the text equivalent explicitly. + nsTextEquivUtils::GetTextEquivFromSubtree(this, aText); + } else { + Name(aText); + } +} + +bool AccessibleWrap::HandleLiveRegionEvent(AccEvent* aEvent) { + auto eventType = aEvent->GetEventType(); + if (eventType != nsIAccessibleEvent::EVENT_TEXT_INSERTED && + eventType != nsIAccessibleEvent::EVENT_NAME_CHANGE) { + // XXX: Right now only announce text inserted events. aria-relevant=removals + // is potentially on the chopping block[1]. We also don't support editable + // text because we currently can't descern the source of the change[2]. + // 1. https://github.com/w3c/aria/issues/712 + // 2. https://bugzilla.mozilla.org/show_bug.cgi?id=1531189 + return false; + } + + if (aEvent->IsFromUserInput()) { + return false; + } + + RefPtr attributes = new AccAttributes(); + nsAccUtils::SetLiveContainerAttributes(attributes, this); + nsString live; + if (!attributes->GetAttribute(nsGkAtoms::containerLive, live)) { + return false; + } + + uint16_t priority = live.EqualsIgnoreCase("assertive") + ? nsIAccessibleAnnouncementEvent::ASSERTIVE + : nsIAccessibleAnnouncementEvent::POLITE; + + Maybe atomic = + attributes->GetAttribute(nsGkAtoms::containerAtomic); + LocalAccessible* announcementTarget = this; + nsAutoString announcement; + if (atomic && *atomic) { + LocalAccessible* atomicAncestor = nullptr; + for (LocalAccessible* parent = announcementTarget; parent; + parent = parent->LocalParent()) { + dom::Element* element = parent->Elm(); + if (element && + nsAccUtils::ARIAAttrValueIs(element, nsGkAtoms::aria_atomic, + nsGkAtoms::_true, eCaseMatters)) { + atomicAncestor = parent; + break; + } + } + + if (atomicAncestor) { + announcementTarget = atomicAncestor; + static_cast(atomicAncestor)->GetTextEquiv(announcement); + } + } else { + GetTextEquiv(announcement); + } + + announcement.CompressWhitespace(); + if (announcement.IsEmpty()) { + return false; + } + + announcementTarget->Announce(announcement, priority); + return true; +} diff --git a/accessible/android/AccessibleWrap.h b/accessible/android/AccessibleWrap.h new file mode 100644 index 0000000000..249c5dc14e --- /dev/null +++ b/accessible/android/AccessibleWrap.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef mozilla_a11y_AccessibleWrap_h_ +#define mozilla_a11y_AccessibleWrap_h_ + +#include "LocalAccessible.h" +#include "mozilla/a11y/RemoteAccessible.h" +#include "mozilla/java/GeckoBundleWrappers.h" +#include "nsCOMPtr.h" + +namespace mozilla { +namespace a11y { + +class AccessibleWrap : public LocalAccessible { + public: + AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc); + virtual ~AccessibleWrap(); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY // TODO: Mark this as MOZ_CAN_RUN_SCRIPT + virtual nsresult + HandleAccEvent(AccEvent* aEvent) override; + + virtual void Shutdown() override; + + virtual bool DoAction(uint8_t aIndex) const override; + + static Accessible* ExploreByTouch(Accessible* aAccessible, float aX, + float aY); + + static uint32_t GetFlags(role aRole, uint64_t aState, uint8_t aActionCount); + + static int32_t GetInputType(const nsString& aInputTypeAttr); + + static int32_t GetAndroidClass(role aRole); + + static void GetRoleDescription(role aRole, AccAttributes* aAttributes, + nsAString& aGeckoRole, + nsAString& aRoleDescription); + + static int32_t AndroidClass(Accessible* aAccessible); + + static int32_t GetVirtualViewID(Accessible* aAccessible); + + static void SetVirtualViewID(Accessible* aAccessible, int32_t aVirtualViewID); + + static Accessible* DoPivot(Accessible* aAccessible, int32_t aGranularity, + bool aForward, bool aInclusive); + + static Maybe> NavigateText( + Accessible* aAccessible, int32_t aGranularity, int32_t aStartOffset, + int32_t aEndOffset, bool aForward, bool aSelect); + + protected: + int32_t mID; + + private: + void GetTextEquiv(nsString& aText); + + bool HandleLiveRegionEvent(AccEvent* aEvent); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/ApplicationAccessibleWrap.h b/accessible/android/ApplicationAccessibleWrap.h new file mode 100644 index 0000000000..89b07916c9 --- /dev/null +++ b/accessible/android/ApplicationAccessibleWrap.h @@ -0,0 +1,20 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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/. */ + +#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__ +#define mozilla_a11y_ApplicationAccessibleWrap_h__ + +#include "ApplicationAccessible.h" + +namespace mozilla { +namespace a11y { + +typedef ApplicationAccessible ApplicationAccessibleWrap; +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/DocAccessibleWrap.cpp b/accessible/android/DocAccessibleWrap.cpp new file mode 100644 index 0000000000..0e46b56649 --- /dev/null +++ b/accessible/android/DocAccessibleWrap.cpp @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LocalAccessible-inl.h" +#include "AccAttributes.h" +#include "DocAccessibleChild.h" +#include "DocAccessibleWrap.h" +#include "nsIDocShell.h" +#include "nsIScrollableFrame.h" +#include "nsLayoutUtils.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" +#include "Pivot.h" +#include "SessionAccessibility.h" +#include "TraversalRule.h" +#include "mozilla/PresShell.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +#define UNIQUE_ID(acc) \ + !acc || (acc->IsDoc() && acc->AsDoc()->IPCDoc()) \ + ? 0 \ + : reinterpret_cast(acc->UniqueID()) + +//////////////////////////////////////////////////////////////////////////////// +// DocAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +DocAccessibleWrap::DocAccessibleWrap(Document* aDocument, PresShell* aPresShell) + : DocAccessible(aDocument, aPresShell) { + // We need an nsINode associated with this accessible to register it with the + // right SessionAccessibility instance. When the base AccessibleWrap + // constructor is called we don't have one yet because null is passed as the + // content node. So we do it here after a Document is associated with the + // accessible. + if (!IPCAccessibilityActive()) { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + SessionAccessibility::RegisterAccessible(this); + } +} + +DocAccessibleWrap::~DocAccessibleWrap() {} + +void DocAccessibleWrap::Shutdown() { + // Unregister here before disconnecting from PresShell. + if (!IPCAccessibilityActive()) { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (IsRoot()) { + SessionAccessibility::UnregisterAll(PresShellPtr()); + } else { + SessionAccessibility::UnregisterAccessible(this); + } + } + DocAccessible::Shutdown(); +} + +DocAccessibleWrap* DocAccessibleWrap::GetTopLevelContentDoc( + AccessibleWrap* aAccessible) { + DocAccessibleWrap* doc = + static_cast(aAccessible->Document()); + while (doc && !doc->IsTopLevelContentDoc()) { + doc = static_cast(doc->ParentDocument()); + } + + return doc; +} + +bool DocAccessibleWrap::IsTopLevelContentDoc() { + DocAccessible* parentDoc = ParentDocument(); + return DocumentNode()->IsContentDocument() && + (!parentDoc || !parentDoc->DocumentNode()->IsContentDocument()); +} + +#undef UNIQUE_ID diff --git a/accessible/android/DocAccessibleWrap.h b/accessible/android/DocAccessibleWrap.h new file mode 100644 index 0000000000..c4408cdf41 --- /dev/null +++ b/accessible/android/DocAccessibleWrap.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef mozilla_a11y_DocAccessibleWrap_h__ +#define mozilla_a11y_DocAccessibleWrap_h__ + +#include "DocAccessible.h" +#include "nsITimer.h" + +namespace mozilla { + +class PresShell; + +namespace a11y { + +class DocAccessibleWrap : public DocAccessible { + public: + DocAccessibleWrap(Document* aDocument, PresShell* aPresShell); + virtual ~DocAccessibleWrap(); + + virtual void Shutdown() override; + + DocAccessibleWrap* GetTopLevelContentDoc(AccessibleWrap* aAccessible); + + bool IsTopLevelContentDoc(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/Platform.cpp b/accessible/android/Platform.cpp new file mode 100644 index 0000000000..02f808f8bc --- /dev/null +++ b/accessible/android/Platform.cpp @@ -0,0 +1,233 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Platform.h" +#include "DocAccessibleWrap.h" +#include "SessionAccessibility.h" +#include "mozilla/a11y/RemoteAccessible.h" +#include "mozilla/Components.h" +#include "nsIAccessibleEvent.h" +#include "nsIAccessiblePivot.h" +#include "nsIStringBundle.h" +#include "TextLeafRange.h" + +#define ROLE_STRINGS_URL "chrome://global/locale/AccessFu.properties" + +using namespace mozilla; +using namespace mozilla::a11y; + +static nsTHashMap sLocalizedStrings; + +void a11y::PlatformInit() { + nsresult rv = NS_OK; + nsCOMPtr stringBundleService = + components::StringBundle::Service(); + if (!stringBundleService) return; + + nsCOMPtr stringBundle; + nsCOMPtr sbs = components::StringBundle::Service(); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get string bundle service"); + return; + } + + rv = sbs->CreateBundle(ROLE_STRINGS_URL, getter_AddRefs(stringBundle)); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get string bundle"); + return; + } + + nsString localizedStr; + // Preload the state required localized string. + rv = stringBundle->GetStringFromName("stateRequired", localizedStr); + if (NS_SUCCEEDED(rv)) { + sLocalizedStrings.InsertOrUpdate(u"stateRequired"_ns, localizedStr); + } + + // Preload heading level localized descriptions 1 thru 6. + for (int32_t level = 1; level <= 6; level++) { + nsAutoString token; + token.AppendPrintf("heading-%d", level); + + nsAutoString formatString; + formatString.AppendInt(level); + AutoTArray formatParams; + formatParams.AppendElement(formatString); + rv = stringBundle->FormatStringFromName("headingLevel", formatParams, + localizedStr); + if (NS_SUCCEEDED(rv)) { + sLocalizedStrings.InsertOrUpdate(token, localizedStr); + } + } + + // Preload any roles that have localized versions +#define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ + msaaRole, ia2Role, androidClass, nameRule) \ + rv = stringBundle->GetStringFromName(stringRole, localizedStr); \ + if (NS_SUCCEEDED(rv)) { \ + sLocalizedStrings.InsertOrUpdate(u##stringRole##_ns, localizedStr); \ + } + +#include "RoleMap.h" +#undef ROLE +} + +void a11y::PlatformShutdown() { sLocalizedStrings.Clear(); } + +void a11y::ProxyCreated(RemoteAccessible* aProxy) { + SessionAccessibility::RegisterAccessible(aProxy); +} + +void a11y::ProxyDestroyed(RemoteAccessible* aProxy) { + SessionAccessibility::UnregisterAccessible(aProxy); +} + +void a11y::PlatformEvent(Accessible* aTarget, uint32_t aEventType) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + if (!sessionAcc) { + return; + } + + switch (aEventType) { + case nsIAccessibleEvent::EVENT_REORDER: + sessionAcc->SendWindowContentChangedEvent(); + break; + case nsIAccessibleEvent::EVENT_SCROLLING_START: + if (Accessible* result = AccessibleWrap::DoPivot( + aTarget, java::SessionAccessibility::HTML_GRANULARITY_DEFAULT, + true, true)) { + sessionAcc->SendAccessibilityFocusedEvent(result, false); + } + break; + default: + break; + } +} + +void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t aState, + bool aEnabled) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + + if (!sessionAcc) { + return; + } + + if (aState & states::CHECKED) { + sessionAcc->SendClickedEvent( + aTarget, java::SessionAccessibility::FLAG_CHECKABLE | + (aEnabled ? java::SessionAccessibility::FLAG_CHECKED : 0)); + } + + if (aState & states::EXPANDED) { + sessionAcc->SendClickedEvent( + aTarget, + java::SessionAccessibility::FLAG_EXPANDABLE | + (aEnabled ? java::SessionAccessibility::FLAG_EXPANDED : 0)); + } + + if (aState & states::SELECTED) { + sessionAcc->SendSelectedEvent(aTarget, aEnabled); + } + + if (aState & states::BUSY) { + sessionAcc->SendWindowStateChangedEvent(aTarget); + } +} + +void a11y::PlatformFocusEvent(Accessible* aTarget, + const LayoutDeviceIntRect& aCaretRect) { + if (RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget)) { + sessionAcc->SendFocusEvent(aTarget); + } +} + +void a11y::PlatformCaretMoveEvent(Accessible* aTarget, int32_t aOffset, + bool aIsSelectionCollapsed, + int32_t aGranularity, + const LayoutDeviceIntRect& aCaretRect, + bool aFromUser) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + if (!sessionAcc) { + return; + } + + if (!aTarget->IsDoc() && !aFromUser && !aIsSelectionCollapsed) { + // Pivot to the caret's position if it has an expanded selection. + // This is used mostly for find in page. + Accessible* leaf = TextLeafPoint::GetCaret(aTarget).ActualizeCaret().mAcc; + MOZ_ASSERT(leaf); + if (leaf) { + if (Accessible* result = AccessibleWrap::DoPivot( + leaf, java::SessionAccessibility::HTML_GRANULARITY_DEFAULT, true, + true)) { + sessionAcc->SendAccessibilityFocusedEvent(result, false); + } + } + } + + sessionAcc->SendTextSelectionChangedEvent(aTarget, aOffset); +} + +void a11y::PlatformTextChangeEvent(Accessible* aTarget, const nsAString& aStr, + int32_t aStart, uint32_t aLen, + bool aIsInsert, bool aFromUser) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + + if (sessionAcc) { + sessionAcc->SendTextChangedEvent(aTarget, aStr, aStart, aLen, aIsInsert, + aFromUser); + } +} + +void a11y::PlatformShowHideEvent(Accessible* aTarget, Accessible* aParent, + bool aInsert, bool aFromUser) { + // We rely on the window content changed events to be dispatched + // after the viewport cache is refreshed. +} + +void a11y::PlatformSelectionEvent(Accessible*, Accessible*, uint32_t) {} + +void a11y::PlatformScrollingEvent(Accessible* aTarget, uint32_t aEventType, + uint32_t aScrollX, uint32_t aScrollY, + uint32_t aMaxScrollX, uint32_t aMaxScrollY) { + if (aEventType == nsIAccessibleEvent::EVENT_SCROLLING) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + + if (sessionAcc) { + sessionAcc->SendScrollingEvent(aTarget, aScrollX, aScrollY, aMaxScrollX, + aMaxScrollY); + } + } +} + +void a11y::PlatformAnnouncementEvent(Accessible* aTarget, + const nsAString& aAnnouncement, + uint16_t aPriority) { + RefPtr sessionAcc = + SessionAccessibility::GetInstanceFor(aTarget); + + if (sessionAcc) { + sessionAcc->SendAnnouncementEvent(aTarget, aAnnouncement, aPriority); + } +} + +bool a11y::LocalizeString(const nsAString& aToken, nsAString& aLocalized) { + MOZ_ASSERT(XRE_IsParentProcess()); + + auto str = sLocalizedStrings.Lookup(aToken); + if (str) { + aLocalized.Assign(*str); + } else { + } + + return !!str; +} diff --git a/accessible/android/RootAccessibleWrap.h b/accessible/android/RootAccessibleWrap.h new file mode 100644 index 0000000000..4198239bad --- /dev/null +++ b/accessible/android/RootAccessibleWrap.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef mozilla_a11y_RootAccessibleWrap_h__ +#define mozilla_a11y_RootAccessibleWrap_h__ + +#include "RootAccessible.h" + +namespace mozilla { + +class PresShell; + +namespace a11y { + +using RootAccessibleWrap = RootAccessible; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/SessionAccessibility.cpp b/accessible/android/SessionAccessibility.cpp new file mode 100644 index 0000000000..aab9b7da69 --- /dev/null +++ b/accessible/android/SessionAccessibility.cpp @@ -0,0 +1,941 @@ +/* -*- Mode: c++; c-basic-offset: 2; tab-width: 20; indent-tabs-mode: nil; -*- + * 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 "SessionAccessibility.h" +#include "LocalAccessible-inl.h" +#include "AndroidUiThread.h" +#include "AndroidBridge.h" +#include "DocAccessibleParent.h" +#include "IDSet.h" +#include "nsThreadUtils.h" +#include "AccAttributes.h" +#include "AccessibilityEvent.h" +#include "DocAccessibleWrap.h" +#include "JavaBuiltins.h" +#include "nsAccessibilityService.h" +#include "nsAccUtils.h" +#include "nsViewManager.h" + +#include "mozilla/PresShell.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/a11y/DocManager.h" +#include "mozilla/a11y/HyperTextAccessibleBase.h" +#include "mozilla/jni/GeckoBundleUtils.h" +#include "mozilla/jni/NativesInlines.h" +#include "mozilla/widget/GeckoViewSupport.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/dom/MouseEventBinding.h" + +#ifdef DEBUG +# include +# define AALOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "GeckoAccessibilityNative", ##args) +#else +# define AALOG(args...) \ + do { \ + } while (0) +#endif + +using namespace mozilla::a11y; + +// IDs should be a positive 32bit integer. +IDSet sIDSet(31UL); + +class Settings final + : public mozilla::java::SessionAccessibility::Settings::Natives { + public: + static void ToggleNativeAccessibility(bool aEnable) { + if (aEnable) { + GetOrCreateAccService(); + } else { + MaybeShutdownAccService(nsAccessibilityService::ePlatformAPI); + } + } +}; + +SessionAccessibility::SessionAccessibility( + jni::NativeWeakPtr aWindow, + java::SessionAccessibility::NativeProvider::Param aSessionAccessibility) + : mWindow(aWindow), mSessionAccessibility(aSessionAccessibility) { + SetAttached(true, nullptr); +} + +void SessionAccessibility::SetAttached(bool aAttached, + already_AddRefed aRunnable) { + if (RefPtr uiThread = GetAndroidUiThread()) { + uiThread->Dispatch(NS_NewRunnableFunction( + "SessionAccessibility::Attach", + [aAttached, + sa = java::SessionAccessibility::NativeProvider::GlobalRef( + mSessionAccessibility), + runnable = RefPtr(aRunnable)] { + sa->SetAttached(aAttached); + if (runnable) { + runnable->Run(); + } + })); + } +} + +void SessionAccessibility::Init() { + java::SessionAccessibility::NativeProvider::Natives< + SessionAccessibility>::Init(); + Settings::Init(); +} + +void SessionAccessibility::GetNodeInfo(int32_t aID, + mozilla::jni::Object::Param aNodeInfo) { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + ReleasableMonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + java::GeckoBundle::GlobalRef ret = nullptr; + RefPtr self(this); + if (Accessible* acc = GetAccessibleByID(aID)) { + if (acc->IsLocal()) { + mal.Unlock(); + nsAppShell::SyncRunEvent( + [this, self, aID, aNodeInfo = jni::Object::GlobalRef(aNodeInfo)] { + if (Accessible* acc = GetAccessibleByID(aID)) { + PopulateNodeInfo(acc, aNodeInfo); + } else { + AALOG("oops, nothing for %d", aID); + } + }); + } else { + PopulateNodeInfo(acc, aNodeInfo); + } + } else { + AALOG("oops, nothing for %d", aID); + } +} + +int SessionAccessibility::GetNodeClassName(int32_t aID) { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + ReleasableMonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + int32_t classNameEnum = java::SessionAccessibility::CLASSNAME_VIEW; + RefPtr self(this); + if (Accessible* acc = GetAccessibleByID(aID)) { + if (acc->IsLocal()) { + mal.Unlock(); + nsAppShell::SyncRunEvent([this, self, aID, &classNameEnum] { + if (Accessible* acc = GetAccessibleByID(aID)) { + classNameEnum = AccessibleWrap::AndroidClass(acc); + } + }); + } else { + classNameEnum = AccessibleWrap::AndroidClass(acc); + } + } + + return classNameEnum; +} + +void SessionAccessibility::SetText(int32_t aID, jni::String::Param aText) { + if (Accessible* acc = GetAccessibleByID(aID)) { + if (acc->IsRemote()) { + acc->AsRemote()->ReplaceText(PromiseFlatString(aText->ToString())); + } else if (acc->AsLocal()->IsHyperText()) { + acc->AsLocal()->AsHyperText()->ReplaceText(aText->ToString()); + } + } +} + +void SessionAccessibility::Click(int32_t aID) { + MOZ_ASSERT(NS_IsMainThread()); + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* acc = GetAccessibleByID(aID)) { + acc->DoAction(0); + } +} + +bool SessionAccessibility::Pivot(int32_t aID, int32_t aGranularity, + bool aForward, bool aInclusive) { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + RefPtr self(this); + if (Accessible* acc = GetAccessibleByID(aID)) { + if (acc->IsLocal()) { + nsAppShell::PostEvent( + [this, self, aID, aGranularity, aForward, aInclusive] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* _acc = GetAccessibleByID(aID)) { + MOZ_ASSERT(_acc->IsLocal()); + if (Accessible* result = AccessibleWrap::DoPivot( + _acc, aGranularity, aForward, aInclusive)) { + SendAccessibilityFocusedEvent(result, true); + } + } + }); + return true; + } + Accessible* result = + AccessibleWrap::DoPivot(acc, aGranularity, aForward, aInclusive); + if (result) { + int32_t virtualViewID = AccessibleWrap::GetVirtualViewID(result); + nsAppShell::PostEvent([this, self, virtualViewID] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* acc = GetAccessibleByID(virtualViewID)) { + SendAccessibilityFocusedEvent(acc, true); + } + }); + return true; + } + } + + return false; +} + +void SessionAccessibility::ExploreByTouch(int32_t aID, float aX, float aY) { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + RefPtr self(this); + if (Accessible* origin = GetAccessibleByID(aID)) { + if (origin->IsLocal()) { + nsAppShell::PostEvent([this, self, aID, aX, aY] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* origin = GetAccessibleByID(aID)) { + if (Accessible* result = + AccessibleWrap::ExploreByTouch(origin, aX, aY)) { + SendHoverEnterEvent(result); + } + } + }); + } else { + if (Accessible* result = AccessibleWrap::ExploreByTouch(origin, aX, aY)) { + int32_t resultID = AccessibleWrap::GetVirtualViewID(result); + nsAppShell::PostEvent([this, self, resultID] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* result = GetAccessibleByID(resultID)) { + SendHoverEnterEvent(result); + } + }); + } + } + } +} + +static void GetSelectionOrCaret(HyperTextAccessibleBase* aHyperTextAcc, + int32_t* aStartOffset, int32_t* aEndOffset) { + if (!aHyperTextAcc->SelectionBoundsAt(0, aStartOffset, aEndOffset)) { + *aStartOffset = *aEndOffset = aHyperTextAcc->CaretOffset(); + } +} + +static void AdjustCaretToTextNavigation(Accessible* aAccessible, + int32_t aStartOffset, + int32_t aEndOffset, bool aForward, + bool aSelect) { + MOZ_ASSERT(NS_IsMainThread()); + if (!(aAccessible->State() & states::EDITABLE)) { + return; + } + + HyperTextAccessibleBase* editable = aAccessible->AsHyperTextBase(); + MOZ_ASSERT(editable); + if (!editable) { + return; + } + + int32_t newOffset = aForward ? aEndOffset : aStartOffset; + if (aSelect) { + int32_t anchor = editable->CaretOffset(); + if (editable->SelectionCount()) { + int32_t startSel, endSel; + GetSelectionOrCaret(editable, &startSel, &endSel); + anchor = startSel == anchor ? endSel : startSel; + } + editable->SetSelectionBoundsAt(0, anchor, newOffset); + } else { + editable->SetCaretOffset(newOffset); + } +} + +bool SessionAccessibility::NavigateText(int32_t aID, int32_t aGranularity, + int32_t aStartOffset, + int32_t aEndOffset, bool aForward, + bool aSelect) { + MOZ_ASSERT(AndroidBridge::IsJavaUiThread()); + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + RefPtr self(this); + if (Accessible* acc = GetAccessibleByID(aID)) { + if (acc->IsLocal()) { + nsAppShell::PostEvent([this, self, aID, aGranularity, aStartOffset, + aEndOffset, aForward, aSelect] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* _acc = GetAccessibleByID(aID)) { + auto result = AccessibleWrap::NavigateText( + _acc, aGranularity, aStartOffset, aEndOffset, aForward, aSelect); + + if (result) { + SendTextTraversedEvent(_acc, result->first, result->second); + AdjustCaretToTextNavigation(_acc, result->first, result->second, + aForward, aSelect); + } + } + }); + return true; + } else { + auto result = AccessibleWrap::NavigateText( + acc, aGranularity, aStartOffset, aEndOffset, aForward, aSelect); + if (result) { + nsAppShell::PostEvent([this, self, aID, result, aForward, aSelect] { + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); + if (Accessible* _acc = GetAccessibleByID(aID)) { + SendTextTraversedEvent(_acc, result->first, result->second); + AdjustCaretToTextNavigation(_acc, result->first, result->second, + aForward, aSelect); + } + }); + } + + return !!result; + } + } + + return false; +} + +void SessionAccessibility::SetSelection(int32_t aID, int32_t aStart, + int32_t aEnd) { + if (Accessible* acc = GetAccessibleByID(aID)) { + if (auto* textAcc = acc->AsHyperTextBase()) { + if (aStart == aEnd) { + textAcc->SetCaretOffset(aStart); + } else { + textAcc->SetSelectionBoundsAt(0, aStart, aEnd); + } + } + } +} + +void SessionAccessibility::Cut(int32_t aID) { + if (Accessible* acc = GetAccessibleByID(aID)) { + if (auto* textAcc = acc->AsHyperTextBase()) { + int32_t startSel, endSel; + if (textAcc->SelectionBoundsAt(0, &startSel, &endSel)) { + textAcc->CutText(startSel, endSel); + } + } + } +} + +void SessionAccessibility::Copy(int32_t aID) { + if (Accessible* acc = GetAccessibleByID(aID)) { + if (auto* textAcc = acc->AsHyperTextBase()) { + int32_t startSel, endSel; + GetSelectionOrCaret(textAcc, &startSel, &endSel); + textAcc->CopyText(startSel, endSel); + } + } +} + +void SessionAccessibility::Paste(int32_t aID) { + if (Accessible* acc = GetAccessibleByID(aID)) { + if (auto* textAcc = acc->AsHyperTextBase()) { + int32_t startSel, endSel; + GetSelectionOrCaret(textAcc, &startSel, &endSel); + if (startSel != endSel) { + textAcc->DeleteText(startSel, endSel); + } + textAcc->PasteText(startSel); + } + } +} + +RefPtr SessionAccessibility::GetInstanceFor( + Accessible* aAccessible) { + MOZ_ASSERT(NS_IsMainThread()); + if (LocalAccessible* localAcc = aAccessible->AsLocal()) { + DocAccessible* docAcc = localAcc->Document(); + // If the accessible is being shutdown from the doc's shutdown + // the doc accessible won't have a ref to a presshell anymore, + // but we should have a ref to the DOM document node, and the DOM doc + // has a ref to the presshell. + dom::Document* doc = docAcc ? docAcc->DocumentNode() : nullptr; + if (doc && doc->IsContentDocument()) { + // Only content accessibles should have an associated SessionAccessible. + return GetInstanceFor(doc->GetPresShell()); + } + } else { + dom::CanonicalBrowsingContext* cbc = + static_cast( + aAccessible->AsRemote()->Document()->Manager()) + ->GetBrowsingContext() + ->Top(); + dom::BrowserParent* bp = cbc->GetBrowserParent(); + if (!bp) { + bp = static_cast( + aAccessible->AsRemote()->Document()->Manager()); + } + if (auto element = bp->GetOwnerElement()) { + if (auto doc = element->OwnerDoc()) { + if (nsPresContext* presContext = doc->GetPresContext()) { + return GetInstanceFor(presContext->PresShell()); + } + } else { + MOZ_ASSERT_UNREACHABLE( + "Browser parent's element does not have owner doc."); + } + } + } + + return nullptr; +} + +RefPtr SessionAccessibility::GetInstanceFor( + PresShell* aPresShell) { + MOZ_ASSERT(NS_IsMainThread()); + if (!aPresShell) { + return nullptr; + } + + nsViewManager* vm = aPresShell->GetViewManager(); + if (!vm) { + return nullptr; + } + + nsCOMPtr rootWidget = vm->GetRootWidget(); + // `rootWidget` can be one of several types. Here we make sure it is an + // android nsWindow. + if (RefPtr window = nsWindow::From(rootWidget)) { + return window->GetSessionAccessibility(); + } + + return nullptr; +} + +void SessionAccessibility::SendAccessibilityFocusedEvent( + Accessible* aAccessible, bool aScrollIntoView) { + MOZ_ASSERT(NS_IsMainThread()); + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_ACCESSIBILITY_FOCUSED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), nullptr); + if (aScrollIntoView) { + aAccessible->ScrollTo(nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE); + } +} + +void SessionAccessibility::SendHoverEnterEvent(Accessible* aAccessible) { + MOZ_ASSERT(NS_IsMainThread()); + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_HOVER_ENTER, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), nullptr); +} + +void SessionAccessibility::SendFocusEvent(Accessible* aAccessible) { + MOZ_ASSERT(NS_IsMainThread()); + // Suppress focus events from about:blank pages. + // This is important for tests. + if (aAccessible->IsDoc() && aAccessible->ChildCount() == 0) { + return; + } + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_FOCUSED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), nullptr); +} + +void SessionAccessibility::SendScrollingEvent(Accessible* aAccessible, + int32_t aScrollX, + int32_t aScrollY, + int32_t aMaxScrollX, + int32_t aMaxScrollY) { + MOZ_ASSERT(NS_IsMainThread()); + int32_t virtualViewId = AccessibleWrap::GetVirtualViewID(aAccessible); + + if (virtualViewId != kNoID) { + // XXX: Support scrolling in subframes + return; + } + + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "scrollX", java::sdk::Integer::ValueOf(aScrollX)); + GECKOBUNDLE_PUT(eventInfo, "scrollY", java::sdk::Integer::ValueOf(aScrollY)); + GECKOBUNDLE_PUT(eventInfo, "maxScrollX", + java::sdk::Integer::ValueOf(aMaxScrollX)); + GECKOBUNDLE_PUT(eventInfo, "maxScrollY", + java::sdk::Integer::ValueOf(aMaxScrollY)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_SCROLLED, virtualViewId, + AccessibleWrap::AndroidClass(aAccessible), eventInfo); + SendWindowContentChangedEvent(); +} + +void SessionAccessibility::SendWindowContentChangedEvent() { + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_WINDOW_CONTENT_CHANGED, kNoID, + java::SessionAccessibility::CLASSNAME_WEBVIEW, nullptr); +} + +void SessionAccessibility::SendWindowStateChangedEvent( + Accessible* aAccessible) { + MOZ_ASSERT(NS_IsMainThread()); + // Suppress window state changed events from about:blank pages. + // This is important for tests. + if (aAccessible->IsDoc() && aAccessible->ChildCount() == 0) { + return; + } + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_WINDOW_STATE_CHANGED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), nullptr); + + SendWindowContentChangedEvent(); +} + +void SessionAccessibility::SendTextSelectionChangedEvent( + Accessible* aAccessible, int32_t aCaretOffset) { + MOZ_ASSERT(NS_IsMainThread()); + int32_t fromIndex = aCaretOffset; + int32_t startSel = -1; + int32_t endSel = -1; + bool hasSelection = + aAccessible->AsHyperTextBase()->SelectionBoundsAt(0, &startSel, &endSel); + + if (hasSelection) { + fromIndex = startSel == aCaretOffset ? endSel : startSel; + } + + nsAutoString text; + if (aAccessible->IsHyperText()) { + aAccessible->AsHyperTextBase()->TextSubstring(0, -1, text); + } else if (aAccessible->IsText()) { + aAccessible->AppendTextTo(text, 0, -1); + } + + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(text)); + GECKOBUNDLE_PUT(eventInfo, "fromIndex", + java::sdk::Integer::ValueOf(fromIndex)); + GECKOBUNDLE_PUT(eventInfo, "toIndex", + java::sdk::Integer::ValueOf(aCaretOffset)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_TEXT_SELECTION_CHANGED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), eventInfo); +} + +void SessionAccessibility::SendTextChangedEvent(Accessible* aAccessible, + const nsAString& aStr, + int32_t aStart, uint32_t aLen, + bool aIsInsert, + bool aFromUser) { + MOZ_ASSERT(NS_IsMainThread()); + if (!aFromUser) { + // Only dispatch text change events from users, for now. + return; + } + + nsAutoString text; + if (aAccessible->IsHyperText()) { + aAccessible->AsHyperTextBase()->TextSubstring(0, -1, text); + } else if (aAccessible->IsText()) { + aAccessible->AppendTextTo(text, 0, -1); + } + nsAutoString beforeText(text); + if (aIsInsert) { + beforeText.Cut(aStart, aLen); + } else { + beforeText.Insert(aStr, aStart); + } + + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(text)); + GECKOBUNDLE_PUT(eventInfo, "beforeText", jni::StringParam(beforeText)); + GECKOBUNDLE_PUT(eventInfo, "fromIndex", java::sdk::Integer::ValueOf(aStart)); + GECKOBUNDLE_PUT(eventInfo, "addedCount", + java::sdk::Integer::ValueOf(aIsInsert ? aLen : 0)); + GECKOBUNDLE_PUT(eventInfo, "removedCount", + java::sdk::Integer::ValueOf(aIsInsert ? 0 : aLen)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_TEXT_CHANGED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), eventInfo); +} + +void SessionAccessibility::SendTextTraversedEvent(Accessible* aAccessible, + int32_t aStartOffset, + int32_t aEndOffset) { + MOZ_ASSERT(NS_IsMainThread()); + nsAutoString text; + if (aAccessible->IsHyperText()) { + aAccessible->AsHyperTextBase()->TextSubstring(0, -1, text); + } else if (aAccessible->IsText()) { + aAccessible->AppendTextTo(text, 0, -1); + } + + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(text)); + GECKOBUNDLE_PUT(eventInfo, "fromIndex", + java::sdk::Integer::ValueOf(aStartOffset)); + GECKOBUNDLE_PUT(eventInfo, "toIndex", + java::sdk::Integer::ValueOf(aEndOffset)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent:: + TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), eventInfo); +} + +void SessionAccessibility::SendClickedEvent(Accessible* aAccessible, + uint32_t aFlags) { + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "flags", java::sdk::Integer::ValueOf(aFlags)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_CLICKED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), eventInfo); +} + +void SessionAccessibility::SendSelectedEvent(Accessible* aAccessible, + bool aSelected) { + MOZ_ASSERT(NS_IsMainThread()); + GECKOBUNDLE_START(eventInfo); + // Boolean::FALSE/TRUE gets clobbered by a macro, so ugh. + GECKOBUNDLE_PUT(eventInfo, "selected", + java::sdk::Integer::ValueOf(aSelected ? 1 : 0)); + GECKOBUNDLE_FINISH(eventInfo); + + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_VIEW_SELECTED, + AccessibleWrap::GetVirtualViewID(aAccessible), + AccessibleWrap::AndroidClass(aAccessible), eventInfo); +} + +void SessionAccessibility::SendAnnouncementEvent(Accessible* aAccessible, + const nsAString& aAnnouncement, + uint16_t aPriority) { + MOZ_ASSERT(NS_IsMainThread()); + GECKOBUNDLE_START(eventInfo); + GECKOBUNDLE_PUT(eventInfo, "text", jni::StringParam(aAnnouncement)); + GECKOBUNDLE_FINISH(eventInfo); + + // Announcements should have the root as their source, so we ignore the + // accessible of the event. + mSessionAccessibility->SendEvent( + java::sdk::AccessibilityEvent::TYPE_ANNOUNCEMENT, kNoID, + java::SessionAccessibility::CLASSNAME_WEBVIEW, eventInfo); +} + +void SessionAccessibility::PopulateNodeInfo( + Accessible* aAccessible, mozilla::jni::Object::Param aNodeInfo) { + nsAutoString name; + aAccessible->Name(name); + nsAutoString textValue; + aAccessible->Value(textValue); + nsAutoString nodeID; + aAccessible->DOMNodeID(nodeID); + nsAutoString accDesc; + aAccessible->Description(accDesc); + uint64_t state = aAccessible->State(); + LayoutDeviceIntRect bounds = aAccessible->Bounds(); + uint8_t actionCount = aAccessible->ActionCount(); + int32_t virtualViewID = AccessibleWrap::GetVirtualViewID(aAccessible); + Accessible* parent = virtualViewID != kNoID ? aAccessible->Parent() : nullptr; + int32_t parentID = parent ? AccessibleWrap::GetVirtualViewID(parent) : 0; + role role = aAccessible->Role(); + if (role == roles::LINK && !(state & states::LINKED)) { + // A link without the linked state ( with no href) shouldn't be presented + // as a link. + role = roles::TEXT; + } + + uint32_t flags = AccessibleWrap::GetFlags(role, state, actionCount); + int32_t className = AccessibleWrap::AndroidClass(aAccessible); + + nsAutoString hint; + nsAutoString text; + nsAutoString description; + if (state & states::EDITABLE) { + // An editable field's name is populated in the hint. + hint.Assign(name); + text.Assign(textValue); + } else { + if (role == roles::LINK || role == roles::HEADING) { + description.Assign(name); + } else { + text.Assign(name); + } + } + + if (!accDesc.IsEmpty()) { + if (!hint.IsEmpty()) { + // If this is an editable, the description is concatenated with a + // whitespace directly after the name. + hint.AppendLiteral(" "); + } + hint.Append(accDesc); + } + + if ((state & states::REQUIRED) != 0) { + nsAutoString requiredString; + if (LocalizeString(u"stateRequired"_ns, requiredString)) { + if (!hint.IsEmpty()) { + // If the hint is non-empty, concatenate with a comma for a brief pause. + hint.AppendLiteral(", "); + } + hint.Append(requiredString); + } + } + + RefPtr attributes = aAccessible->Attributes(); + + nsAutoString geckoRole; + nsAutoString roleDescription; + if (virtualViewID != kNoID) { + AccessibleWrap::GetRoleDescription(role, attributes, geckoRole, + roleDescription); + } + + int32_t inputType = 0; + if (attributes) { + nsString inputTypeAttr; + attributes->GetAttribute(nsGkAtoms::textInputType, inputTypeAttr); + inputType = AccessibleWrap::GetInputType(inputTypeAttr); + } + + auto childCount = aAccessible->ChildCount(); + nsTArray children(childCount); + if (!nsAccUtils::MustPrune(aAccessible)) { + for (uint32_t i = 0; i < childCount; i++) { + auto child = aAccessible->ChildAt(i); + children.AppendElement(AccessibleWrap::GetVirtualViewID(child)); + } + } + + const int32_t boundsArray[4] = {bounds.x, bounds.y, bounds.x + bounds.width, + bounds.y + bounds.height}; + + mSessionAccessibility->PopulateNodeInfo( + aNodeInfo, virtualViewID, parentID, jni::IntArray::From(children), flags, + className, jni::IntArray::New(boundsArray, 4), jni::StringParam(text), + jni::StringParam(description), jni::StringParam(hint), + jni::StringParam(geckoRole), jni::StringParam(roleDescription), + jni::StringParam(nodeID), inputType); + + if (aAccessible->HasNumericValue()) { + double curValue = aAccessible->CurValue(); + double minValue = aAccessible->MinValue(); + double maxValue = aAccessible->MaxValue(); + double step = aAccessible->Step(); + + int32_t rangeType = 0; // integer + if (maxValue == 1 && minValue == 0) { + rangeType = 2; // percent + } else if (std::round(step) != step) { + rangeType = 1; // float; + } + + mSessionAccessibility->PopulateNodeRangeInfo( + aNodeInfo, rangeType, static_cast(minValue), + static_cast(maxValue), static_cast(curValue)); + } + + if (attributes) { + Maybe rowIndex = + attributes->GetAttribute(nsGkAtoms::posinset); + if (rowIndex) { + mSessionAccessibility->PopulateNodeCollectionItemInfo( + aNodeInfo, *rowIndex - 1, 1, 0, 1); + } + + Maybe rowCount = + attributes->GetAttribute(nsGkAtoms::child_item_count); + if (rowCount) { + int32_t selectionMode = 0; + if (aAccessible->IsSelect()) { + selectionMode = (state & states::MULTISELECTABLE) ? 2 : 1; + } + mSessionAccessibility->PopulateNodeCollectionInfo( + aNodeInfo, *rowCount, 1, selectionMode, + attributes->HasAttribute(nsGkAtoms::tree)); + } + } +} + +Accessible* SessionAccessibility::GetAccessibleByID(int32_t aID) const { + return mIDToAccessibleMap.Get(aID); +} + +#ifdef DEBUG +static bool IsDetachedDoc(Accessible* aAccessible) { + if (!aAccessible->IsRemote() || !aAccessible->AsRemote()->IsDoc()) { + return false; + } + + return !aAccessible->Parent() || + aAccessible->Parent()->FirstChild() != aAccessible; +} +#endif + +SessionAccessibility::IDMappingEntry::IDMappingEntry(Accessible* aAccessible) + : mInternalID(0) { + *this = aAccessible; +} + +SessionAccessibility::IDMappingEntry& +SessionAccessibility::IDMappingEntry::operator=(Accessible* aAccessible) { + mInternalID = aAccessible->ID(); + MOZ_ASSERT(!(mInternalID & IS_REMOTE), "First bit is used in accessible ID!"); + if (aAccessible->IsRemote()) { + mInternalID |= IS_REMOTE; + } + + Accessible* docAcc = nsAccUtils::DocumentFor(aAccessible); + MOZ_ASSERT(docAcc); + if (docAcc) { + MOZ_ASSERT(docAcc->IsRemote() == aAccessible->IsRemote()); + if (docAcc->IsRemote()) { + mDoc = docAcc->AsRemote()->AsDoc(); + } else { + mDoc = docAcc->AsLocal(); + } + } + + return *this; +} + +SessionAccessibility::IDMappingEntry::operator Accessible*() const { + if (mInternalID == 0) { + return static_cast(mDoc.get()); + } + + if (mInternalID == IS_REMOTE) { + return static_cast(mDoc.get()); + } + + if (mInternalID & IS_REMOTE) { + return static_cast(mDoc.get()) + ->GetAccessible(mInternalID & ~IS_REMOTE); + } + + Accessible* accessible = + static_cast(mDoc.get()) + ->AsDoc() + ->GetAccessibleByUniqueID(reinterpret_cast(mInternalID)); + // If the accessible is retrievable from the DocAccessible, it can't be + // defunct. + MOZ_ASSERT(!accessible->AsLocal()->IsDefunct()); + + return accessible; +} + +void SessionAccessibility::RegisterAccessible(Accessible* aAccessible) { + if (IPCAccessibilityActive()) { + // Don't register accessible in content process. + return; + } + + nsAccessibilityService::GetAndroidMonitor().AssertCurrentThreadOwns(); + RefPtr sessionAcc = GetInstanceFor(aAccessible); + if (!sessionAcc) { + return; + } + + bool isTopLevel = false; + if (aAccessible->IsLocal() && aAccessible->IsDoc()) { + DocAccessibleWrap* doc = + static_cast(aAccessible->AsLocal()->AsDoc()); + isTopLevel = doc->IsTopLevelContentDoc(); + } else if (aAccessible->IsRemote() && aAccessible->IsDoc()) { + isTopLevel = aAccessible->AsRemote()->AsDoc()->IsTopLevel(); + } + + int32_t virtualViewID = kNoID; + if (!isTopLevel) { + if (sessionAcc->mIDToAccessibleMap.IsEmpty()) { + // We expect there to already be at least one accessible + // registered (the top-level one). If it isn't we are + // probably in a shutdown process where it was already + // unregistered. So we don't register this accessible. + return; + } + // Don't use the special "unset" value (0). + while ((virtualViewID = sIDSet.GetID()) == kUnsetID) { + } + } + AccessibleWrap::SetVirtualViewID(aAccessible, virtualViewID); + + Accessible* oldAcc = sessionAcc->mIDToAccessibleMap.Get(virtualViewID); + if (oldAcc) { + // About to overwrite mapping of registered accessible. This should + // only happen when the registered accessible is a detached document. + MOZ_ASSERT(IsDetachedDoc(oldAcc), + "ID already registered to non-detached document"); + AccessibleWrap::SetVirtualViewID(oldAcc, kUnsetID); + } + + sessionAcc->mIDToAccessibleMap.InsertOrUpdate(virtualViewID, aAccessible); +} + +void SessionAccessibility::UnregisterAccessible(Accessible* aAccessible) { + if (IPCAccessibilityActive()) { + // Don't unregister accessible in content process. + return; + } + + nsAccessibilityService::GetAndroidMonitor().AssertCurrentThreadOwns(); + int32_t virtualViewID = AccessibleWrap::GetVirtualViewID(aAccessible); + if (virtualViewID == kUnsetID) { + return; + } + + RefPtr sessionAcc = GetInstanceFor(aAccessible); + if (sessionAcc) { + Accessible* registeredAcc = + sessionAcc->mIDToAccessibleMap.Get(virtualViewID); + if (registeredAcc != aAccessible) { + // Attempting to unregister an accessible that is not mapped to + // its virtual view ID. This probably means it is a detached document + // and a more recent document overwrote its '-1' mapping. + // We set its own virtual view ID to `kUnsetID` and return early. + MOZ_ASSERT(!registeredAcc || IsDetachedDoc(aAccessible), + "Accessible is detached document"); + AccessibleWrap::SetVirtualViewID(aAccessible, kUnsetID); + return; + } + + MOZ_ASSERT(registeredAcc, "Unregistering unregistered accessible"); + MOZ_ASSERT(registeredAcc == aAccessible, "Unregistering wrong accessible"); + sessionAcc->mIDToAccessibleMap.Remove(virtualViewID); + } + + if (virtualViewID > kNoID) { + sIDSet.ReleaseID(virtualViewID); + } + + AccessibleWrap::SetVirtualViewID(aAccessible, kUnsetID); +} + +void SessionAccessibility::UnregisterAll(PresShell* aPresShell) { + if (IPCAccessibilityActive()) { + // Don't unregister accessible in content process. + return; + } + + nsAccessibilityService::GetAndroidMonitor().AssertCurrentThreadOwns(); + RefPtr sessionAcc = GetInstanceFor(aPresShell); + if (sessionAcc) { + sessionAcc->mIDToAccessibleMap.Clear(); + } +} diff --git a/accessible/android/SessionAccessibility.h b/accessible/android/SessionAccessibility.h new file mode 100644 index 0000000000..deacc57150 --- /dev/null +++ b/accessible/android/SessionAccessibility.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef mozilla_a11y_SessionAccessibility_h_ +#define mozilla_a11y_SessionAccessibility_h_ + +#include "mozilla/java/SessionAccessibilityNatives.h" +#include "mozilla/widget/GeckoViewSupport.h" +#include "nsAppShell.h" +#include "nsThreadUtils.h" +#include "nsWindow.h" + +namespace mozilla { +namespace a11y { + +class Accessible; + +class SessionAccessibility final + : public java::SessionAccessibility::NativeProvider::Natives< + SessionAccessibility> { + public: + typedef java::SessionAccessibility::NativeProvider::Natives< + SessionAccessibility> + Base; + + SessionAccessibility( + jni::NativeWeakPtr aWindow, + java::SessionAccessibility::NativeProvider::Param aSessionAccessibility); + + void OnWeakNonIntrusiveDetach(already_AddRefed aDisposer) { + SetAttached(false, std::move(aDisposer)); + } + + const java::SessionAccessibility::NativeProvider::Ref& + GetJavaAccessibility() { + return mSessionAccessibility; + } + + static void Init(); + static RefPtr GetInstanceFor(Accessible* aAccessible); + static RefPtr GetInstanceFor(PresShell* aPresShell); + + // Native implementations + using Base::AttachNative; + using Base::DisposeNative; + void GetNodeInfo(int32_t aID, mozilla::jni::Object::Param aNodeInfo); + int GetNodeClassName(int32_t aID); + void SetText(int32_t aID, jni::String::Param aText); + void Click(int32_t aID); + bool Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive); + void ExploreByTouch(int32_t aID, float aX, float aY); + bool NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset, + int32_t aEndOffset, bool aForward, bool aSelect); + void SetSelection(int32_t aID, int32_t aStart, int32_t aEnd); + void Cut(int32_t aID); + void Copy(int32_t aID); + MOZ_CAN_RUN_SCRIPT_BOUNDARY void Paste(int32_t aID); + void StartNativeAccessibility(); + + // Event methods + void SendFocusEvent(Accessible* aAccessible); + void SendScrollingEvent(Accessible* aAccessible, int32_t aScrollX, + int32_t aScrollY, int32_t aMaxScrollX, + int32_t aMaxScrollY); + MOZ_CAN_RUN_SCRIPT_BOUNDARY + void SendAccessibilityFocusedEvent(Accessible* aAccessible, + bool aScrollIntoView); + void SendHoverEnterEvent(Accessible* aAccessible); + void SendTextSelectionChangedEvent(Accessible* aAccessible, + int32_t aCaretOffset); + void SendTextTraversedEvent(Accessible* aAccessible, int32_t aStartOffset, + int32_t aEndOffset); + void SendTextChangedEvent(Accessible* aAccessible, const nsAString& aStr, + int32_t aStart, uint32_t aLen, bool aIsInsert, + bool aFromUser); + void SendSelectedEvent(Accessible* aAccessible, bool aSelected); + void SendClickedEvent(Accessible* aAccessible, uint32_t aFlags); + void SendWindowContentChangedEvent(); + void SendWindowStateChangedEvent(Accessible* aAccessible); + void SendAnnouncementEvent(Accessible* aAccessible, + const nsAString& aAnnouncement, + uint16_t aPriority); + + Accessible* GetAccessibleByID(int32_t aID) const; + + static const int32_t kNoID = -1; + static const int32_t kUnsetID = 0; + + static void RegisterAccessible(Accessible* aAccessible); + static void UnregisterAccessible(Accessible* aAccessible); + static void UnregisterAll(PresShell* aPresShell); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SessionAccessibility) + + private: + ~SessionAccessibility() {} + + void PopulateNodeInfo(Accessible* aAccessible, + mozilla::jni::Object::Param aNodeInfo); + + void SetAttached(bool aAttached, already_AddRefed aRunnable); + + bool DoNavigateText(Accessible* aAccessible, int32_t aGranularity, + int32_t aStartOffset, int32_t aEndOffset, bool aForward, + bool aSelect); + + jni::NativeWeakPtr mWindow; // Parent only + java::SessionAccessibility::NativeProvider::GlobalRef mSessionAccessibility; + + class IDMappingEntry { + public: + explicit IDMappingEntry(Accessible* aAccessible); + + IDMappingEntry& operator=(Accessible* aAccessible); + + operator Accessible*() const; + + private: + // A strong reference to a DocAccessible or DocAccessibleParent. They don't + // share any useful base class except nsISupports, so we use that. + // When we retrieve the document from this reference we cast it to + // LocalAccessible in the DocAccessible case because DocAccessible has + // multiple inheritance paths for nsISupports. + RefPtr mDoc; + // The ID of the accessible as used in the internal doc mapping. + // We rely on this ID being pointer derived and therefore divisible by two + // so we can use the first bit to mark if it is remote or not. + uint64_t mInternalID; + + static const uintptr_t IS_REMOTE = 0x1; + }; + + /* + * This provides a mapping from 32 bit id to accessible objects. + */ + nsBaseHashtable + mIDToAccessibleMap; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/TraversalRule.cpp b/accessible/android/TraversalRule.cpp new file mode 100644 index 0000000000..4203fd48c6 --- /dev/null +++ b/accessible/android/TraversalRule.cpp @@ -0,0 +1,288 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TraversalRule.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/a11y/Accessible.h" + +#include "mozilla/a11y/Role.h" +#include "HTMLListAccessible.h" +#include "SessionAccessibility.h" +#include "nsAccUtils.h" +#include "nsIAccessiblePivot.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +TraversalRule::TraversalRule() + : TraversalRule(java::SessionAccessibility::HTML_GRANULARITY_DEFAULT, + true) {} + +TraversalRule::TraversalRule(int32_t aGranularity, bool aIsLocal) + : mGranularity(aGranularity), mIsLocal(aIsLocal) {} + +uint16_t TraversalRule::Match(Accessible* aAcc) { + MOZ_ASSERT(aAcc); + + if (mIsLocal && aAcc->IsRemote()) { + // If we encounter a remote accessible in a local rule, we should + // ignore the subtree because we won't encounter anymore local accessibles + // in it. + return nsIAccessibleTraversalRule::FILTER_IGNORE | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } else if (!mIsLocal && aAcc->IsLocal()) { + // If we encounter a local accessible in a remote rule we are likely + // traversing backwards/upwards, we don't ignore its subtree because it is + // likely the outer doc root of the remote tree. + return nsIAccessibleTraversalRule::FILTER_IGNORE; + } + + uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE; + + if (nsAccUtils::MustPrune(aAcc)) { + result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + + uint64_t state = aAcc->State(); + + if ((state & states::INVISIBLE) != 0) { + return result; + } + + if (aAcc->Opacity() == 0.0f) { + return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + + switch (mGranularity) { + case java::SessionAccessibility::HTML_GRANULARITY_LINK: + result |= LinkMatch(aAcc); + break; + case java::SessionAccessibility::HTML_GRANULARITY_CONTROL: + result |= ControlMatch(aAcc); + break; + case java::SessionAccessibility::HTML_GRANULARITY_SECTION: + result |= SectionMatch(aAcc); + break; + case java::SessionAccessibility::HTML_GRANULARITY_HEADING: + result |= HeadingMatch(aAcc); + break; + case java::SessionAccessibility::HTML_GRANULARITY_LANDMARK: + result |= LandmarkMatch(aAcc); + break; + default: + result |= DefaultMatch(aAcc); + break; + } + + return result; +} + +bool TraversalRule::IsSingleLineage(Accessible* aAccessible) { + Accessible* child = aAccessible; + while (child) { + switch (child->ChildCount()) { + case 0: + return true; + case 1: + child = child->FirstChild(); + break; + case 2: + if (IsListItemBullet(child->FirstChild())) { + child = child->LastChild(); + } else { + return false; + } + break; + default: + return false; + } + } + + return true; +} + +bool TraversalRule::IsListItemBullet(const Accessible* aAccessible) { + return aAccessible->Role() == roles::LISTITEM_MARKER; +} + +bool TraversalRule::IsFlatSubtree(const Accessible* aAccessible) { + for (auto child = aAccessible->FirstChild(); child; + child = child->NextSibling()) { + roles::Role role = child->Role(); + if (role == roles::TEXT_LEAF || role == roles::STATICTEXT) { + continue; + } + + if (child->ChildCount() > 0 || child->ActionCount() > 0) { + return false; + } + } + + return true; +} + +bool TraversalRule::HasName(const Accessible* aAccessible) { + nsAutoString name; + aAccessible->Name(name); + name.CompressWhitespace(); + return !name.IsEmpty(); +} + +uint16_t TraversalRule::LinkMatch(Accessible* aAccessible) { + if (aAccessible->Role() == roles::LINK && + (aAccessible->State() & states::LINKED) != 0) { + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} + +uint16_t TraversalRule::HeadingMatch(Accessible* aAccessible) { + if (aAccessible->Role() == roles::HEADING && aAccessible->ChildCount()) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} + +uint16_t TraversalRule::SectionMatch(Accessible* aAccessible) { + roles::Role role = aAccessible->Role(); + if (role == roles::HEADING || role == roles::LANDMARK || + aAccessible->LandmarkRole()) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} + +uint16_t TraversalRule::LandmarkMatch(Accessible* aAccessible) { + if (aAccessible->LandmarkRole()) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} + +uint16_t TraversalRule::ControlMatch(Accessible* aAccessible) { + switch (aAccessible->Role()) { + case roles::PUSHBUTTON: + case roles::SPINBUTTON: + case roles::TOGGLE_BUTTON: + case roles::BUTTONDROPDOWN: + case roles::COMBOBOX: + case roles::LISTBOX: + case roles::ENTRY: + case roles::PASSWORD_TEXT: + case roles::PAGETAB: + case roles::RADIOBUTTON: + case roles::RADIO_MENU_ITEM: + case roles::SLIDER: + case roles::CHECKBUTTON: + case roles::CHECK_MENU_ITEM: + case roles::SWITCH: + case roles::MENUITEM: + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + case roles::LINK: + return LinkMatch(aAccessible); + case roles::EDITCOMBOBOX: + if (aAccessible->State() & states::EDITABLE) { + // Only match ARIA 1.0 comboboxes; i.e. where the combobox itself is + // editable. If it's a 1.1 combobox, the combobox is just a container; + // we want to stop on the textbox inside it, not the container. + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + break; + default: + break; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} + +uint16_t TraversalRule::DefaultMatch(Accessible* aAccessible) { + switch (aAccessible->Role()) { + case roles::COMBOBOX: + // We don't want to ignore the subtree because this is often + // where the list box hangs out. + return nsIAccessibleTraversalRule::FILTER_MATCH; + case roles::EDITCOMBOBOX: + if (aAccessible->State() & states::EDITABLE) { + // Only match ARIA 1.0 comboboxes; i.e. where the combobox itself is + // editable. If it's a 1.1 combobox, the combobox is just a container; + // we want to stop on the textbox inside it. + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + break; + case roles::TEXT_LEAF: + case roles::GRAPHIC: + // Nameless text leaves are boring, skip them. + if (HasName(aAccessible)) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + break; + case roles::STATICTEXT: + // Ignore list bullets + if (!IsListItemBullet(aAccessible)) { + return nsIAccessibleTraversalRule::FILTER_MATCH; + } + break; + case roles::HEADING: + case roles::COLUMNHEADER: + case roles::ROWHEADER: + case roles::STATUSBAR: + if ((aAccessible->ChildCount() > 0 || HasName(aAccessible)) && + (IsSingleLineage(aAccessible) || IsFlatSubtree(aAccessible))) { + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + break; + case roles::GRID_CELL: + if (IsSingleLineage(aAccessible) || IsFlatSubtree(aAccessible)) { + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + break; + case roles::LABEL: + if (IsFlatSubtree(aAccessible)) { + // Match if this is a label with text but no nested controls. + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + break; + case roles::MENUITEM: + case roles::LINK: + case roles::PAGETAB: + case roles::PUSHBUTTON: + case roles::CHECKBUTTON: + case roles::RADIOBUTTON: + case roles::PROGRESSBAR: + case roles::BUTTONDROPDOWN: + case roles::BUTTONMENU: + case roles::CHECK_MENU_ITEM: + case roles::PASSWORD_TEXT: + case roles::RADIO_MENU_ITEM: + case roles::TOGGLE_BUTTON: + case roles::ENTRY: + case roles::KEY: + case roles::SLIDER: + case roles::SPINBUTTON: + case roles::OPTION: + case roles::SWITCH: + case roles::MATHML_MATH: + // Ignore the subtree, if there is one. So that we don't land on + // the same content that was already presented by its parent. + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + default: + break; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE; +} diff --git a/accessible/android/TraversalRule.h b/accessible/android/TraversalRule.h new file mode 100644 index 0000000000..27b2b2f5fb --- /dev/null +++ b/accessible/android/TraversalRule.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _TraversalRule_H_ +#define _TraversalRule_H_ + +#include "Pivot.h" + +namespace mozilla { +namespace a11y { + +class Accessible; + +/** + * Class represents a simple traversal rule. + */ +class TraversalRule : public PivotRule { + public: + TraversalRule(); + explicit TraversalRule(int32_t aGranularity, bool aIsLocal); + + ~TraversalRule() = default; + + virtual uint16_t Match(Accessible* aAcc) override; + + private: + bool IsSingleLineage(Accessible* aAccessible); + + bool IsFlatSubtree(const Accessible* aAccessible); + + bool IsListItemBullet(const Accessible* aAccessible); + + bool HasName(const Accessible* aAccessible); + + uint16_t DefaultMatch(Accessible* aAccessible); + + uint16_t LinkMatch(Accessible* aAccessible); + + uint16_t HeadingMatch(Accessible* aAccessible); + + uint16_t ControlMatch(Accessible* aAccessible); + + uint16_t SectionMatch(Accessible* aAccessible); + + uint16_t LandmarkMatch(Accessible* aAccessible); + + int32_t mGranularity; + + bool mIsLocal; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/android/moz.build b/accessible/android/moz.build new file mode 100644 index 0000000000..f8a185da48 --- /dev/null +++ b/accessible/android/moz.build @@ -0,0 +1,35 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.a11y += [ + "AccessibleWrap.h", + "SessionAccessibility.h", + "TraversalRule.h", +] + +SOURCES += [ + "AccessibleWrap.cpp", + "DocAccessibleWrap.cpp", + "Platform.cpp", + "SessionAccessibility.cpp", + "TraversalRule.cpp", +] + +LOCAL_INCLUDES += [ + "/accessible/base", + "/accessible/generic", + "/accessible/html", + "/accessible/ipc", + "/accessible/xpcom", + "/accessible/xul", + "/dom/base", + "/widget", + "/widget/android", +] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/accessible/aom/AccessibleNode.cpp b/accessible/aom/AccessibleNode.cpp new file mode 100644 index 0000000000..2aff34238c --- /dev/null +++ b/accessible/aom/AccessibleNode.cpp @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 "AccessibleNode.h" +#include "mozilla/dom/AccessibleNodeBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMStringList.h" +#include "mozilla/StaticPrefs_accessibility.h" +#include "nsContentUtils.h" + +#include "LocalAccessible-inl.h" +#include "nsAccessibilityService.h" +#include "DocAccessible.h" + +#include "mozilla/dom/ToJSValue.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::dom; + +bool AccessibleNode::IsAOMEnabled(JSContext* aCx, JSObject* /*unused*/) { + return nsContentUtils::IsSystemCaller(aCx) || + StaticPrefs::accessibility_AOM_enabled(); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AccessibleNode, mRelationProperties, + mIntl, mDOMNode, mStates) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AccessibleNode) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AccessibleNode) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AccessibleNode) + +AccessibleNode::AccessibleNode(nsINode* aNode) + : mDoubleProperties(3), + mIntProperties(3), + mUIntProperties(6), + mBooleanProperties(0), + mRelationProperties(3), + mStringProperties(16), + mDOMNode(aNode) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!accService) { + return; + } + + DocAccessible* doc = accService->GetDocAccessible(mDOMNode->OwnerDoc()); + if (doc) { + mIntl = doc->GetAccessible(mDOMNode); + } +} + +AccessibleNode::~AccessibleNode() {} + +/* virtual */ +JSObject* AccessibleNode::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return AccessibleNode_Binding::Wrap(aCx, this, aGivenProto); +} + +/* virtual */ +ParentObject AccessibleNode::GetParentObject() const { + return mDOMNode->GetParentObject(); +} + +void AccessibleNode::GetComputedRole(nsAString& aRole) { + if (mIntl) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (accService) { + accService->GetStringRole(mIntl->Role(), aRole); + return; + } + } + + aRole.AssignLiteral("unknown"); +} + +void AccessibleNode::GetStates(nsTArray& aStates) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!mIntl || !accService) { + aStates.AppendElement(u"defunct"_ns); + return; + } + + if (mStates) { + aStates = mStates->StringArray().Clone(); + return; + } + + mStates = accService->GetStringStates(mIntl->State()); + aStates = mStates->StringArray().Clone(); +} + +void AccessibleNode::GetAttributes(nsTArray& aAttributes) { + if (!mIntl) { + return; + } + + RefPtr attrs = mIntl->Attributes(); + + for (const auto& iter : *attrs) { + aAttributes.AppendElement(nsAtomString(iter.Name())); + } +} + +bool AccessibleNode::Is(const Sequence& aFlavors) { + nsAccessibilityService* accService = GetOrCreateAccService(); + if (!mIntl || !accService) { + for (const auto& flavor : aFlavors) { + if (!flavor.EqualsLiteral("unknown") && + !flavor.EqualsLiteral("defunct")) { + return false; + } + } + return true; + } + + nsAutoString role; + accService->GetStringRole(mIntl->Role(), role); + + if (!mStates) { + mStates = accService->GetStringStates(mIntl->State()); + } + + for (const auto& flavor : aFlavors) { + if (!flavor.Equals(role) && !mStates->Contains(flavor)) { + return false; + } + } + return true; +} + +bool AccessibleNode::Has(const Sequence& aAttributes) { + if (!mIntl) { + return false; + } + RefPtr attrs = mIntl->Attributes(); + for (const auto& attr : aAttributes) { + RefPtr attrAtom = NS_Atomize(attr); + if (!attrs->HasAttribute(attrAtom)) { + return false; + } + } + return true; +} + +void AccessibleNode::Get(JSContext* aCX, const nsAString& aAttribute, + JS::MutableHandle aValue, + ErrorResult& aRv) { + if (!mIntl) { + aRv.ThrowInvalidStateError("No attributes available"); + return; + } + + RefPtr attrAtom = NS_Atomize(aAttribute); + RefPtr attrs = mIntl->Attributes(); + nsAutoString valueStr; + attrs->GetAttribute(attrAtom, valueStr); + if (!ToJSValue(aCX, valueStr, aValue)) { + aRv.NoteJSContextException(aCX); + return; + } +} + +nsINode* AccessibleNode::GetDOMNode() { return mDOMNode; } diff --git a/accessible/aom/AccessibleNode.h b/accessible/aom/AccessibleNode.h new file mode 100644 index 0000000000..e9b328b13d --- /dev/null +++ b/accessible/aom/AccessibleNode.h @@ -0,0 +1,212 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* 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/. */ + +#ifndef A11Y_AOM_ACCESSIBLENODE_H +#define A11Y_AOM_ACCESSIBLENODE_H + +#include "nsTHashMap.h" +#include "nsRefPtrHashtable.h" +#include "nsWrapperCache.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMString.h" +#include "mozilla/dom/Nullable.h" + +class nsINode; + +namespace mozilla { + +class ErrorResult; + +namespace a11y { +class LocalAccessible; +} + +namespace dom { + +class DOMStringList; +struct ParentObject; + +#define ANODE_ENUM(name) e##name, + +#define ANODE_FUNC(typeName, type, name) \ + dom::Nullable Get##name() { \ + return GetProperty(AOM##typeName##Property::e##name); \ + } \ + \ + void Set##name(const dom::Nullable& a##name) { \ + SetProperty(AOM##typeName##Property::e##name, a##name); \ + } + +#define ANODE_STRING_FUNC(name) \ + void Get##name(nsAString& a##name) { \ + return GetProperty(AOMStringProperty::e##name, a##name); \ + } \ + \ + void Set##name(const nsAString& a##name) { \ + SetProperty(AOMStringProperty::e##name, a##name); \ + } + +#define ANODE_RELATION_FUNC(name) \ + already_AddRefed Get##name() { \ + return GetProperty(AOMRelationProperty::e##name); \ + } \ + \ + void Set##name(AccessibleNode* a##name) { \ + SetProperty(AOMRelationProperty::e##name, a##name); \ + } + +#define ANODE_PROPS(typeName, type, ...) \ + enum class AOM##typeName##Property{ \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__))}; \ + MOZ_FOR_EACH(ANODE_FUNC, (typeName, type, ), (__VA_ARGS__)) + +#define ANODE_STRING_PROPS(...) \ + enum class AOMStringProperty { \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ + }; \ + MOZ_FOR_EACH(ANODE_STRING_FUNC, (), (__VA_ARGS__)) + +#define ANODE_RELATION_PROPS(...) \ + enum class AOMRelationProperty { \ + MOZ_FOR_EACH(ANODE_ENUM, (), (__VA_ARGS__)) \ + }; \ + MOZ_FOR_EACH(ANODE_RELATION_FUNC, (), (__VA_ARGS__)) + +#define ANODE_ACCESSOR_MUTATOR(typeName, type, defVal) \ + nsTHashMap m##typeName##Properties; \ + \ + dom::Nullable GetProperty(AOM##typeName##Property aProperty) { \ + type value = defVal; \ + if (m##typeName##Properties.Get(static_cast(aProperty), &value)) { \ + return dom::Nullable(value); \ + } \ + return dom::Nullable(); \ + } \ + \ + void SetProperty(AOM##typeName##Property aProperty, \ + const dom::Nullable& aValue) { \ + if (aValue.IsNull()) { \ + m##typeName##Properties.Remove(static_cast(aProperty)); \ + } else { \ + m##typeName##Properties.InsertOrUpdate(static_cast(aProperty), \ + aValue.Value()); \ + } \ + } + +class AccessibleNode : public nsISupports, public nsWrapperCache { + public: + explicit AccessibleNode(nsINode* aNode); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS; + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AccessibleNode); + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) final; + dom::ParentObject GetParentObject() const; + + void GetComputedRole(nsAString& aRole); + void GetStates(nsTArray& aStates); + void GetAttributes(nsTArray& aAttributes); + nsINode* GetDOMNode(); + + bool Is(const Sequence& aFlavors); + bool Has(const Sequence& aAttributes); + void Get(JSContext* cx, const nsAString& aAttribute, + JS::MutableHandle aValue, ErrorResult& aRv); + + static bool IsAOMEnabled(JSContext*, JSObject*); + + ANODE_STRING_PROPS(Autocomplete, Checked, Current, HasPopUp, Invalid, + KeyShortcuts, Label, Live, Orientation, Placeholder, + Pressed, Relevant, Role, RoleDescription, Sort, ValueText) + + ANODE_PROPS(Boolean, bool, Atomic, Busy, Disabled, Expanded, Hidden, Modal, + Multiline, Multiselectable, ReadOnly, Required, Selected) + + ANODE_PROPS(UInt, uint32_t, ColIndex, ColSpan, Level, PosInSet, RowIndex, + RowSpan) + + ANODE_PROPS(Int, int32_t, ColCount, RowCount, SetSize) + + ANODE_PROPS(Double, double, ValueMax, ValueMin, ValueNow) + + ANODE_RELATION_PROPS(ActiveDescendant, Details, ErrorMessage) + + protected: + AccessibleNode(const AccessibleNode& aCopy) = delete; + AccessibleNode& operator=(const AccessibleNode& aCopy) = delete; + virtual ~AccessibleNode(); + + void GetProperty(AOMStringProperty aProperty, nsAString& aRetval) { + nsString data; + if (!mStringProperties.Get(static_cast(aProperty), &data)) { + SetDOMStringToNull(data); + } + aRetval = data; + } + + void SetProperty(AOMStringProperty aProperty, const nsAString& aValue) { + if (DOMStringIsNull(aValue)) { + mStringProperties.Remove(static_cast(aProperty)); + } else { + nsString value(aValue); + mStringProperties.InsertOrUpdate(static_cast(aProperty), value); + } + } + + dom::Nullable GetProperty(AOMBooleanProperty aProperty) { + int num = static_cast(aProperty); + if (mBooleanProperties & (1U << (2 * num))) { + bool data = static_cast(mBooleanProperties & (1U << (2 * num + 1))); + return dom::Nullable(data); + } + return dom::Nullable(); + } + + void SetProperty(AOMBooleanProperty aProperty, + const dom::Nullable& aValue) { + int num = static_cast(aProperty); + if (aValue.IsNull()) { + mBooleanProperties &= ~(1U << (2 * num)); + } else { + mBooleanProperties |= (1U << (2 * num)); + mBooleanProperties = + (aValue.Value() ? mBooleanProperties | (1U << (2 * num + 1)) + : mBooleanProperties & ~(1U << (2 * num + 1))); + } + } + + ANODE_ACCESSOR_MUTATOR(Double, double, 0.0) + ANODE_ACCESSOR_MUTATOR(Int, int32_t, 0) + ANODE_ACCESSOR_MUTATOR(UInt, uint32_t, 0) + + already_AddRefed GetProperty(AOMRelationProperty aProperty) { + return mRelationProperties.Get(static_cast(aProperty)); + } + + void SetProperty(AOMRelationProperty aProperty, AccessibleNode* aValue) { + if (!aValue) { + mRelationProperties.Remove(static_cast(aProperty)); + } else { + mRelationProperties.InsertOrUpdate(static_cast(aProperty), + RefPtr{aValue}); + } + } + + // The 2k'th bit indicates whether the k'th boolean property is used(1) or + // not(0) and 2k+1'th bit contains the property's value(1:true, 0:false) + uint32_t mBooleanProperties; + nsRefPtrHashtable mRelationProperties; + nsTHashMap mStringProperties; + + RefPtr mIntl; + RefPtr mDOMNode; + RefPtr mStates; +}; + +} // namespace dom +} // namespace mozilla + +#endif // A11Y_JSAPI_ACCESSIBLENODE diff --git a/accessible/aom/moz.build b/accessible/aom/moz.build new file mode 100644 index 0000000000..88b941435e --- /dev/null +++ b/accessible/aom/moz.build @@ -0,0 +1,44 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom += [ + "AccessibleNode.h", +] + +UNIFIED_SOURCES += [ + "AccessibleNode.cpp", +] + +LOCAL_INCLUDES += [ + "/accessible/base", + "/accessible/generic", +] + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + LOCAL_INCLUDES += [ + "/accessible/atk", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": + LOCAL_INCLUDES += [ + "/accessible/windows/ia2", + "/accessible/windows/msaa", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa": + LOCAL_INCLUDES += [ + "/accessible/mac", + ] +elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + LOCAL_INCLUDES += [ + "/accessible/android", + ] +else: + LOCAL_INCLUDES += [ + "/accessible/other", + ] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp new file mode 100644 index 0000000000..98f303ee4a --- /dev/null +++ b/accessible/atk/AccessibleWrap.cpp @@ -0,0 +1,1305 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "AccessibleWrap.h" + +#include "LocalAccessible-inl.h" +#include "AccAttributes.h" +#include "ApplicationAccessibleWrap.h" +#include "InterfaceInitFuncs.h" +#include "nsAccUtils.h" +#include "mozilla/a11y/PDocAccessible.h" +#include "OuterDocAccessible.h" +#include "RemoteAccessible.h" +#include "DocAccessibleParent.h" +#include "RootAccessible.h" +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" +#include "nsMai.h" +#include "nsMaiHyperlink.h" +#include "nsString.h" +#include "nsStateMap.h" +#include "mozilla/a11y/Platform.h" +#include "Relation.h" +#include "RootAccessible.h" +#include "States.h" +#include "nsISimpleEnumerator.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Sprintf.h" +#include "nsAccessibilityService.h" +#include "nsComponentManagerUtils.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +MaiAtkObject::EAvailableAtkSignals MaiAtkObject::gAvailableAtkSignals = + eUnknown; + +// defined in ApplicationAccessibleWrap.cpp +extern "C" GType g_atk_hyperlink_impl_type; + +/* MaiAtkObject */ + +enum { + ACTIVATE, + CREATE, + DEACTIVATE, + DESTROY, + MAXIMIZE, + MINIMIZE, + RESIZE, + RESTORE, + LAST_SIGNAL +}; + +enum MaiInterfaceType { + MAI_INTERFACE_COMPONENT, /* 0 */ + MAI_INTERFACE_ACTION, + MAI_INTERFACE_VALUE, + MAI_INTERFACE_EDITABLE_TEXT, + MAI_INTERFACE_HYPERTEXT, + MAI_INTERFACE_HYPERLINK_IMPL, + MAI_INTERFACE_SELECTION, + MAI_INTERFACE_TABLE, + MAI_INTERFACE_TEXT, + MAI_INTERFACE_DOCUMENT, + MAI_INTERFACE_IMAGE, /* 10 */ + MAI_INTERFACE_TABLE_CELL +}; + +static GType GetAtkTypeForMai(MaiInterfaceType type) { + switch (type) { + case MAI_INTERFACE_COMPONENT: + return ATK_TYPE_COMPONENT; + case MAI_INTERFACE_ACTION: + return ATK_TYPE_ACTION; + case MAI_INTERFACE_VALUE: + return ATK_TYPE_VALUE; + case MAI_INTERFACE_EDITABLE_TEXT: + return ATK_TYPE_EDITABLE_TEXT; + case MAI_INTERFACE_HYPERTEXT: + return ATK_TYPE_HYPERTEXT; + case MAI_INTERFACE_HYPERLINK_IMPL: + return g_atk_hyperlink_impl_type; + case MAI_INTERFACE_SELECTION: + return ATK_TYPE_SELECTION; + case MAI_INTERFACE_TABLE: + return ATK_TYPE_TABLE; + case MAI_INTERFACE_TEXT: + return ATK_TYPE_TEXT; + case MAI_INTERFACE_DOCUMENT: + return ATK_TYPE_DOCUMENT; + case MAI_INTERFACE_IMAGE: + return ATK_TYPE_IMAGE; + case MAI_INTERFACE_TABLE_CELL: + MOZ_ASSERT(false); + } + return G_TYPE_INVALID; +} + +#define NON_USER_EVENT ":system" + +// The atk interfaces we can expose without checking what version of ATK we are +// dealing with. At the moment AtkTableCell is the only interface we can't +// always expose. +static const GInterfaceInfo atk_if_infos[] = { + {(GInterfaceInitFunc)componentInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)actionInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)valueInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr, + nullptr}, + {(GInterfaceInitFunc)editableTextInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)hypertextInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)hyperlinkImplInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)selectionInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)tableInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr, + nullptr}, + {(GInterfaceInitFunc)textInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr, + nullptr}, + {(GInterfaceInitFunc)documentInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}, + {(GInterfaceInitFunc)imageInterfaceInitCB, (GInterfaceFinalizeFunc) nullptr, + nullptr}}; + +static GQuark quark_mai_hyperlink = 0; + +AtkHyperlink* MaiAtkObject::GetAtkHyperlink() { + NS_ASSERTION(quark_mai_hyperlink, "quark_mai_hyperlink not initialized"); + MaiHyperlink* maiHyperlink = + (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink); + if (!maiHyperlink) { + maiHyperlink = new MaiHyperlink(acc); + g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, maiHyperlink); + } + + return maiHyperlink->GetAtkHyperlink(); +} + +void MaiAtkObject::Shutdown() { + acc = nullptr; + MaiHyperlink* maiHyperlink = + (MaiHyperlink*)g_object_get_qdata(G_OBJECT(this), quark_mai_hyperlink); + if (maiHyperlink) { + delete maiHyperlink; + g_object_set_qdata(G_OBJECT(this), quark_mai_hyperlink, nullptr); + } +} + +struct MaiAtkObjectClass { + AtkObjectClass parent_class; +}; + +static guint mai_atk_object_signals[LAST_SIGNAL] = { + 0, +}; + +static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName); + +G_BEGIN_DECLS +/* callbacks for MaiAtkObject */ +static void classInitCB(AtkObjectClass* aClass); +static void initializeCB(AtkObject* aAtkObj, gpointer aData); +static void finalizeCB(GObject* aObj); + +/* callbacks for AtkObject virtual functions */ +static const gchar* getNameCB(AtkObject* aAtkObj); +/* getDescriptionCB is also used by image interface */ +const gchar* getDescriptionCB(AtkObject* aAtkObj); +static AtkRole getRoleCB(AtkObject* aAtkObj); +static AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj); +static const gchar* GetLocaleCB(AtkObject*); +static AtkObject* getParentCB(AtkObject* aAtkObj); +static gint getChildCountCB(AtkObject* aAtkObj); +static AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex); +static gint getIndexInParentCB(AtkObject* aAtkObj); +static AtkStateSet* refStateSetCB(AtkObject* aAtkObj); +static AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj); + +/* the missing atkobject virtual functions */ +/* + static AtkLayer getLayerCB(AtkObject *aAtkObj); + static gint getMdiZorderCB(AtkObject *aAtkObj); + static void SetNameCB(AtkObject *aAtkObj, + const gchar *name); + static void SetDescriptionCB(AtkObject *aAtkObj, + const gchar *description); + static void SetParentCB(AtkObject *aAtkObj, + AtkObject *parent); + static void SetRoleCB(AtkObject *aAtkObj, + AtkRole role); + static guint ConnectPropertyChangeHandlerCB( + AtkObject *aObj, + AtkPropertyChangeHandler *handler); + static void RemovePropertyChangeHandlerCB( + AtkObject *aAtkObj, + guint handler_id); + static void InitializeCB(AtkObject *aAtkObj, + gpointer data); + static void ChildrenChangedCB(AtkObject *aAtkObj, + guint change_index, + gpointer changed_child); + static void FocusEventCB(AtkObject *aAtkObj, + gboolean focus_in); + static void PropertyChangeCB(AtkObject *aAtkObj, + AtkPropertyValues *values); + static void StateChangeCB(AtkObject *aAtkObj, + const gchar *name, + gboolean state_set); + static void VisibleDataChangedCB(AtkObject *aAtkObj); +*/ +G_END_DECLS + +static GType GetMaiAtkType(uint16_t interfacesBits); +static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits); + +static gpointer parent_class = nullptr; + +GType mai_atk_object_get_type(void) { + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof(MaiAtkObjectClass), + (GBaseInitFunc) nullptr, + (GBaseFinalizeFunc) nullptr, + (GClassInitFunc)classInitCB, + (GClassFinalizeFunc) nullptr, + nullptr, /* class data */ + sizeof(MaiAtkObject), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) nullptr, + nullptr /* value table */ + }; + + type = g_type_register_static(ATK_TYPE_OBJECT, "MaiAtkObject", &tinfo, + GTypeFlags(0)); + quark_mai_hyperlink = g_quark_from_static_string("MaiHyperlink"); + } + return type; +} + +AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) + : LocalAccessible(aContent, aDoc), mAtkObject(nullptr) {} + +AccessibleWrap::~AccessibleWrap() { + NS_ASSERTION(!mAtkObject, "ShutdownAtkObject() is not called"); +} + +void AccessibleWrap::ShutdownAtkObject() { + if (!mAtkObject) return; + + NS_ASSERTION(IS_MAI_OBJECT(mAtkObject), "wrong type of atk object"); + if (IS_MAI_OBJECT(mAtkObject)) MAI_ATK_OBJECT(mAtkObject)->Shutdown(); + + g_object_unref(mAtkObject); + mAtkObject = nullptr; +} + +void AccessibleWrap::Shutdown() { + ShutdownAtkObject(); + LocalAccessible::Shutdown(); +} + +void AccessibleWrap::GetNativeInterface(void** aOutAccessible) { + *aOutAccessible = nullptr; + + if (!mAtkObject) { + if (IsDefunct() || IsText()) { + // We don't create ATK objects for node which has been shutdown or + // plain text leaves + return; + } + + GType type = GetMaiAtkType(CreateMaiInterfaces()); + if (!type) return; + + mAtkObject = reinterpret_cast(g_object_new(type, nullptr)); + if (!mAtkObject) return; + + atk_object_initialize(mAtkObject, static_cast(this)); + mAtkObject->role = ATK_ROLE_INVALID; + mAtkObject->layer = ATK_LAYER_INVALID; + } + + *aOutAccessible = mAtkObject; +} + +AtkObject* AccessibleWrap::GetAtkObject(void) { + void* atkObj = nullptr; + GetNativeInterface(&atkObj); + return static_cast(atkObj); +} + +// Get AtkObject from LocalAccessible interface +/* static */ +AtkObject* AccessibleWrap::GetAtkObject(LocalAccessible* acc) { + void* atkObjPtr = nullptr; + acc->GetNativeInterface(&atkObjPtr); + return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr; +} + +/* private */ +uint16_t AccessibleWrap::CreateMaiInterfaces(void) { + uint16_t interfacesBits = 0; + + // The Component interface is supported by all accessibles. + interfacesBits |= 1 << MAI_INTERFACE_COMPONENT; + + // Add Action interface if the action count is more than zero. + if (ActionCount() > 0) interfacesBits |= 1 << MAI_INTERFACE_ACTION; + + // Text, Editabletext, and Hypertext interface. + HyperTextAccessible* hyperText = AsHyperText(); + if (hyperText && hyperText->IsTextRole()) { + interfacesBits |= 1 << MAI_INTERFACE_TEXT; + interfacesBits |= 1 << MAI_INTERFACE_EDITABLE_TEXT; + if (!nsAccUtils::MustPrune(this)) { + interfacesBits |= 1 << MAI_INTERFACE_HYPERTEXT; + } + } + + // Value interface. + if (HasNumericValue()) interfacesBits |= 1 << MAI_INTERFACE_VALUE; + + // Document interface. + if (IsDoc()) interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT; + + if (IsImage()) interfacesBits |= 1 << MAI_INTERFACE_IMAGE; + + // HyperLink interface. + if (IsLink()) interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; + + if (!nsAccUtils::MustPrune(this)) { // These interfaces require children + // Table interface. + if (AsTable()) interfacesBits |= 1 << MAI_INTERFACE_TABLE; + + if (AsTableCell()) interfacesBits |= 1 << MAI_INTERFACE_TABLE_CELL; + + // Selection interface. + if (IsSelect()) { + interfacesBits |= 1 << MAI_INTERFACE_SELECTION; + } + } + + return interfacesBits; +} + +static GType GetMaiAtkType(uint16_t interfacesBits) { + GType type; + static const GTypeInfo tinfo = { + sizeof(MaiAtkObjectClass), + (GBaseInitFunc) nullptr, + (GBaseFinalizeFunc) nullptr, + (GClassInitFunc) nullptr, + (GClassFinalizeFunc) nullptr, + nullptr, /* class data */ + sizeof(MaiAtkObject), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) nullptr, + nullptr /* value table */ + }; + + /* + * The members we use to register GTypes are GetAtkTypeForMai + * and atk_if_infos, which are constant values to each MaiInterface + * So we can reuse the registered GType when having + * the same MaiInterface types. + */ + const char* atkTypeName = GetUniqueMaiAtkTypeName(interfacesBits); + type = g_type_from_name(atkTypeName); + if (type) { + return type; + } + + /* + * gobject limits the number of types that can directly derive from any + * given object type to 4095. + */ + static uint16_t typeRegCount = 0; + if (typeRegCount++ >= 4095) { + return G_TYPE_INVALID; + } + type = g_type_register_static(MAI_TYPE_ATK_OBJECT, atkTypeName, &tinfo, + GTypeFlags(0)); + + for (uint32_t index = 0; index < ArrayLength(atk_if_infos); index++) { + if (interfacesBits & (1 << index)) { + g_type_add_interface_static(type, + GetAtkTypeForMai((MaiInterfaceType)index), + &atk_if_infos[index]); + } + } + + // Special case AtkTableCell so we can check what version of Atk we are + // dealing with. + if (IsAtkVersionAtLeast(2, 12) && + (interfacesBits & (1 << MAI_INTERFACE_TABLE_CELL))) { + const GInterfaceInfo cellInfo = { + (GInterfaceInitFunc)tableCellInterfaceInitCB, + (GInterfaceFinalizeFunc) nullptr, nullptr}; + g_type_add_interface_static(type, gAtkTableCellGetTypeFunc(), &cellInfo); + } + + return type; +} + +static const char* GetUniqueMaiAtkTypeName(uint16_t interfacesBits) { +#define MAI_ATK_TYPE_NAME_LEN (30) /* 10+sizeof(uint16_t)*8/4+1 < 30 */ + + static gchar namePrefix[] = "MaiAtkType"; /* size = 10 */ + static gchar name[MAI_ATK_TYPE_NAME_LEN + 1]; + + SprintfLiteral(name, "%s%x", namePrefix, interfacesBits); + name[MAI_ATK_TYPE_NAME_LEN] = '\0'; + + return name; +} + +bool AccessibleWrap::IsValidObject() { + // to ensure we are not shut down + return !IsDefunct(); +} + +/* static functions for ATK callbacks */ +void classInitCB(AtkObjectClass* aClass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(aClass); + + parent_class = g_type_class_peek_parent(aClass); + + aClass->get_name = getNameCB; + aClass->get_description = getDescriptionCB; + aClass->get_parent = getParentCB; + aClass->get_n_children = getChildCountCB; + aClass->ref_child = refChildCB; + aClass->get_index_in_parent = getIndexInParentCB; + aClass->get_role = getRoleCB; + aClass->get_attributes = getAttributesCB; + aClass->get_object_locale = GetLocaleCB; + aClass->ref_state_set = refStateSetCB; + aClass->ref_relation_set = refRelationSetCB; + + aClass->initialize = initializeCB; + + gobject_class->finalize = finalizeCB; + + mai_atk_object_signals[ACTIVATE] = g_signal_new( + "activate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[CREATE] = g_signal_new( + "create", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[DEACTIVATE] = g_signal_new( + "deactivate", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[DESTROY] = g_signal_new( + "destroy", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[MAXIMIZE] = g_signal_new( + "maximize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[MINIMIZE] = g_signal_new( + "minimize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[RESIZE] = g_signal_new( + "resize", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + mai_atk_object_signals[RESTORE] = g_signal_new( + "restore", MAI_TYPE_ATK_OBJECT, G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +void initializeCB(AtkObject* aAtkObj, gpointer aData) { + NS_ASSERTION((IS_MAI_OBJECT(aAtkObj)), "Invalid AtkObject"); + NS_ASSERTION(aData, "Invalid Data to init AtkObject"); + if (!aAtkObj || !aData) return; + + /* call parent init function */ + /* AtkObjectClass has not a "initialize" function now, + * maybe it has later + */ + + if (ATK_OBJECT_CLASS(parent_class)->initialize) { + ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData); + } + + /* initialize object */ + MAI_ATK_OBJECT(aAtkObj)->acc = static_cast(aData); +} + +void finalizeCB(GObject* aObj) { + if (!IS_MAI_OBJECT(aObj)) return; + NS_ASSERTION(!MAI_ATK_OBJECT(aObj)->acc, "acc NOT null"); + + // call parent finalize function + // finalize of GObjectClass will unref the accessible parent if has + if (G_OBJECT_CLASS(parent_class)->finalize) { + G_OBJECT_CLASS(parent_class)->finalize(aObj); + } +} + +const gchar* getNameCB(AtkObject* aAtkObj) { + nsAutoString name; + if (Accessible* acc = GetInternalObj(aAtkObj)) { + acc->Name(name); + } else { + return nullptr; + } + + // XXX Firing an event from here does not seem right + MaybeFireNameChange(aAtkObj, name); + + return aAtkObj->name; +} + +static void MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName) { + NS_ConvertUTF16toUTF8 newNameUTF8(aNewName); + if (aAtkObj->name && !strcmp(aAtkObj->name, newNameUTF8.get())) return; + + // Below we duplicate the functionality of atk_object_set_name(), + // but without calling atk_object_get_name(). Instead of + // atk_object_get_name() we directly access aAtkObj->name. This is because + // atk_object_get_name() would call getNameCB() which would call + // MaybeFireNameChange() (or atk_object_set_name() before this problem was + // fixed) and we would get an infinite recursion. + // See http://bugzilla.mozilla.org/733712 + + // Do not notify for initial name setting. + // See bug http://bugzilla.gnome.org/665870 + bool notify = !!aAtkObj->name; + + free(aAtkObj->name); + aAtkObj->name = strdup(newNameUTF8.get()); + + if (notify) g_object_notify(G_OBJECT(aAtkObj), "accessible-name"); +} + +const gchar* getDescriptionCB(AtkObject* aAtkObj) { + nsAutoString uniDesc; + if (Accessible* acc = GetInternalObj(aAtkObj)) { + acc->Description(uniDesc); + } else { + return nullptr; + } + + NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description); + if (!uniDesc.Equals(objDesc)) { + atk_object_set_description(aAtkObj, NS_ConvertUTF16toUTF8(uniDesc).get()); + } + + return aAtkObj->description; +} + +AtkRole getRoleCB(AtkObject* aAtkObj) { + if (aAtkObj->role != ATK_ROLE_INVALID) return aAtkObj->role; + + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return ATK_ROLE_INVALID; + } + +#ifdef DEBUG + if (AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj)) { + NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap), + "Does not support Text interface when it should"); + } +#endif + +#define ROLE(geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \ + msaaRole, ia2Role, androidClass, nameRule) \ + case roles::geckoRole: \ + aAtkObj->role = atkRole; \ + break; + + switch (acc->Role()) { +#include "RoleMap.h" + default: + MOZ_CRASH("Unknown role."); + } + +#undef ROLE + + if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1)) { + aAtkObj->role = ATK_ROLE_LIST; + } else if (aAtkObj->role == ATK_ROLE_TABLE_ROW && + !IsAtkVersionAtLeast(2, 1)) { + aAtkObj->role = ATK_ROLE_LIST_ITEM; + } else if (aAtkObj->role == ATK_ROLE_MATH && !IsAtkVersionAtLeast(2, 12)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 12)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_LANDMARK && + !IsAtkVersionAtLeast(2, 12)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_FOOTNOTE && + !IsAtkVersionAtLeast(2, 25, 2)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_STATIC && !IsAtkVersionAtLeast(2, 16)) { + aAtkObj->role = ATK_ROLE_TEXT; + } else if ((aAtkObj->role == ATK_ROLE_MATH_FRACTION || + aAtkObj->role == ATK_ROLE_MATH_ROOT) && + !IsAtkVersionAtLeast(2, 16)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_MARK && !IsAtkVersionAtLeast(2, 36)) { + aAtkObj->role = ATK_ROLE_TEXT; + } else if (aAtkObj->role == ATK_ROLE_SUGGESTION && + !IsAtkVersionAtLeast(2, 36)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 36)) { + aAtkObj->role = ATK_ROLE_SECTION; + } else if ((aAtkObj->role == ATK_ROLE_CONTENT_DELETION || + aAtkObj->role == ATK_ROLE_CONTENT_INSERTION) && + !IsAtkVersionAtLeast(2, 34)) { + aAtkObj->role = ATK_ROLE_SECTION; + } + + return aAtkObj->role; +} + +static AtkAttributeSet* ConvertToAtkAttributeSet(AccAttributes* aAttributes) { + if (!aAttributes) { + return nullptr; + } + + AtkAttributeSet* objAttributeSet = nullptr; + + for (auto iter : *aAttributes) { + nsAutoString name; + iter.NameAsString(name); + if (name.Equals(u"placeholder")) { + name.AssignLiteral(u"placeholder-text"); + } + + nsAutoString value; + iter.ValueAsString(value); + + AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); + objAttr->name = g_strdup(NS_ConvertUTF16toUTF8(name).get()); + objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); + objAttributeSet = g_slist_prepend(objAttributeSet, objAttr); + } + + // libspi will free it + return objAttributeSet; +} + +AtkAttributeSet* getAttributesCB(AtkObject* aAtkObj) { + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return nullptr; + } + RefPtr attributes = acc->Attributes(); + return ConvertToAtkAttributeSet(attributes); +} + +const gchar* GetLocaleCB(AtkObject* aAtkObj) { + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return nullptr; + } + + nsAutoString locale; + acc->Language(locale); + return AccessibleWrap::ReturnString(locale); +} + +AtkObject* getParentCB(AtkObject* aAtkObj) { + if (aAtkObj->accessible_parent) return aAtkObj->accessible_parent; + + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return nullptr; + } + + Accessible* parent = acc->Parent(); + AtkObject* atkParent = parent ? GetWrapperFor(parent) : nullptr; + if (atkParent) atk_object_set_parent(aAtkObj, atkParent); + + return aAtkObj->accessible_parent; +} + +gint getChildCountCB(AtkObject* aAtkObj) { + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc || nsAccUtils::MustPrune(acc)) { + return 0; + } + return static_cast(acc->EmbeddedChildCount()); +} + +AtkObject* refChildCB(AtkObject* aAtkObj, gint aChildIndex) { + // aChildIndex should not be less than zero + if (aChildIndex < 0) { + return nullptr; + } + + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc || nsAccUtils::MustPrune(acc)) { + return nullptr; + } + Accessible* accChild = acc->EmbeddedChildAt(aChildIndex); + if (!accChild) { + return nullptr; + } + + AtkObject* childAtkObj = GetWrapperFor(accChild); + NS_ASSERTION(childAtkObj, "Fail to get AtkObj"); + if (!childAtkObj) { + return nullptr; + } + + g_object_ref(childAtkObj); + + if (aAtkObj != childAtkObj->accessible_parent) { + atk_object_set_parent(childAtkObj, aAtkObj); + } + + return childAtkObj; +} + +gint getIndexInParentCB(AtkObject* aAtkObj) { + // We don't use LocalAccessible::IndexInParent() because we don't include text + // leaf nodes as children in ATK. + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return -1; + } + if (acc->IsDoc()) { + return 0; + } + Accessible* parent = acc->Parent(); + if (!parent) { + return -1; + } + return parent->IndexOfEmbeddedChild(acc); +} + +static void TranslateStates(uint64_t aState, roles::Role aRole, + AtkStateSet* aStateSet) { + // atk doesn't have a read only state so read only things shouldn't be + // editable. However, we don't do this for list items because Gecko always + // exposes those as read only. + if ((aState & states::READONLY) && aRole != roles::LISTITEM) { + aState &= ~states::EDITABLE; + } + + // Convert every state to an entry in AtkStateMap + uint64_t bitMask = 1; + for (auto stateIndex = 0U; stateIndex < gAtkStateMapLen; stateIndex++) { + if (gAtkStateMap[stateIndex] + .atkState) { // There's potentially an ATK state for this + bool isStateOn = (aState & bitMask) != 0; + if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) { + isStateOn = !isStateOn; + } + if (isStateOn) { + atk_state_set_add_state(aStateSet, gAtkStateMap[stateIndex].atkState); + } + } + bitMask <<= 1; + } +} + +AtkStateSet* refStateSetCB(AtkObject* aAtkObj) { + AtkStateSet* state_set = nullptr; + state_set = ATK_OBJECT_CLASS(parent_class)->ref_state_set(aAtkObj); + + if (Accessible* acc = GetInternalObj(aAtkObj)) { + TranslateStates(acc->State(), acc->Role(), state_set); + } else { + TranslateStates(states::DEFUNCT, roles::NOTHING, state_set); + } + + return state_set; +} + +static void UpdateAtkRelation(RelationType aType, Accessible* aAcc, + AtkRelationType aAtkType, + AtkRelationSet* aAtkSet) { + if (aAtkType == ATK_RELATION_NULL) return; + + AtkRelation* atkRelation = + atk_relation_set_get_relation_by_type(aAtkSet, aAtkType); + if (atkRelation) atk_relation_set_remove(aAtkSet, atkRelation); + + Relation rel(aAcc->RelationByType(aType)); + nsTArray targets; + Accessible* tempAcc = nullptr; + while ((tempAcc = rel.Next())) { + targets.AppendElement(GetWrapperFor(tempAcc)); + } + + if (targets.Length()) { + atkRelation = + atk_relation_new(targets.Elements(), targets.Length(), aAtkType); + atk_relation_set_add(aAtkSet, atkRelation); + g_object_unref(atkRelation); + } +} + +AtkRelationSet* refRelationSetCB(AtkObject* aAtkObj) { + AtkRelationSet* relation_set = + ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj); + + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return relation_set; + } + +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ + UpdateAtkRelation(RelationType::geckoType, acc, atkType, relation_set); + +#include "RelationTypeMap.h" + +#undef RELATIONTYPE + + return relation_set; +} + +// Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap +// for it. +AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) { + NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); + + // If we're working with an ATK object, we need to convert the Accessible + // back to an AccessibleWrap: + Accessible* storedAcc = MAI_ATK_OBJECT(aAtkObj)->acc; + if (!storedAcc) { + return nullptr; + } + auto* accWrap = static_cast(storedAcc->AsLocal()); + + // Check if the accessible was deconstructed. + if (!accWrap) return nullptr; + + NS_ENSURE_TRUE(accWrap->GetAtkObject() == aAtkObj, nullptr); + + AccessibleWrap* appAccWrap = ApplicationAcc(); + if (appAccWrap != accWrap && !accWrap->IsValidObject()) { + return nullptr; + } + + return accWrap; +} + +RemoteAccessible* GetProxy(AtkObject* aObj) { + Accessible* acc = GetInternalObj(aObj); + if (!acc) { + return nullptr; + } + + return acc->AsRemote(); +} + +Accessible* GetInternalObj(AtkObject* aObj) { + if (!aObj || !IS_MAI_OBJECT(aObj)) return nullptr; + + return MAI_ATK_OBJECT(aObj)->acc; +} + +AtkObject* GetWrapperFor(Accessible* aAcc) { + if (!aAcc) { + return nullptr; + } + + if (aAcc->IsRemote()) { + return reinterpret_cast(aAcc->AsRemote()->GetWrapper()); + } + + return AccessibleWrap::GetAtkObject(aAcc->AsLocal()); +} + +static uint16_t GetInterfacesForProxy(RemoteAccessible* aProxy) { + uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT; + if (aProxy->IsHyperText()) { + interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT) | + (1 << MAI_INTERFACE_EDITABLE_TEXT); + } + + if (aProxy->IsLink()) { + interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL; + } + + if (aProxy->HasNumericValue()) { + interfaces |= 1 << MAI_INTERFACE_VALUE; + } + + if (aProxy->IsTable()) { + interfaces |= 1 << MAI_INTERFACE_TABLE; + } + + if (aProxy->IsTableCell()) { + interfaces |= 1 << MAI_INTERFACE_TABLE_CELL; + } + + if (aProxy->IsImage()) { + interfaces |= 1 << MAI_INTERFACE_IMAGE; + } + + if (aProxy->IsDoc()) { + interfaces |= 1 << MAI_INTERFACE_DOCUMENT; + } + + if (aProxy->IsSelect()) { + interfaces |= 1 << MAI_INTERFACE_SELECTION; + } + + if (aProxy->IsActionable()) { + interfaces |= 1 << MAI_INTERFACE_ACTION; + } + + return interfaces; +} + +void a11y::ProxyCreated(RemoteAccessible* aProxy) { + MOZ_ASSERT(aProxy->RemoteParent() || aProxy->IsDoc(), + "Need parent to check for HyperLink interface"); + GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy)); + NS_ASSERTION(type, "why don't we have a type!"); + + AtkObject* obj = reinterpret_cast(g_object_new(type, nullptr)); + if (!obj) return; + + atk_object_initialize(obj, static_cast(aProxy)); + obj->role = ATK_ROLE_INVALID; + obj->layer = ATK_LAYER_INVALID; + aProxy->SetWrapper(reinterpret_cast(obj)); +} + +void a11y::ProxyDestroyed(RemoteAccessible* aProxy) { + auto obj = reinterpret_cast(aProxy->GetWrapper()); + if (!obj) { + return; + } + + obj->Shutdown(); + g_object_unref(obj); + aProxy->SetWrapper(0); +} + +void a11y::PlatformEvent(Accessible* aTarget, uint32_t aEventType) { + AtkObject* wrapper = GetWrapperFor(aTarget); + + switch (aEventType) { + case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE: + if (aTarget->IsDoc()) { + g_signal_emit_by_name(wrapper, "load_complete"); + } + // XXX - Handle native dialog accessibles. + if (!aTarget->IsRoot() && aTarget->HasARIARole() && + aTarget->Role() == roles::DIALOG) { + guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + } + break; + case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD: + if (aTarget->IsDoc()) { + g_signal_emit_by_name(wrapper, "reload"); + } + break; + case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED: + if (aTarget->IsDoc()) { + g_signal_emit_by_name(wrapper, "load_stopped"); + } + break; + case nsIAccessibleEvent::EVENT_MENUPOPUP_START: + atk_focus_tracker_notify(wrapper); // fire extra focus event + atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, true); + atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true); + break; + case nsIAccessibleEvent::EVENT_MENUPOPUP_END: + atk_object_notify_state_change(wrapper, ATK_STATE_VISIBLE, false); + atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, false); + break; + case nsIAccessibleEvent::EVENT_ALERT: + // A hack using state change showing events as alert events. + atk_object_notify_state_change(wrapper, ATK_STATE_SHOWING, true); + break; + case nsIAccessibleEvent::EVENT_VALUE_CHANGE: + case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE: + if (aTarget->HasNumericValue()) { + // Make sure this is a numeric value. Don't fire for string value + // changes (e.g. text editing) ATK values are always numeric. + g_object_notify((GObject*)wrapper, "accessible-value"); + } + break; + case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: + g_signal_emit_by_name(wrapper, "text_selection_changed"); + break; + case nsIAccessibleEvent::EVENT_SELECTION_WITHIN: + g_signal_emit_by_name(wrapper, "selection_changed"); + break; + case nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED: + g_signal_emit_by_name(wrapper, "text-attributes-changed"); + break; + case nsIAccessibleEvent::EVENT_NAME_CHANGE: { + nsAutoString newName; + aTarget->Name(newName); + MaybeFireNameChange(wrapper, newName); + break; + } + case nsIAccessibleEvent::EVENT_WINDOW_ACTIVATE: { + guint id = g_signal_lookup("activate", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + // Always fire a current focus event after activation. + FocusMgr()->ForceFocusEvent(); + break; + } + case nsIAccessibleEvent::EVENT_WINDOW_DEACTIVATE: { + guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + break; + } + case nsIAccessibleEvent::EVENT_WINDOW_MAXIMIZE: { + guint id = g_signal_lookup("maximize", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + break; + } + case nsIAccessibleEvent::EVENT_WINDOW_MINIMIZE: { + guint id = g_signal_lookup("minimize", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + break; + } + case nsIAccessibleEvent::EVENT_WINDOW_RESTORE: { + guint id = g_signal_lookup("restore", MAI_TYPE_ATK_OBJECT); + g_signal_emit(wrapper, id, 0); + break; + } + } +} + +void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t aState, + bool aEnabled) { + MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget)); + atkObj->FireStateChangeEvent(aState, aEnabled); +} + +void a11y::PlatformFocusEvent(Accessible* aTarget, + const LayoutDeviceIntRect& aCaretRect) { + AtkObject* wrapper = GetWrapperFor(aTarget); + + // XXX Do we really need this check? If so, do we need a similar check for + // RemoteAccessible? + if (LocalAccessible* localTarget = aTarget->AsLocal()) { + a11y::RootAccessible* rootAcc = localTarget->RootAccessible(); + if (!rootAcc || !rootAcc->IsActivated()) { + return; + } + } + + atk_focus_tracker_notify(wrapper); + atk_object_notify_state_change(wrapper, ATK_STATE_FOCUSED, true); +} + +void a11y::PlatformCaretMoveEvent(Accessible* aTarget, int32_t aOffset, + bool aIsSelectionCollapsed, + int32_t aGranularity, + const LayoutDeviceIntRect& aCaretRect, + bool aFromUser) { + AtkObject* wrapper = GetWrapperFor(aTarget); + g_signal_emit_by_name(wrapper, "text_caret_moved", aOffset); +} + +void MaiAtkObject::FireStateChangeEvent(uint64_t aState, bool aEnabled) { + auto state = aState; + int32_t stateIndex = -1; + while (state > 0) { + ++stateIndex; + state >>= 1; + } + + MOZ_ASSERT( + stateIndex >= 0 && stateIndex < static_cast(gAtkStateMapLen), + "No ATK state for internal state was found"); + if (stateIndex < 0 || stateIndex >= static_cast(gAtkStateMapLen)) { + return; + } + + if (gAtkStateMap[stateIndex].atkState != kNone) { + MOZ_ASSERT(gAtkStateMap[stateIndex].stateMapEntryType != kNoStateChange, + "State changes should not fired for this state"); + + if (gAtkStateMap[stateIndex].stateMapEntryType == kMapOpposite) { + aEnabled = !aEnabled; + } + + // Fire state change for first state if there is one to map + atk_object_notify_state_change(&parent, gAtkStateMap[stateIndex].atkState, + aEnabled); + } +} + +void a11y::PlatformTextChangeEvent(Accessible* aTarget, const nsAString& aStr, + int32_t aStart, uint32_t aLen, + bool aIsInsert, bool aFromUser) { + MaiAtkObject* atkObj = MAI_ATK_OBJECT(GetWrapperFor(aTarget)); + atkObj->FireTextChangeEvent(aStr, aStart, aLen, aIsInsert, aFromUser); +} + +#define OLD_TEXT_INSERTED "text_changed::insert" +#define OLD_TEXT_REMOVED "text_changed::delete" +static const char* oldTextChangeStrings[2][2] = { + {OLD_TEXT_REMOVED NON_USER_EVENT, OLD_TEXT_INSERTED NON_USER_EVENT}, + {OLD_TEXT_REMOVED, OLD_TEXT_INSERTED}}; + +#define TEXT_INSERTED "text-insert" +#define TEXT_REMOVED "text-remove" +#define NON_USER_DETAIL "::system" +static const char* textChangedStrings[2][2] = { + {TEXT_REMOVED NON_USER_DETAIL, TEXT_INSERTED NON_USER_DETAIL}, + {TEXT_REMOVED, TEXT_INSERTED}}; + +void MaiAtkObject::FireTextChangeEvent(const nsAString& aStr, int32_t aStart, + uint32_t aLen, bool aIsInsert, + bool aFromUser) { + if (gAvailableAtkSignals == eUnknown) { + gAvailableAtkSignals = g_signal_lookup("text-insert", G_OBJECT_TYPE(this)) + ? eHaveNewAtkTextSignals + : eNoNewAtkSignals; + } + + if (gAvailableAtkSignals == eNoNewAtkSignals) { + // XXX remove this code and the gHaveNewTextSignals check when we can + // stop supporting old atk since it doesn't really work anyway + // see bug 619002 + const char* signal_name = oldTextChangeStrings[aFromUser][aIsInsert]; + g_signal_emit_by_name(this, signal_name, aStart, aLen); + } else { + const char* signal_name = textChangedStrings[aFromUser][aIsInsert]; + g_signal_emit_by_name(this, signal_name, aStart, aLen, + NS_ConvertUTF16toUTF8(aStr).get()); + } +} + +void a11y::PlatformShowHideEvent(Accessible* aTarget, Accessible* aParent, + bool aInsert, bool aFromUser) { + AtkObject* atkObj = GetWrapperFor(aTarget); + if (!aInsert) { + // XXX - Handle native dialog accessibles. + if (!aTarget->IsRoot() && aTarget->HasARIARole() && + aTarget->Role() == roles::DIALOG) { + guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT); + g_signal_emit(atkObj, id, 0); + } + } + + MaiAtkObject* obj = MAI_ATK_OBJECT(atkObj); + obj->FireAtkShowHideEvent(GetWrapperFor(aParent), aInsert, aFromUser); +} + +#define ADD_EVENT "children_changed::add" +#define HIDE_EVENT "children_changed::remove" + +static const char* kMutationStrings[2][2] = { + {HIDE_EVENT NON_USER_EVENT, ADD_EVENT NON_USER_EVENT}, + {HIDE_EVENT, ADD_EVENT}, +}; + +void MaiAtkObject::FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, + bool aFromUser) { + if (!aParent) { + // XXX ATK needs a parent for these events. However, we might have already + // unbound from the parent by the time we fire a hide event. Ideally, we + // need to find a way to keep the parent around, but ATK clients don't seem + // to care about these missing events. + MOZ_ASSERT(!aIsAdded); + return; + } + int32_t indexInParent = getIndexInParentCB(&this->parent); + const char* signal_name = kMutationStrings[aFromUser][aIsAdded]; + g_signal_emit_by_name(aParent, signal_name, indexInParent, this, nullptr); +} + +void a11y::PlatformSelectionEvent(Accessible*, Accessible* aWidget, uint32_t) { + MaiAtkObject* obj = MAI_ATK_OBJECT(GetWrapperFor(aWidget)); + g_signal_emit_by_name(obj, "selection_changed"); +} + +// static +void AccessibleWrap::GetKeyBinding(Accessible* aAccessible, + nsAString& aResult) { + // Return all key bindings including access key and keyboard shortcut. + + // Get access key. + nsAutoString keyBindingsStr; + KeyBinding keyBinding = aAccessible->AccessKey(); + if (!keyBinding.IsEmpty()) { + keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat); + + Accessible* parent = aAccessible->Parent(); + roles::Role role = parent ? parent->Role() : roles::NOTHING; + if (role == roles::PARENT_MENUITEM || role == roles::MENUITEM || + role == roles::RADIO_MENU_ITEM || role == roles::CHECK_MENU_ITEM) { + // It is submenu, expose keyboard shortcuts from menu hierarchy like + // "s;f:s" + nsAutoString keysInHierarchyStr = keyBindingsStr; + do { + KeyBinding parentKeyBinding = parent->AccessKey(); + if (!parentKeyBinding.IsEmpty()) { + nsAutoString str; + parentKeyBinding.ToString(str, KeyBinding::eAtkFormat); + str.Append(':'); + + keysInHierarchyStr.Insert(str, 0); + } + } while ((parent = parent->Parent()) && parent->Role() != roles::MENUBAR); + + keyBindingsStr.Append(';'); + keyBindingsStr.Append(keysInHierarchyStr); + } + } else { + // No access key, add ';' to point this. + keyBindingsStr.Append(';'); + } + + // Get keyboard shortcut. + keyBindingsStr.Append(';'); + if (LocalAccessible* localAcc = aAccessible->AsLocal()) { + keyBinding = localAcc->KeyboardShortcut(); + if (!keyBinding.IsEmpty()) { + keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat); + } + } + aResult = keyBindingsStr; +} + +// static +Accessible* AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible, + int32_t aColIdx) { + if (!aAccessible) { + return nullptr; + } + + Accessible* cell = aAccessible->CellAt(0, aColIdx); + if (!cell) { + return nullptr; + } + + // If the cell at the first row is column header then assume it is column + // header for all rows, + if (cell->Role() == roles::COLUMNHEADER) { + return cell; + } + + // otherwise get column header for the data cell at the first row. + TableCellAccessible* tableCell = cell->AsTableCell(); + if (!tableCell) { + return nullptr; + } + + AutoTArray headerCells; + tableCell->ColHeaderCells(&headerCells); + if (headerCells.IsEmpty()) { + return nullptr; + } + + return headerCells[0]; +} + +// static +Accessible* AccessibleWrap::GetRowHeader(TableAccessible* aAccessible, + int32_t aRowIdx) { + if (!aAccessible) { + return nullptr; + } + + Accessible* cell = aAccessible->CellAt(aRowIdx, 0); + if (!cell) { + return nullptr; + } + + // If the cell at the first column is row header then assume it is row + // header for all columns, + if (cell->Role() == roles::ROWHEADER) { + return cell; + } + + // otherwise get row header for the data cell at the first column. + TableCellAccessible* tableCell = cell->AsTableCell(); + if (!tableCell) { + return nullptr; + } + + AutoTArray headerCells; + tableCell->RowHeaderCells(&headerCells); + if (headerCells.IsEmpty()) { + return nullptr; + } + + return headerCells[0]; +} diff --git a/accessible/atk/AccessibleWrap.h b/accessible/atk/AccessibleWrap.h new file mode 100644 index 0000000000..e12189405d --- /dev/null +++ b/accessible/atk/AccessibleWrap.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_ACCESSIBLE_WRAP_H__ +#define __NS_ACCESSIBLE_WRAP_H__ + +#include "nsCOMPtr.h" +#include "LocalAccessible.h" + +struct _AtkObject; +typedef struct _AtkObject AtkObject; + +enum AtkProperty { + PROP_0, // gobject convention + PROP_NAME, + PROP_DESCRIPTION, + PROP_PARENT, // ancestry has changed + PROP_ROLE, + PROP_LAYER, + PROP_MDI_ZORDER, + PROP_TABLE_CAPTION, + PROP_TABLE_COLUMN_DESCRIPTION, + PROP_TABLE_COLUMN_HEADER, + PROP_TABLE_ROW_DESCRIPTION, + PROP_TABLE_ROW_HEADER, + PROP_TABLE_SUMMARY, + PROP_LAST // gobject convention +}; + +struct AtkPropertyChange { + int32_t type; // property type as listed above + void* oldvalue; + void* newvalue; +}; + +namespace mozilla { +namespace a11y { + +class MaiHyperlink; + +/** + * Atk specific functionality for an accessibility tree node that originated in + * mDoc's content process. + * + * AccessibleWrap, and its descendents in atk directory provide the + * implementation of AtkObject. + */ +class AccessibleWrap : public LocalAccessible { + public: + AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc); + virtual ~AccessibleWrap(); + void ShutdownAtkObject(); + + virtual void Shutdown() override; + + // return the atk object for this AccessibleWrap + virtual void GetNativeInterface(void** aOutAccessible) override; + + AtkObject* GetAtkObject(void); + static AtkObject* GetAtkObject(LocalAccessible* aAccessible); + + bool IsValidObject(); + + static const char* ReturnString(nsAString& aString) { + static nsCString returnedString; + CopyUTF16toUTF8(aString, returnedString); + return returnedString.get(); + } + + static void GetKeyBinding(Accessible* aAccessible, nsAString& aResult); + + static Accessible* GetColumnHeader(TableAccessible* aAccessible, + int32_t aColIdx); + static Accessible* GetRowHeader(TableAccessible* aAccessible, + int32_t aRowIdx); + + protected: + nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject* aObject); + nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject); + + AtkObject* mAtkObject; + + private: + uint16_t CreateMaiInterfaces(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif /* __NS_ACCESSIBLE_WRAP_H__ */ diff --git a/accessible/atk/ApplicationAccessibleWrap.cpp b/accessible/atk/ApplicationAccessibleWrap.cpp new file mode 100644 index 0000000000..4c3e615387 --- /dev/null +++ b/accessible/atk/ApplicationAccessibleWrap.cpp @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ApplicationAccessibleWrap.h" + +#include "nsMai.h" +#include "nsAccessibilityService.h" + +#include +#include "atk/atkobject.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +// ApplicationAccessibleWrap + +ApplicationAccessibleWrap::ApplicationAccessibleWrap() = default; + +ApplicationAccessibleWrap::~ApplicationAccessibleWrap() { + AccessibleWrap::ShutdownAtkObject(); +} + +gboolean toplevel_event_watcher(GSignalInvocationHint* ihint, + guint n_param_values, + const GValue* param_values, gpointer data) { + static GQuark sQuark_gecko_acc_obj = 0; + + if (!sQuark_gecko_acc_obj) { + sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj"); + } + + if (nsAccessibilityService::IsShutdown()) return TRUE; + + GObject* object = + reinterpret_cast(g_value_get_object(param_values)); + if (!GTK_IS_WINDOW(object)) return TRUE; + + AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object)); + AtkRole role = atk_object_get_role(child); + + // GTK native dialog + if (!IS_MAI_OBJECT(child) && + (role == ATK_ROLE_DIALOG || role == ATK_ROLE_FILE_CHOOSER || + role == ATK_ROLE_COLOR_CHOOSER || role == ATK_ROLE_FONT_CHOOSER)) { + if (data == reinterpret_cast(nsIAccessibleEvent::EVENT_SHOW)) { + // Attach the dialog accessible to app accessible tree + LocalAccessible* windowAcc = + GetAccService()->AddNativeRootAccessible(child); + g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, + reinterpret_cast(windowAcc)); + + } else { + // Deattach the dialog accessible + LocalAccessible* windowAcc = reinterpret_cast( + g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj)); + if (windowAcc) { + GetAccService()->RemoveNativeRootAccessible(windowAcc); + g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, nullptr); + } + } + } + + return TRUE; +} + +ENameValueFlag ApplicationAccessibleWrap::Name(nsString& aName) const { + // ATK doesn't provide a way to obtain an application name (for example, + // Firefox or Thunderbird) like IA2 does. Thus let's return an application + // name as accessible name that was used to get a branding name (for example, + // Minefield aka nightly Firefox or Daily aka nightly Thunderbird). + AppName(aName); + return eNameOK; +} + +void ApplicationAccessibleWrap::GetNativeInterface(void** aOutAccessible) { + *aOutAccessible = nullptr; + + if (!mAtkObject) { + mAtkObject = reinterpret_cast( + g_object_new(MAI_TYPE_ATK_OBJECT, nullptr)); + if (!mAtkObject) return; + + atk_object_initialize(mAtkObject, static_cast(this)); + mAtkObject->role = ATK_ROLE_INVALID; + mAtkObject->layer = ATK_LAYER_INVALID; + } + + *aOutAccessible = mAtkObject; +} + +struct AtkRootAccessibleAddedEvent { + AtkObject* app_accessible; + AtkObject* root_accessible; + uint32_t index; +}; + +gboolean fireRootAccessibleAddedCB(gpointer data) { + AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data; + g_signal_emit_by_name(eventData->app_accessible, "children_changed::add", + eventData->index, eventData->root_accessible, nullptr); + g_object_unref(eventData->app_accessible); + g_object_unref(eventData->root_accessible); + free(data); + + return FALSE; +} + +bool ApplicationAccessibleWrap::InsertChildAt(uint32_t aIdx, + LocalAccessible* aChild) { + if (!ApplicationAccessible::InsertChildAt(aIdx, aChild)) return false; + + AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild); + atk_object_set_parent(atkAccessible, mAtkObject); + + uint32_t count = mChildren.Length(); + + // Emit children_changed::add in a timeout + // to make sure aRootAccWrap is fully initialized. + AtkRootAccessibleAddedEvent* eventData = + (AtkRootAccessibleAddedEvent*)malloc(sizeof(AtkRootAccessibleAddedEvent)); + if (eventData) { + eventData->app_accessible = mAtkObject; + eventData->root_accessible = atkAccessible; + eventData->index = count - 1; + g_object_ref(mAtkObject); + g_object_ref(atkAccessible); + g_timeout_add(0, fireRootAccessibleAddedCB, eventData); + } + + return true; +} + +bool ApplicationAccessibleWrap::RemoveChild(LocalAccessible* aChild) { + int32_t index = aChild->IndexInParent(); + + AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild); + atk_object_set_parent(atkAccessible, nullptr); + g_signal_emit_by_name(mAtkObject, "children_changed::remove", index, + atkAccessible, nullptr); + + return ApplicationAccessible::RemoveChild(aChild); +} diff --git a/accessible/atk/ApplicationAccessibleWrap.h b/accessible/atk/ApplicationAccessibleWrap.h new file mode 100644 index 0000000000..fe14dc045a --- /dev/null +++ b/accessible/atk/ApplicationAccessibleWrap.h @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__ +#define mozilla_a11y_ApplicationAccessibleWrap_h__ + +#include "ApplicationAccessible.h" + +namespace mozilla { +namespace a11y { + +class ApplicationAccessibleWrap : public ApplicationAccessible { + public: + ApplicationAccessibleWrap(); + virtual ~ApplicationAccessibleWrap(); + + // LocalAccessible + virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override; + virtual bool InsertChildAt(uint32_t aIdx, LocalAccessible* aChild) override; + virtual bool RemoveChild(LocalAccessible* aChild) override; + + /** + * Return the atk object for app root accessible. + */ + virtual void GetNativeInterface(void** aOutAccessible) override; +}; + +} // namespace a11y +} // namespace mozilla + +#endif /* __NS_APP_ROOT_ACCESSIBLE_H__ */ diff --git a/accessible/atk/DOMtoATK.cpp b/accessible/atk/DOMtoATK.cpp new file mode 100644 index 0000000000..2c23731bba --- /dev/null +++ b/accessible/atk/DOMtoATK.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMtoATK.h" +#include "nsUTF8Utils.h" + +namespace mozilla { +namespace a11y { + +namespace DOMtoATK { + +void AddBOMs(nsACString& aDest, const nsACString& aSource) { + uint32_t destlength = 0; + + // First compute how much room we will need. + for (uint32_t srci = 0; srci < aSource.Length();) { + int bytes = UTF8traits::bytes(aSource[srci]); + if (bytes >= 4) { + // Non-BMP character, will add a BOM after it. + destlength += 3; + } + // Skip whole character encoding. + srci += bytes; + destlength += bytes; + } + + uint32_t desti = 0; // Index within aDest. + + // Add BOMs after non-BMP characters. + aDest.SetLength(destlength); + for (uint32_t srci = 0; srci < aSource.Length();) { + uint32_t bytes = UTF8traits::bytes(aSource[srci]); + + MOZ_ASSERT(bytes <= aSource.Length() - srci, + "We should have the whole sequence"); + + // Copy whole sequence. + aDest.Replace(desti, bytes, Substring(aSource, srci, bytes)); + desti += bytes; + srci += bytes; + + if (bytes >= 4) { + // More than 4 bytes in UTF-8 encoding exactly means more than 16 encoded + // bits. This is thus a non-BMP character which needed a surrogate + // pair to get encoded in UTF-16, add a BOM after it. + + // And add a BOM after it. + aDest.Replace(desti, 3, "\xEF\xBB\xBF"); + desti += 3; + } + } + MOZ_ASSERT(desti == destlength, + "Incoherency between computed length" + "and actually translated length"); +} + +void ATKStringConverterHelper::AdjustOffsets(gint* aStartOffset, + gint* aEndOffset, gint count) { + MOZ_ASSERT(!mAdjusted, + "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be " + "called only once"); + + if (*aStartOffset > 0) { + (*aStartOffset)--; + mStartShifted = true; + } + + if (*aEndOffset >= 0 && *aEndOffset < count) { + (*aEndOffset)++; + mEndShifted = true; + } + +#ifdef DEBUG + mAdjusted = true; +#endif +} + +gchar* ATKStringConverterHelper::FinishUTF16toUTF8(nsCString& aStr) { + int skip = 0; + + if (mStartShifted) { + // AdjustOffsets added a leading character. + + MOZ_ASSERT(aStr.Length() > 0, "There should be a leading character"); + MOZ_ASSERT( + static_cast(aStr.Length()) >= UTF8traits::bytes(aStr.CharAt(0)), + "The leading character should be complete"); + + // drop first character + skip = UTF8traits::bytes(aStr.CharAt(0)); + } + + if (mEndShifted) { + // AdjustOffsets added a trailing character. + + MOZ_ASSERT(aStr.Length() > 0, "There should be a trailing character"); + + int trail = -1; + // Find beginning of last character. + for (trail = aStr.Length() - 1; trail >= 0; trail--) { + if (!UTF8traits::isInSeq(aStr.CharAt(trail))) { + break; + } + } + MOZ_ASSERT(trail >= 0, + "There should be at least a whole trailing character"); + MOZ_ASSERT(trail + UTF8traits::bytes(aStr.CharAt(trail)) == + static_cast(aStr.Length()), + "The trailing character should be complete"); + + // Drop the last character. + aStr.Truncate(trail); + } + + // copy and return, libspi will free it + return g_strdup(aStr.get() + skip); +} + +gchar* ATKStringConverterHelper::ConvertAdjusted(const nsAString& aStr) { + MOZ_ASSERT(mAdjusted, + "DOMtoATK::ATKStringConverterHelper::AdjustOffsets needs to be " + "called before ATKStringConverterHelper::ConvertAdjusted"); + + NS_ConvertUTF16toUTF8 cautoStr(aStr); + if (!cautoStr.get()) { + return nullptr; + } + + nsAutoCString cautoStrBOMs; + AddBOMs(cautoStrBOMs, cautoStr); + return FinishUTF16toUTF8(cautoStrBOMs); +} + +gchar* Convert(const nsAString& aStr) { + NS_ConvertUTF16toUTF8 cautoStr(aStr); + if (!cautoStr.get()) { + return nullptr; + } + + nsAutoCString cautoStrBOMs; + AddBOMs(cautoStrBOMs, cautoStr); + return g_strdup(cautoStrBOMs.get()); +} + +} // namespace DOMtoATK + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/atk/DOMtoATK.h b/accessible/atk/DOMtoATK.h new file mode 100644 index 0000000000..322358bc6e --- /dev/null +++ b/accessible/atk/DOMtoATK.h @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include "mozilla/a11y/HyperTextAccessibleBase.h" +#include "nsCharTraits.h" +#include "nsString.h" + +/** + * ATK offsets are counted in unicode codepoints, while DOM offsets are counted + * in UTF-16 code units. That makes a difference for non-BMP characters, + * which need two UTF-16 code units to be represented (a pair of surrogates), + * while they are just one unicode character. + * + * To keep synchronization between ATK offsets (unicode codepoints) and DOM + * offsets (UTF-16 code units), after translation from UTF-16 to UTF-8 we add a + * BOM after each non-BMP character (which would otherwise use 2 UTF-16 + * code units for only 1 unicode codepoint). + * + * BOMs (Byte Order Marks, U+FEFF, also known as ZERO WIDTH NO-BREAK SPACE, but + * that usage is deprecated) normally only appear at the beginning of unicode + * files, but their occurrence within text (notably after cut&paste) is not + * uncommon, and are thus considered as non-text. + * + * Since the selection requested through ATK may not contain both surrogates + * at the ends of the selection, we need to fetch one UTF-16 code point more + * on both side, and get rid of it before returning the string to ATK. The + * ATKStringConverterHelper class maintains this, NewATKString should be used + * to call it properly. + * + * In the end, + * - if the start is between the high and low surrogates, the UTF-8 result + * includes a BOM from it but not the character + * - if the end is between the high and low surrogates, the UTF-8 result + * includes the character but *not* the BOM + * - all non-BMP characters that are fully in the string are in the UTF-8 result + * as character followed by BOM + */ +namespace mozilla { +namespace a11y { + +namespace DOMtoATK { + +/** + * Converts a string of accessible text into ATK gchar* string (by adding + * BOMs). This can be used when offsets do not need to be adjusted because + * ends of the string can not fall between surrogates. + */ +gchar* Convert(const nsAString& aStr); + +/** + * Add a BOM after each non-BMP character. + */ +void AddBOMs(nsACString& aDest, const nsACString& aSource); + +class ATKStringConverterHelper { + public: + ATKStringConverterHelper(void) + : +#ifdef DEBUG + mAdjusted(false), +#endif + mStartShifted(false), + mEndShifted(false) { + } + + /** + * In order to properly get non-BMP values, offsets need to be changed + * to get one character more on each end, so that ConvertUTF16toUTF8 can + * convert surrogates even if the originally requested offsets fall between + * them. + */ + void AdjustOffsets(gint* aStartOffset, gint* aEndOffset, gint count); + + /** + * Converts a string of accessible text with adjusted offsets into ATK + * gchar* string (by adding BOMs). Note, AdjustOffsets has to be called + * before getting the text passed to this. + */ + gchar* ConvertAdjusted(const nsAString& aStr); + + private: + /** + * Remove the additional characters requested by PrepareUTF16toUTF8. + */ + gchar* FinishUTF16toUTF8(nsCString& aStr); + +#ifdef DEBUG + bool mAdjusted; +#endif + bool mStartShifted; + bool mEndShifted; +}; + +/** + * Get text from aAccessible, using ATKStringConverterHelper to properly + * introduce appropriate BOMs. + */ +inline gchar* NewATKString(HyperTextAccessibleBase* aAccessible, + gint aStartOffset, gint aEndOffset) { + gint startOffset = aStartOffset, endOffset = aEndOffset; + ATKStringConverterHelper converter; + converter.AdjustOffsets(&startOffset, &endOffset, + gint(aAccessible->CharacterCount())); + nsAutoString str; + aAccessible->TextSubstring(startOffset, endOffset, str); + + if (str.Length() == 0) { + // Bogus offsets, or empty string, either way we do not need conversion. + return g_strdup(""); + } + + return converter.ConvertAdjusted(str); +} + +/** + * Get a character from aAccessible, fetching more data as appropriate to + * properly get non-BMP characters or a BOM as appropriate. + */ +inline gunichar ATKCharacter(HyperTextAccessibleBase* aAccessible, + gint aOffset) { + // char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib. + gunichar character = static_cast(aAccessible->CharAt(aOffset)); + + if (NS_IS_LOW_SURROGATE(character)) { + // Trailing surrogate, return BOM instead. + return 0xFEFF; + } + + if (NS_IS_HIGH_SURROGATE(character)) { + // Heading surrogate, get the trailing surrogate and combine them. + gunichar characterLow = + static_cast(aAccessible->CharAt(aOffset + 1)); + + if (!NS_IS_LOW_SURROGATE(characterLow)) { + // It should have been a trailing surrogate... Flag the error. + return 0xFFFD; + } + return SURROGATE_TO_UCS4(character, characterLow); + } + + return character; +} + +} // namespace DOMtoATK + +} // namespace a11y +} // namespace mozilla diff --git a/accessible/atk/DocAccessibleWrap.cpp b/accessible/atk/DocAccessibleWrap.cpp new file mode 100644 index 0000000000..f3dfba71ac --- /dev/null +++ b/accessible/atk/DocAccessibleWrap.cpp @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DocAccessibleWrap.h" +#include "mozilla/PresShell.h" +#include "nsIWidgetListener.h" +#include "nsTArray.h" +#include "nsWindow.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// DocAccessibleWrap +//////////////////////////////////////////////////////////////////////////////// + +DocAccessibleWrap::DocAccessibleWrap(dom::Document* aDocument, + PresShell* aPresShell) + : DocAccessible(aDocument, aPresShell) {} + +DocAccessibleWrap::~DocAccessibleWrap() {} + +bool DocAccessibleWrap::IsActivated() { + if (nsWindow* window = nsWindow::GetFocusedWindow()) { + if (nsIWidgetListener* listener = window->GetWidgetListener()) { + if (PresShell* presShell = listener->GetPresShell()) { + return presShell == PresShellPtr(); + } + } + } + + return false; +} diff --git a/accessible/atk/DocAccessibleWrap.h b/accessible/atk/DocAccessibleWrap.h new file mode 100644 index 0000000000..883a4b8f0a --- /dev/null +++ b/accessible/atk/DocAccessibleWrap.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For documentation of the accessibility architecture, + * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html + */ + +#ifndef mozilla_a11y_DocAccessibleWrap_h__ +#define mozilla_a11y_DocAccessibleWrap_h__ + +#include "DocAccessible.h" + +namespace mozilla { + +class PresShell; + +namespace a11y { + +class DocAccessibleWrap : public DocAccessible { + public: + DocAccessibleWrap(dom::Document* aDocument, PresShell* aPresShell); + virtual ~DocAccessibleWrap(); + + bool IsActivated(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/atk/InterfaceInitFuncs.h b/accessible/atk/InterfaceInitFuncs.h new file mode 100644 index 0000000000..43ed8ff4ee --- /dev/null +++ b/accessible/atk/InterfaceInitFuncs.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ATK_INTERFACE_INIT_FUNCS_H_ +#define ATK_INTERFACE_INIT_FUNCS_H_ + +#include + +namespace mozilla { +namespace a11y { + +class AccessibleWrap; + +} // namespace a11y +} // namespace mozilla + +extern "C" { +void actionInterfaceInitCB(AtkActionIface* aIface); +void componentInterfaceInitCB(AtkComponentIface* aIface); +void documentInterfaceInitCB(AtkDocumentIface* aIface); +void editableTextInterfaceInitCB(AtkEditableTextIface* aIface); +void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface* aIface); +void hypertextInterfaceInitCB(AtkHypertextIface* aIface); +void imageInterfaceInitCB(AtkImageIface* aIface); +void selectionInterfaceInitCB(AtkSelectionIface* aIface); +void tableInterfaceInitCB(AtkTableIface* aIface); +void tableCellInterfaceInitCB(AtkTableCellIface* aIface); +void textInterfaceInitCB(AtkTextIface* aIface); +void valueInterfaceInitCB(AtkValueIface* aIface); +} + +/** + * XXX these should live in a file of utils for atk. + */ +AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY, + AtkCoordType aCoordType); +void getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth, + gint* aHeight, AtkCoordType aCoordType); + +#endif // ATK_INTERFACE_INIT_FUNCS_H_ diff --git a/accessible/atk/Platform.cpp b/accessible/atk/Platform.cpp new file mode 100644 index 0000000000..e166fcfc32 --- /dev/null +++ b/accessible/atk/Platform.cpp @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Platform.h" + +#include "nsIAccessibleEvent.h" +#include "nsIGSettingsService.h" +#include "nsMai.h" +#include "nsServiceManagerUtils.h" +#include "prenv.h" +#include "prlink.h" + +#ifdef MOZ_ENABLE_DBUS +# include +#endif +#include + +using namespace mozilla; +using namespace mozilla::a11y; + +int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0; + +GType (*gAtkTableCellGetTypeFunc)(); + +extern "C" { +typedef GType (*AtkGetTypeType)(void); +typedef void (*AtkBridgeAdaptorInit)(int*, char**[]); +} + +static PRLibrary* sATKLib = nullptr; +static const char sATKLibName[] = "libatk-1.0.so.0"; +static const char sATKHyperlinkImplGetTypeSymbol[] = + "atk_hyperlink_impl_get_type"; + +gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*, + gpointer); +static bool sToplevel_event_hook_added = false; +static gulong sToplevel_show_hook = 0; +static gulong sToplevel_hide_hook = 0; + +GType g_atk_hyperlink_impl_type = G_TYPE_INVALID; + +struct AtkBridgeModule { + const char* libName; + PRLibrary* lib; + const char* initName; + AtkBridgeAdaptorInit init; +}; + +static AtkBridgeModule sAtkBridge = {"libatk-bridge-2.0.so.0", nullptr, + "atk_bridge_adaptor_init", nullptr}; + +static nsresult LoadGtkModule(AtkBridgeModule& aModule) { + NS_ENSURE_ARG(aModule.libName); + + if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) { + return NS_ERROR_FAILURE; + } + + // we have loaded the library, try to get the function ptrs + if (!(aModule.init = (AtkBridgeAdaptorInit)PR_FindFunctionSymbol( + aModule.lib, aModule.initName))) { + // fail, :( + PR_UnloadLibrary(aModule.lib); + aModule.lib = nullptr; + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +void a11y::PlatformInit() { + if (!ShouldA11yBeEnabled()) return; + + sATKLib = PR_LoadLibrary(sATKLibName); + if (!sATKLib) return; + + AtkGetTypeType pfn_atk_hyperlink_impl_get_type = + (AtkGetTypeType)PR_FindFunctionSymbol(sATKLib, + sATKHyperlinkImplGetTypeSymbol); + if (pfn_atk_hyperlink_impl_get_type) { + g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type(); + } + + gAtkTableCellGetTypeFunc = + (GType(*)())PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type"); + + const char* (*atkGetVersion)() = + (const char* (*)())PR_FindFunctionSymbol(sATKLib, "atk_get_version"); + if (atkGetVersion) { + const char* version = atkGetVersion(); + if (version) { + char* endPtr = nullptr; + atkMajorVersion = strtol(version, &endPtr, 10); + if (atkMajorVersion != 0L) { + atkMinorVersion = strtol(endPtr + 1, &endPtr, 10); + if (atkMinorVersion != 0L) { + atkMicroVersion = strtol(endPtr + 1, &endPtr, 10); + } + } + } + } + + // Initialize the MAI Utility class, it will overwrite gail_util. + g_type_class_unref(g_type_class_ref(mai_util_get_type())); + + // Init atk-bridge now + PR_SetEnv("NO_AT_BRIDGE=0"); + nsresult rv = LoadGtkModule(sAtkBridge); + if (NS_SUCCEEDED(rv)) { + (*sAtkBridge.init)(nullptr, nullptr); + } + + if (!sToplevel_event_hook_added) { + sToplevel_event_hook_added = true; + sToplevel_show_hook = g_signal_add_emission_hook( + g_signal_lookup("show", GTK_TYPE_WINDOW), 0, toplevel_event_watcher, + reinterpret_cast(nsIAccessibleEvent::EVENT_SHOW), nullptr); + sToplevel_hide_hook = g_signal_add_emission_hook( + g_signal_lookup("hide", GTK_TYPE_WINDOW), 0, toplevel_event_watcher, + reinterpret_cast(nsIAccessibleEvent::EVENT_HIDE), nullptr); + } +} + +void a11y::PlatformShutdown() { + if (sToplevel_event_hook_added) { + sToplevel_event_hook_added = false; + g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW), + sToplevel_show_hook); + g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), + sToplevel_hide_hook); + } + + if (sAtkBridge.lib) { + // Do not shutdown/unload atk-bridge, + // an exit function registered will take care of it + // PR_UnloadLibrary(sAtkBridge.lib); + sAtkBridge.lib = nullptr; + sAtkBridge.init = nullptr; + } + // if (sATKLib) { + // PR_UnloadLibrary(sATKLib); + // sATKLib = nullptr; + // } +} + +static const char sAccEnv[] = "GNOME_ACCESSIBILITY"; +#ifdef MOZ_ENABLE_DBUS +static DBusPendingCall* sPendingCall = nullptr; +#endif + +void a11y::PreInit() { +#ifdef MOZ_ENABLE_DBUS + static bool sChecked = FALSE; + if (sChecked) return; + + sChecked = TRUE; + + // dbus is only checked if GNOME_ACCESSIBILITY is unset + // also make sure that a session bus address is available to prevent dbus from + // starting a new one. Dbus confuses the test harness when it creates a new + // process (see bug 693343) + if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) return; + + DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr); + if (!bus) return; + + dbus_connection_set_exit_on_disconnect(bus, FALSE); + + static const char* iface = "org.a11y.Status"; + static const char* member = "IsEnabled"; + DBusMessage* message; + message = + dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus", + "org.freedesktop.DBus.Properties", "Get"); + if (!message) goto dbus_done; + + dbus_message_append_args(message, DBUS_TYPE_STRING, &iface, DBUS_TYPE_STRING, + &member, DBUS_TYPE_INVALID); + dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000); + dbus_message_unref(message); + +dbus_done: + dbus_connection_unref(bus); +#endif +} + +bool a11y::ShouldA11yBeEnabled() { + static bool sChecked = false, sShouldEnable = false; + if (sChecked) return sShouldEnable; + + sChecked = true; + + EPlatformDisabledState disabledState = PlatformDisabledState(); + if (disabledState == ePlatformIsDisabled) { + return sShouldEnable = false; + } + if (disabledState == ePlatformIsForceEnabled) { + return sShouldEnable = true; + } + + // check if accessibility enabled/disabled by environment variable + const char* envValue = PR_GetEnv(sAccEnv); + if (envValue) return sShouldEnable = !!atoi(envValue); + +#ifdef MOZ_ENABLE_DBUS + PreInit(); + bool dbusSuccess = false; + DBusMessage* reply = nullptr; + if (!sPendingCall) goto dbus_done; + + dbus_pending_call_block(sPendingCall); + reply = dbus_pending_call_steal_reply(sPendingCall); + dbus_pending_call_unref(sPendingCall); + sPendingCall = nullptr; + if (!reply || + dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN || + strcmp(dbus_message_get_signature(reply), DBUS_TYPE_VARIANT_AS_STRING)) { + goto dbus_done; + } + + DBusMessageIter iter, iter_variant, iter_struct; + dbus_bool_t dResult; + dbus_message_iter_init(reply, &iter); + dbus_message_iter_recurse(&iter, &iter_variant); + switch (dbus_message_iter_get_arg_type(&iter_variant)) { + case DBUS_TYPE_STRUCT: + // at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct + dbus_message_iter_recurse(&iter_variant, &iter_struct); + if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) { + dbus_message_iter_get_basic(&iter_struct, &dResult); + sShouldEnable = dResult; + dbusSuccess = true; + } + + break; + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(&iter_variant, &dResult); + sShouldEnable = dResult; + dbusSuccess = true; + break; + default: + break; + } + +dbus_done: + if (reply) dbus_message_unref(reply); + + if (dbusSuccess) return sShouldEnable; +#endif + +// check GSettings +#define GSETINGS_A11Y_INTERFACE "org.gnome.desktop.interface" +#define GSETINGS_A11Y_KEY "toolkit-accessibility" + nsCOMPtr gsettings = + do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + nsCOMPtr a11y_settings; + + if (gsettings) { + gsettings->GetCollectionForSchema(nsLiteralCString(GSETINGS_A11Y_INTERFACE), + getter_AddRefs(a11y_settings)); + if (a11y_settings) { + a11y_settings->GetBoolean(nsLiteralCString(GSETINGS_A11Y_KEY), + &sShouldEnable); + } + } + + return sShouldEnable; +} diff --git a/accessible/atk/RootAccessibleWrap.cpp b/accessible/atk/RootAccessibleWrap.cpp new file mode 100644 index 0000000000..41916a0055 --- /dev/null +++ b/accessible/atk/RootAccessibleWrap.cpp @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RootAccessibleWrap.h" + +#include "atk/atkobject.h" +#include "nsTArray.h" + +#include + +using namespace mozilla::a11y; + +GtkWindowAccessible::GtkWindowAccessible(AtkObject* aAccessible) { + g_object_ref(aAccessible); + mAtkObject = aAccessible; +} + +GtkWindowAccessible::~GtkWindowAccessible() { + g_object_unref(mAtkObject); + mAtkObject = nullptr; +} diff --git a/accessible/atk/RootAccessibleWrap.h b/accessible/atk/RootAccessibleWrap.h new file mode 100644 index 0000000000..75038f698d --- /dev/null +++ b/accessible/atk/RootAccessibleWrap.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_a11y_RootAccessibleWrap_h__ +#define mozilla_a11y_RootAccessibleWrap_h__ + +#include "BaseAccessibles.h" +#include "RootAccessible.h" + +namespace mozilla { +namespace a11y { + +typedef RootAccessible RootAccessibleWrap; + +/* GtkWindowAccessible is the accessible class for gtk+ native window. + * The instance of GtkWindowAccessible is a child of MaiAppRoot instance. + * It is added into root when the toplevel window is created, and removed + * from root when the toplevel window is destroyed. + */ +class GtkWindowAccessible final : public DummyAccessible { + public: + explicit GtkWindowAccessible(AtkObject* aAccessible); + virtual ~GtkWindowAccessible(); +}; + +} // namespace a11y +} // namespace mozilla + +#endif /* mozilla_a11y_Root_Accessible_Wrap_h__ */ diff --git a/accessible/atk/UtilInterface.cpp b/accessible/atk/UtilInterface.cpp new file mode 100644 index 0000000000..8389e09f80 --- /dev/null +++ b/accessible/atk/UtilInterface.cpp @@ -0,0 +1,347 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ApplicationAccessible.h" +#include "mozilla/Likely.h" +#include "nsAccessibilityService.h" +#include "nsMai.h" + +#include +#include +#include +#include + +using namespace mozilla; +using namespace mozilla::a11y; + +typedef AtkUtil MaiUtil; +typedef AtkUtilClass MaiUtilClass; + +#define MAI_VERSION MOZILLA_VERSION +#define MAI_NAME "Gecko" + +extern "C" { +static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener, + const gchar* event_type); +static void (*gail_remove_global_event_listener)(guint remove_listener); +static void (*gail_remove_key_event_listener)(guint remove_listener); +static AtkObject* (*gail_get_root)(); +} + +struct MaiUtilListenerInfo { + gint key; + guint signal_id; + gulong hook_id; + // For window create/destory/minimize/maximize/restore/activate/deactivate + // events, we'll chain gail_util's add/remove_global_event_listener. + // So we store the listenerid returned by gail's add_global_event_listener + // in this structure to call gail's remove_global_event_listener later. + guint gail_listenerid; +}; + +static GHashTable* sListener_list = nullptr; +static gint sListener_idx = 1; + +extern "C" { +static guint add_listener(GSignalEmissionHook listener, + const gchar* object_type, const gchar* signal, + const gchar* hook_data, guint gail_listenerid = 0) { + GType type; + guint signal_id; + gint rc = 0; + + type = g_type_from_name(object_type); + if (type) { + signal_id = g_signal_lookup(signal, type); + if (signal_id > 0) { + MaiUtilListenerInfo* listener_info; + + rc = sListener_idx; + + listener_info = + (MaiUtilListenerInfo*)g_malloc(sizeof(MaiUtilListenerInfo)); + listener_info->key = sListener_idx; + listener_info->hook_id = g_signal_add_emission_hook( + signal_id, 0, listener, g_strdup(hook_data), (GDestroyNotify)g_free); + listener_info->signal_id = signal_id; + listener_info->gail_listenerid = gail_listenerid; + + g_hash_table_insert(sListener_list, &(listener_info->key), listener_info); + sListener_idx++; + } else { + g_warning("Invalid signal type %s\n", signal); + } + } else { + g_warning("Invalid object type %s\n", object_type); + } + return rc; +} + +static guint mai_util_add_global_event_listener(GSignalEmissionHook listener, + const gchar* event_type) { + guint rc = 0; + gchar** split_string; + + split_string = g_strsplit(event_type, ":", 3); + + if (split_string) { + if (!strcmp("window", split_string[0])) { + guint gail_listenerid = 0; + if (gail_add_global_event_listener) { + // call gail's function to track gtk native window events + gail_listenerid = gail_add_global_event_listener(listener, event_type); + } + + rc = add_listener(listener, "MaiAtkObject", split_string[1], event_type, + gail_listenerid); + } else { + rc = add_listener(listener, split_string[1], split_string[2], event_type); + } + g_strfreev(split_string); + } + return rc; +} + +static void mai_util_remove_global_event_listener(guint remove_listener) { + if (remove_listener > 0) { + MaiUtilListenerInfo* listener_info; + gint tmp_idx = remove_listener; + + listener_info = + (MaiUtilListenerInfo*)g_hash_table_lookup(sListener_list, &tmp_idx); + + if (listener_info != nullptr) { + if (gail_remove_global_event_listener && listener_info->gail_listenerid) { + gail_remove_global_event_listener(listener_info->gail_listenerid); + } + + /* Hook id of 0 and signal id of 0 are invalid */ + if (listener_info->hook_id != 0 && listener_info->signal_id != 0) { + /* Remove the emission hook */ + g_signal_remove_emission_hook(listener_info->signal_id, + listener_info->hook_id); + + /* Remove the element from the hash */ + g_hash_table_remove(sListener_list, &tmp_idx); + } else { + g_warning("Invalid listener hook_id %ld or signal_id %d\n", + listener_info->hook_id, listener_info->signal_id); + } + } else { + // atk-bridge is initialized with gail (e.g. yelp) + // try gail_remove_global_event_listener + if (gail_remove_global_event_listener) { + return gail_remove_global_event_listener(remove_listener); + } + + g_warning("No listener with the specified listener id %d", + remove_listener); + } + } else { + g_warning("Invalid listener_id %d", remove_listener); + } +} + +static AtkKeyEventStruct* atk_key_event_from_gdk_event_key(GdkEventKey* key) { + AtkKeyEventStruct* event = g_new0(AtkKeyEventStruct, 1); + switch (key->type) { + case GDK_KEY_PRESS: + event->type = ATK_KEY_EVENT_PRESS; + break; + case GDK_KEY_RELEASE: + event->type = ATK_KEY_EVENT_RELEASE; + break; + default: + g_assert_not_reached(); + return nullptr; + } + event->state = key->state; + event->keyval = key->keyval; + event->length = key->length; + if (key->string && key->string[0] && + g_unichar_isgraph(g_utf8_get_char(key->string))) { + event->string = key->string; + } else if (key->type == GDK_KEY_PRESS || key->type == GDK_KEY_RELEASE) { + event->string = gdk_keyval_name(key->keyval); + } + event->keycode = key->hardware_keycode; + event->timestamp = key->time; + + return event; +} + +struct MaiKeyEventInfo { + AtkKeyEventStruct* key_event; + gpointer func_data; +}; + +union AtkKeySnoopFuncPointer { + AtkKeySnoopFunc func_ptr; + gpointer data; +}; + +static gboolean notify_hf(gpointer key, gpointer value, gpointer data) { + MaiKeyEventInfo* info = (MaiKeyEventInfo*)data; + AtkKeySnoopFuncPointer atkKeySnoop; + atkKeySnoop.data = value; + return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE + : FALSE; +} + +static void insert_hf(gpointer key, gpointer value, gpointer data) { + GHashTable* new_table = (GHashTable*)data; + g_hash_table_insert(new_table, key, value); +} + +static GHashTable* sKey_listener_list = nullptr; + +static gint mai_key_snooper(GtkWidget* the_widget, GdkEventKey* event, + gpointer func_data) { + /* notify each AtkKeySnoopFunc in turn... */ + + MaiKeyEventInfo* info = g_new0(MaiKeyEventInfo, 1); + gint consumed = 0; + if (sKey_listener_list) { + GHashTable* new_hash = g_hash_table_new(nullptr, nullptr); + g_hash_table_foreach(sKey_listener_list, insert_hf, new_hash); + info->key_event = atk_key_event_from_gdk_event_key(event); + info->func_data = func_data; + consumed = g_hash_table_foreach_steal(new_hash, notify_hf, info); + g_hash_table_destroy(new_hash); + g_free(info->key_event); + } + g_free(info); + return (consumed ? 1 : 0); +} + +static guint sKey_snooper_id = 0; + +static guint mai_util_add_key_event_listener(AtkKeySnoopFunc listener, + gpointer data) { + if (MOZ_UNLIKELY(!listener)) { + return 0; + } + + static guint key = 0; + + if (!sKey_listener_list) { + sKey_listener_list = g_hash_table_new(nullptr, nullptr); + } + + // If we have no registered event listeners then we need to (re)install the + // key event snooper. + if (g_hash_table_size(sKey_listener_list) == 0) { + sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data); + } + + AtkKeySnoopFuncPointer atkKeySnoop; + atkKeySnoop.func_ptr = listener; + key++; + g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key), + atkKeySnoop.data); + return key; +} + +static void mai_util_remove_key_event_listener(guint remove_listener) { + if (!sKey_listener_list) { + // atk-bridge is initialized with gail (e.g. yelp) + // try gail_remove_key_event_listener + return gail_remove_key_event_listener(remove_listener); + } + + g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER(remove_listener)); + if (g_hash_table_size(sKey_listener_list) == 0) { + gtk_key_snooper_remove(sKey_snooper_id); + } +} + +static AtkObject* mai_util_get_root() { + ApplicationAccessible* app = ApplicationAcc(); + if (app) return app->GetAtkObject(); + + // We've shutdown, try to use gail instead + // (to avoid assert in spi_atk_tidy_windows()) + // XXX tbsaunde then why didn't we replace the gail atk_util impl? + if (gail_get_root) return gail_get_root(); + + return nullptr; +} + +static const gchar* mai_util_get_toolkit_name() { return MAI_NAME; } + +static const gchar* mai_util_get_toolkit_version() { return MAI_VERSION; } + +static void _listener_info_destroy(gpointer data) { g_free(data); } + +static void window_added(AtkObject* atk_obj, guint index, AtkObject* child) { + if (!IS_MAI_OBJECT(child)) return; + + static guint id = g_signal_lookup("create", MAI_TYPE_ATK_OBJECT); + g_signal_emit(child, id, 0); +} + +static void window_removed(AtkObject* atk_obj, guint index, AtkObject* child) { + if (!IS_MAI_OBJECT(child)) return; + + static guint id = g_signal_lookup("destroy", MAI_TYPE_ATK_OBJECT); + g_signal_emit(child, id, 0); +} + +static void UtilInterfaceInit(MaiUtilClass* klass) { + AtkUtilClass* atk_class; + gpointer data; + + data = g_type_class_peek(ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS(data); + + // save gail function pointer + gail_add_global_event_listener = atk_class->add_global_event_listener; + gail_remove_global_event_listener = atk_class->remove_global_event_listener; + gail_remove_key_event_listener = atk_class->remove_key_event_listener; + gail_get_root = atk_class->get_root; + + atk_class->add_global_event_listener = mai_util_add_global_event_listener; + atk_class->remove_global_event_listener = + mai_util_remove_global_event_listener; + atk_class->add_key_event_listener = mai_util_add_key_event_listener; + atk_class->remove_key_event_listener = mai_util_remove_key_event_listener; + atk_class->get_root = mai_util_get_root; + atk_class->get_toolkit_name = mai_util_get_toolkit_name; + atk_class->get_toolkit_version = mai_util_get_toolkit_version; + + sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr, + _listener_info_destroy); + // Keep track of added/removed windows. + AtkObject* root = atk_get_root(); + g_signal_connect(root, "children-changed::add", (GCallback)window_added, + nullptr); + g_signal_connect(root, "children-changed::remove", (GCallback)window_removed, + nullptr); +} +} + +GType mai_util_get_type() { + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof(MaiUtilClass), + (GBaseInitFunc) nullptr, /* base init */ + (GBaseFinalizeFunc) nullptr, /* base finalize */ + (GClassInitFunc)UtilInterfaceInit, /* class init */ + (GClassFinalizeFunc) nullptr, /* class finalize */ + nullptr, /* class data */ + sizeof(MaiUtil), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) nullptr, /* instance init */ + nullptr /* value table */ + }; + + type = + g_type_register_static(ATK_TYPE_UTIL, "MaiUtil", &tinfo, GTypeFlags(0)); + } + return type; +} diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build new file mode 100644 index 0000000000..092ecada57 --- /dev/null +++ b/accessible/atk/moz.build @@ -0,0 +1,64 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.a11y += [ + "AccessibleWrap.h", +] + +SOURCES += [ + "AccessibleWrap.cpp", + "ApplicationAccessibleWrap.cpp", + "DocAccessibleWrap.cpp", + "DOMtoATK.cpp", + "nsMaiHyperlink.cpp", + "nsMaiInterfaceAction.cpp", + "nsMaiInterfaceComponent.cpp", + "nsMaiInterfaceDocument.cpp", + "nsMaiInterfaceEditableText.cpp", + "nsMaiInterfaceHyperlinkImpl.cpp", + "nsMaiInterfaceHypertext.cpp", + "nsMaiInterfaceImage.cpp", + "nsMaiInterfaceSelection.cpp", + "nsMaiInterfaceTable.cpp", + "nsMaiInterfaceTableCell.cpp", + "nsMaiInterfaceText.cpp", + "nsMaiInterfaceValue.cpp", + "Platform.cpp", + "RootAccessibleWrap.cpp", + "UtilInterface.cpp", +] + +LOCAL_INCLUDES += [ + "/accessible/base", + "/accessible/generic", + "/accessible/html", + "/accessible/ipc", + "/accessible/xpcom", + "/accessible/xul", + "/layout/generic", + "/other-licenses/atk-1.0", + "/widget", + "/widget/gtk", +] + +FINAL_LIBRARY = "xul" + +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": + CFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] + CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] + +if CONFIG["MOZ_ENABLE_DBUS"]: + CXXFLAGS += CONFIG["MOZ_DBUS_CFLAGS"] + +include("/ipc/chromium/chromium-config.mozbuild") + +if CONFIG["CC_TYPE"] in ("clang", "gcc"): + # Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib / + # gobject headers. See bug 1243331 comment 3. + CXXFLAGS += [ + "-Wno-error=unused-function", + "-Wno-unused-local-typedefs", + ] diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h new file mode 100644 index 0000000000..175a3c64ff --- /dev/null +++ b/accessible/atk/nsMai.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __NS_MAI_H__ +#define __NS_MAI_H__ + +#include +#include +#include + +#include "AccessibleWrap.h" + +namespace mozilla { +namespace a11y { +class RemoteAccessible; +class Accessible; +} // namespace a11y +} // namespace mozilla + +#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type()) +#define MAI_ATK_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAI_TYPE_ATK_OBJECT, MaiAtkObject)) +#define MAI_ATK_OBJECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAI_TYPE_ATK_OBJECT, MaiAtkObjectClass)) +#define IS_MAI_OBJECT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAI_TYPE_ATK_OBJECT)) +#define IS_MAI_OBJECT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAI_TYPE_ATK_OBJECT)) +#define MAI_ATK_OBJECT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAI_TYPE_ATK_OBJECT, MaiAtkObjectClass)) +GType mai_atk_object_get_type(void); +GType mai_util_get_type(); + +// This is a pointer to the atk_table_cell_get_type function if we are using +// a version of atk that defines that. +extern "C" GType (*gAtkTableCellGetTypeFunc)(); + +mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj); +mozilla::a11y::RemoteAccessible* GetProxy(AtkObject* aAtkObj); +mozilla::a11y::Accessible* GetInternalObj(AtkObject* aObj); +AtkObject* GetWrapperFor(mozilla::a11y::Accessible* acc); + +extern int atkMajorVersion, atkMinorVersion, atkMicroVersion; + +/** + * Return true if the loaded version of libatk-1.0.so is at least + * aMajor.aMinor.aMicro. + */ +static inline bool IsAtkVersionAtLeast(int aMajor, int aMinor, int aMicro = 0) { + return aMajor < atkMajorVersion || + (aMajor == atkMajorVersion && + (aMinor < atkMinorVersion || + (aMinor == atkMinorVersion && aMicro <= atkMicroVersion))); +} + +/** + * This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject + */ +struct MaiAtkObject { + AtkObject parent; + /* + * The AccessibleWrap whose properties and features are exported + * via this object instance. + */ + mozilla::a11y::Accessible* acc; + + /* + * Get the AtkHyperlink for this atk object. + */ + AtkHyperlink* GetAtkHyperlink(); + + /* + * Shutdown this AtkObject. + */ + void Shutdown(); + + /* + * Notify atk of a state change on this AtkObject. + */ + void FireStateChangeEvent(uint64_t aState, bool aEnabled); + + /* + * Notify ATK of a text change within this ATK object. + */ + void FireTextChangeEvent(const nsAString& aStr, int32_t aStart, uint32_t aLen, + bool aIsInsert, bool aIsFromUser); + + /** + * Notify ATK of a shown or hidden subtree rooted at aObject whose parent is + * aParent + */ + void FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, bool aFromUser); + + private: + /* + * do we have text-remove and text-insert signals if not we need to use + * text-changed see AccessibleWrap::FireAtkTextChangedEvent() and + * bug 619002 + */ + enum EAvailableAtkSignals { + eUnknown, + eHaveNewAtkTextSignals, + eNoNewAtkSignals + }; + + static EAvailableAtkSignals gAvailableAtkSignals; +}; + +#endif /* __NS_MAI_H__ */ diff --git a/accessible/atk/nsMaiHyperlink.cpp b/accessible/atk/nsMaiHyperlink.cpp new file mode 100644 index 0000000000..65f223e528 --- /dev/null +++ b/accessible/atk/nsMaiHyperlink.cpp @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIURI.h" +#include "nsMaiHyperlink.h" +#include "mozilla/a11y/RemoteAccessible.h" + +using namespace mozilla::a11y; + +/* MaiAtkHyperlink */ + +#define MAI_TYPE_ATK_HYPERLINK (mai_atk_hyperlink_get_type()) +#define MAI_ATK_HYPERLINK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlink)) +#define MAI_ATK_HYPERLINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), MAI_TYPE_ATK_HYPERLINK, \ + MaiAtkHyperlinkClass)) +#define MAI_IS_ATK_HYPERLINK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), MAI_TYPE_ATK_HYPERLINK)) +#define MAI_IS_ATK_HYPERLINK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), MAI_TYPE_ATK_HYPERLINK)) +#define MAI_ATK_HYPERLINK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), MAI_TYPE_ATK_HYPERLINK, \ + MaiAtkHyperlinkClass)) + +/** + * This MaiAtkHyperlink is a thin wrapper, in the MAI namespace, + * for AtkHyperlink + */ + +struct MaiAtkHyperlink { + AtkHyperlink parent; + + /* + * The MaiHyperlink whose properties and features are exported via this + * hyperlink instance. + */ + MaiHyperlink* maiHyperlink; +}; + +struct MaiAtkHyperlinkClass { + AtkHyperlinkClass parent_class; +}; + +GType mai_atk_hyperlink_get_type(void); + +G_BEGIN_DECLS +/* callbacks for AtkHyperlink */ +static void classInitCB(AtkHyperlinkClass* aClass); +static void finalizeCB(GObject* aObj); + +/* callbacks for AtkHyperlink virtual functions */ +static gchar* getUriCB(AtkHyperlink* aLink, gint aLinkIndex); +static AtkObject* getObjectCB(AtkHyperlink* aLink, gint aLinkIndex); +static gint getEndIndexCB(AtkHyperlink* aLink); +static gint getStartIndexCB(AtkHyperlink* aLink); +static gboolean isValidCB(AtkHyperlink* aLink); +static gint getAnchorCountCB(AtkHyperlink* aLink); +G_END_DECLS + +static gpointer parent_class = nullptr; + +static MaiHyperlink* GetMaiHyperlink(AtkHyperlink* aHyperlink) { + NS_ENSURE_TRUE(MAI_IS_ATK_HYPERLINK(aHyperlink), nullptr); + MaiHyperlink* maiHyperlink = MAI_ATK_HYPERLINK(aHyperlink)->maiHyperlink; + NS_ENSURE_TRUE(maiHyperlink != nullptr, nullptr); + NS_ENSURE_TRUE(maiHyperlink->GetAtkHyperlink() == aHyperlink, nullptr); + return maiHyperlink; +} + +GType mai_atk_hyperlink_get_type(void) { + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof(MaiAtkHyperlinkClass), + (GBaseInitFunc) nullptr, + (GBaseFinalizeFunc) nullptr, + (GClassInitFunc)classInitCB, + (GClassFinalizeFunc) nullptr, + nullptr, /* class data */ + sizeof(MaiAtkHyperlink), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) nullptr, + nullptr /* value table */ + }; + + type = g_type_register_static(ATK_TYPE_HYPERLINK, "MaiAtkHyperlink", &tinfo, + GTypeFlags(0)); + } + return type; +} + +MaiHyperlink::MaiHyperlink(Accessible* aHyperLink) + : mHyperlink(aHyperLink), mMaiAtkHyperlink(nullptr) { + mMaiAtkHyperlink = reinterpret_cast( + g_object_new(mai_atk_hyperlink_get_type(), nullptr)); + NS_ASSERTION(mMaiAtkHyperlink, "OUT OF MEMORY"); + if (!mMaiAtkHyperlink) return; + + MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = this; +} + +MaiHyperlink::~MaiHyperlink() { + if (mMaiAtkHyperlink) { + MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = nullptr; + g_object_unref(mMaiAtkHyperlink); + } +} + +/* static functions for ATK callbacks */ + +void classInitCB(AtkHyperlinkClass* aClass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(aClass); + + parent_class = g_type_class_peek_parent(aClass); + + aClass->get_uri = getUriCB; + aClass->get_object = getObjectCB; + aClass->get_end_index = getEndIndexCB; + aClass->get_start_index = getStartIndexCB; + aClass->is_valid = isValidCB; + aClass->get_n_anchors = getAnchorCountCB; + + gobject_class->finalize = finalizeCB; +} + +void finalizeCB(GObject* aObj) { + NS_ASSERTION(MAI_IS_ATK_HYPERLINK(aObj), "Invalid MaiAtkHyperlink"); + if (!MAI_IS_ATK_HYPERLINK(aObj)) return; + + MaiAtkHyperlink* maiAtkHyperlink = MAI_ATK_HYPERLINK(aObj); + maiAtkHyperlink->maiHyperlink = nullptr; + + /* call parent finalize function */ + if (G_OBJECT_CLASS(parent_class)->finalize) { + G_OBJECT_CLASS(parent_class)->finalize(aObj); + } +} + +gchar* getUriCB(AtkHyperlink* aLink, gint aLinkIndex) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) { + return nullptr; + } + + Accessible* acc = maiLink->Acc(); + if (!acc) { + return nullptr; + } + + nsAutoCString cautoStr; + nsCOMPtr uri = acc->AnchorURIAt(aLinkIndex); + if (!uri) return nullptr; + + nsresult rv = uri->GetSpec(cautoStr); + NS_ENSURE_SUCCESS(rv, nullptr); + + return g_strdup(cautoStr.get()); +} + +AtkObject* getObjectCB(AtkHyperlink* aLink, gint aLinkIndex) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) { + return nullptr; + } + + Accessible* acc = maiLink->Acc(); + if (!acc) { + return nullptr; + } + + Accessible* anchor = acc->AnchorAt(aLinkIndex); + return anchor ? GetWrapperFor(anchor) : nullptr; +} + +gint getEndIndexCB(AtkHyperlink* aLink) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) return false; + + return static_cast(maiLink->Acc()->EndOffset()); +} + +gint getStartIndexCB(AtkHyperlink* aLink) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) return -1; + + return static_cast(maiLink->Acc()->StartOffset()); +} + +gboolean isValidCB(AtkHyperlink* aLink) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) return false; + + Accessible* acc = maiLink->Acc(); + if (!acc) { + return false; + } + + return static_cast(acc->IsLinkValid()); +} + +gint getAnchorCountCB(AtkHyperlink* aLink) { + MaiHyperlink* maiLink = GetMaiHyperlink(aLink); + if (!maiLink) return -1; + + Accessible* acc = maiLink->Acc(); + if (!acc) { + return -1; + } + + return static_cast(acc->AnchorCount()); +} diff --git a/accessible/atk/nsMaiHyperlink.h b/accessible/atk/nsMaiHyperlink.h new file mode 100644 index 0000000000..34f517cc7a --- /dev/null +++ b/accessible/atk/nsMaiHyperlink.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MAI_HYPERLINK_H__ +#define __MAI_HYPERLINK_H__ + +#include "nsMai.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/LocalAccessible.h" +#include "mozilla/a11y/RemoteAccessible.h" +#include "nsDebug.h" + +struct _AtkHyperlink; +typedef struct _AtkHyperlink AtkHyperlink; + +namespace mozilla { +namespace a11y { + +/* + * MaiHyperlink is a auxiliary class for MaiInterfaceHyperText. + */ + +class MaiHyperlink { + public: + explicit MaiHyperlink(Accessible* aHyperLink); + ~MaiHyperlink(); + + public: + AtkHyperlink* GetAtkHyperlink() const { return mMaiAtkHyperlink; } + Accessible* Acc() { + if (!mHyperlink) { + return nullptr; + } + NS_ASSERTION(mHyperlink->IsLink(), "Why isn't it a link!"); + return mHyperlink; + } + + protected: + Accessible* mHyperlink; + AtkHyperlink* mMaiAtkHyperlink; +}; + +} // namespace a11y +} // namespace mozilla + +#endif /* __MAI_HYPERLINK_H__ */ diff --git a/accessible/atk/nsMaiInterfaceAction.cpp b/accessible/atk/nsMaiInterfaceAction.cpp new file mode 100644 index 0000000000..8149e0aff5 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceAction.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "nsMai.h" +#include "mozilla/Likely.h" +#include "nsAccessibilityService.h" +#include "RemoteAccessible.h" +#include "nsString.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { + +static gboolean doActionCB(AtkAction* aAction, gint aActionIndex) { + AtkObject* atkObject = ATK_OBJECT(aAction); + if (Accessible* acc = GetInternalObj(atkObject)) { + return acc->DoAction(aActionIndex); + } + + return false; +} + +static gint getActionCountCB(AtkAction* aAction) { + AtkObject* atkObject = ATK_OBJECT(aAction); + if (Accessible* acc = GetInternalObj(atkObject)) { + return acc->ActionCount(); + } + + return 0; +} + +static const gchar* getActionDescriptionCB(AtkAction* aAction, + gint aActionIndex) { + AtkObject* atkObject = ATK_OBJECT(aAction); + nsAutoString description; + if (Accessible* acc = GetInternalObj(atkObject)) { + acc->ActionDescriptionAt(aActionIndex, description); + return AccessibleWrap::ReturnString(description); + } + + return nullptr; +} + +static const gchar* getActionNameCB(AtkAction* aAction, gint aActionIndex) { + AtkObject* atkObject = ATK_OBJECT(aAction); + nsAutoString autoStr; + if (Accessible* acc = GetInternalObj(atkObject)) { + acc->ActionNameAt(aActionIndex, autoStr); + return AccessibleWrap::ReturnString(autoStr); + } + + return nullptr; +} + +static const gchar* getKeyBindingCB(AtkAction* aAction, gint aActionIndex) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aAction)); + if (!acc) { + return nullptr; + } + nsAutoString keyBindingsStr; + AccessibleWrap::GetKeyBinding(acc, keyBindingsStr); + + return AccessibleWrap::ReturnString(keyBindingsStr); +} +} + +void actionInterfaceInitCB(AtkActionIface* aIface) { + NS_ASSERTION(aIface, "Invalid aIface"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->do_action = doActionCB; + aIface->get_n_actions = getActionCountCB; + aIface->get_description = getActionDescriptionCB; + aIface->get_keybinding = getKeyBindingCB; + aIface->get_name = getActionNameCB; +} diff --git a/accessible/atk/nsMaiInterfaceComponent.cpp b/accessible/atk/nsMaiInterfaceComponent.cpp new file mode 100644 index 0000000000..81e82a8f53 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceComponent.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "AccessibleWrap.h" +#include "nsAccUtils.h" +#include "nsMai.h" +#include "nsWindow.h" +#include "mozilla/Likely.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/a11y/RemoteAccessible.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/Document.h" +#include "nsAccessibilityService.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { + +static AtkObject* refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX, + gint aAccY, AtkCoordType aCoordType) { + return refAccessibleAtPointHelper(ATK_OBJECT(aComponent), aAccX, aAccY, + aCoordType); +} + +static void getExtentsCB(AtkComponent* aComponent, gint* aX, gint* aY, + gint* aWidth, gint* aHeight, AtkCoordType aCoordType) { + getExtentsHelper(ATK_OBJECT(aComponent), aX, aY, aWidth, aHeight, aCoordType); +} + +static gboolean grabFocusCB(AtkComponent* aComponent) { + AtkObject* atkObject = ATK_OBJECT(aComponent); + Accessible* acc = GetInternalObj(atkObject); + if (acc) { + acc->TakeFocus(); + return TRUE; + } + return FALSE; +} + +// ScrollType is compatible +MOZ_CAN_RUN_SCRIPT_BOUNDARY +static gboolean scrollToCB(AtkComponent* aComponent, AtkScrollType type) { + AtkObject* atkObject = ATK_OBJECT(aComponent); + if (Accessible* acc = GetInternalObj(atkObject)) { + acc->ScrollTo(type); + return TRUE; + } + + return FALSE; +} + +// CoordType is compatible +static gboolean scrollToPointCB(AtkComponent* aComponent, AtkCoordType coords, + gint x, gint y) { + AtkObject* atkObject = ATK_OBJECT(aComponent); + AccessibleWrap* accWrap = GetAccessibleWrap(atkObject); + if (accWrap) { + accWrap->ScrollToPoint(coords, x, y); + return TRUE; + } + + RemoteAccessible* proxy = GetProxy(atkObject); + if (proxy) { + proxy->ScrollToPoint(coords, x, y); + return TRUE; + } + + return FALSE; +} +} + +AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY, + AtkCoordType aCoordType) { + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return nullptr; + } + + // Accessible::ChildAtPoint(x,y) is in screen pixels. + if (aCoordType == ATK_XY_WINDOW) { + mozilla::LayoutDeviceIntPoint winCoords = + nsAccUtils::GetScreenCoordsForWindow(acc); + aX += winCoords.x; + aY += winCoords.y; + } + + Accessible* accAtPoint = + acc->ChildAtPoint(aX, aY, Accessible::EWhichChildAtPoint::DeepestChild); + if (!accAtPoint) { + return nullptr; + } + roles::Role role = accAtPoint->Role(); + if (role == roles::TEXT_LEAF || role == roles::STATICTEXT) { + // We don't include text leaf nodes in the ATK tree, so return the parent. + accAtPoint = accAtPoint->Parent(); + MOZ_ASSERT(accAtPoint, "Text leaf should always have a parent"); + } + AtkObject* atkObj = GetWrapperFor(accAtPoint); + if (atkObj) { + g_object_ref(atkObj); + } + return atkObj; +} + +static double getScaleFactor(Accessible* aAccessible) { + DocAccessible* docAcc = nullptr; + if (LocalAccessible* localAcc = aAccessible->AsLocal()) { + docAcc = localAcc->Document(); + } else { + RemoteAccessible* remote = aAccessible->AsRemote(); + LocalAccessible* outerDoc = remote->OuterDocOfRemoteBrowser(); + if (outerDoc) { + docAcc = outerDoc->Document(); + } + } + + if (!docAcc || !docAcc->DocumentNode()) { + return 1.0; + } + + nsCOMPtr rootWidget = + nsContentUtils::WidgetForDocument(docAcc->DocumentNode()); + if (!rootWidget) { + return 1.0; + } + + if (RefPtr window = + static_cast(rootWidget->GetTopLevelWidget())) { + return window->FractionalScaleFactor(); + } + + return 1.0; +} + +void getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth, + gint* aHeight, AtkCoordType aCoordType) { + *aX = *aY = *aWidth = *aHeight = -1; + + Accessible* acc = GetInternalObj(aAtkObj); + if (!acc) { + return; + } + + mozilla::LayoutDeviceIntRect screenRect = acc->Bounds(); + if (screenRect.IsEmpty()) { + return; + } + + if (aCoordType == ATK_XY_WINDOW) { + mozilla::LayoutDeviceIntPoint winCoords = + nsAccUtils::GetScreenCoordsForWindow(acc); + screenRect.x -= winCoords.x; + screenRect.y -= winCoords.y; + } + + double scaleFactor = getScaleFactor(acc); + + *aX = screenRect.x / scaleFactor; + *aY = screenRect.y / scaleFactor; + *aWidth = screenRect.width / scaleFactor; + *aHeight = screenRect.height / scaleFactor; +} + +void componentInterfaceInitCB(AtkComponentIface* aIface) { + NS_ASSERTION(aIface, "Invalid Interface"); + if (MOZ_UNLIKELY(!aIface)) return; + + /* + * Use default implementation in atk for contains, get_position, + * and get_size + */ + aIface->ref_accessible_at_point = refAccessibleAtPointCB; + aIface->get_extents = getExtentsCB; + aIface->grab_focus = grabFocusCB; + if (IsAtkVersionAtLeast(2, 30)) { + aIface->scroll_to = scrollToCB; + aIface->scroll_to_point = scrollToPointCB; + } +} diff --git a/accessible/atk/nsMaiInterfaceDocument.cpp b/accessible/atk/nsMaiInterfaceDocument.cpp new file mode 100644 index 0000000000..da1bffce37 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceDocument.cpp @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "AccessibleWrap.h" +#include "DocAccessible.h" +#include "nsAccUtils.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/Likely.h" + +using namespace mozilla::a11y; + +static const char* const kDocUrlName = "DocURL"; +static const char* const kMimeTypeName = "MimeType"; + +// below functions are vfuncs on an ATK interface so they need to be C call +extern "C" { + +static const gchar* getDocumentLocaleCB(AtkDocument* aDocument); +static AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument); +static const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument, + const gchar* aAttrName); + +void documentInterfaceInitCB(AtkDocumentIface* aIface) { + NS_ASSERTION(aIface, "Invalid Interface"); + if (MOZ_UNLIKELY(!aIface)) return; + + /* + * We don't support get_document or set_attribute right now. + */ + aIface->get_document_attributes = getDocumentAttributesCB; + aIface->get_document_attribute_value = getDocumentAttributeValueCB; + aIface->get_document_locale = getDocumentLocaleCB; +} + +const gchar* getDocumentLocaleCB(AtkDocument* aDocument) { + nsAutoString locale; + Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument)); + if (acc) { + acc->Language(locale); + } + + return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale); +} + +static inline GSList* prependToList(GSList* aList, const char* const aName, + const nsAutoString& aValue) { + if (aValue.IsEmpty()) { + return aList; + } + + // libspi will free these + AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); + atkAttr->name = g_strdup(aName); + atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get()); + return g_slist_prepend(aList, atkAttr); +} + +AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument) { + nsAutoString url; + nsAutoString mimeType; + Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument)); + + if (!acc || !acc->IsDoc()) { + return nullptr; + } + + nsAccUtils::DocumentURL(acc, url); + nsAccUtils::DocumentMimeType(acc, mimeType); + + // according to atkobject.h, AtkAttributeSet is a GSList + GSList* attributes = nullptr; + attributes = prependToList(attributes, kDocUrlName, url); + attributes = prependToList(attributes, kMimeTypeName, mimeType); + + return attributes; +} + +const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument, + const gchar* aAttrName) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aDocument)); + + if (!acc || !acc->IsDoc()) { + return nullptr; + } + + nsAutoString attrValue; + if (!strcasecmp(aAttrName, kDocUrlName)) { + nsAccUtils::DocumentURL(acc, attrValue); + } else if (!strcasecmp(aAttrName, kMimeTypeName)) { + nsAccUtils::DocumentMimeType(acc, attrValue); + } else { + return nullptr; + } + + return attrValue.IsEmpty() ? nullptr + : AccessibleWrap::ReturnString(attrValue); +} +} diff --git a/accessible/atk/nsMaiInterfaceEditableText.cpp b/accessible/atk/nsMaiInterfaceEditableText.cpp new file mode 100644 index 0000000000..2dc692362e --- /dev/null +++ b/accessible/atk/nsMaiInterfaceEditableText.cpp @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "HyperTextAccessible-inl.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "nsString.h" +#include "mozilla/Likely.h" + +using namespace mozilla::a11y; + +extern "C" { +static void setTextContentsCB(AtkEditableText* aText, const gchar* aString) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + NS_ConvertUTF8toUTF16 strContent(aString); + text->ReplaceText(strContent); + } + } +} + +static void insertTextCB(AtkEditableText* aText, const gchar* aString, + gint aLength, gint* aPosition) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + NS_ConvertUTF8toUTF16 strContent(aString); + text->InsertText(strContent, *aPosition); + } + } +} + +static void copyTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + text->CopyText(aStartPos, aEndPos); + } + } +} + +static void cutTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + text->CutText(aStartPos, aEndPos); + } + } +} + +static void deleteTextCB(AtkEditableText* aText, gint aStartPos, gint aEndPos) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + text->DeleteText(aStartPos, aEndPos); + } + } +} + +MOZ_CAN_RUN_SCRIPT_BOUNDARY +static void pasteTextCB(AtkEditableText* aText, gint aPosition) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (acc->IsTextRole()) { + return; + } + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + text->PasteText(aPosition); + } + } +} +} + +void editableTextInterfaceInitCB(AtkEditableTextIface* aIface) { + NS_ASSERTION(aIface, "Invalid aIface"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->set_text_contents = setTextContentsCB; + aIface->insert_text = insertTextCB; + aIface->copy_text = copyTextCB; + aIface->cut_text = cutTextCB; + aIface->delete_text = deleteTextCB; + aIface->paste_text = pasteTextCB; +} diff --git a/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp b/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp new file mode 100644 index 0000000000..ed8c4f4fce --- /dev/null +++ b/accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "nsMaiHyperlink.h" +#include "mozilla/Likely.h" + +using namespace mozilla::a11y; + +extern "C" { +static AtkHyperlink* getHyperlinkCB(AtkHyperlinkImpl* aImpl) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aImpl)); + if (!acc) { + return nullptr; + } + + NS_ASSERTION(acc->IsLink(), "why isn't it a link!"); + + return MAI_ATK_OBJECT(aImpl)->GetAtkHyperlink(); +} +} + +void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface* aIface) { + NS_ASSERTION(aIface, "no interface!"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_hyperlink = getHyperlinkCB; +} diff --git a/accessible/atk/nsMaiInterfaceHypertext.cpp b/accessible/atk/nsMaiInterfaceHypertext.cpp new file mode 100644 index 0000000000..1e073c87d2 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceHypertext.cpp @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "HyperTextAccessible.h" +#include "nsMai.h" +#include "nsMaiHyperlink.h" +#include "RemoteAccessible.h" +#include "mozilla/Likely.h" + +using namespace mozilla::a11y; + +extern "C" { + +static AtkHyperlink* getLinkCB(AtkHypertext* aText, gint aLinkIndex) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase()) { + Accessible* linkAcc = hyperText->LinkAt(aLinkIndex); + AtkObject* atkHyperLink = GetWrapperFor(linkAcc); + NS_ENSURE_TRUE(IS_MAI_OBJECT(atkHyperLink), nullptr); + return MAI_ATK_OBJECT(atkHyperLink)->GetAtkHyperlink(); + } + } + + return nullptr; +} + +static gint getLinkCountCB(AtkHypertext* aText) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase()) { + return static_cast(hyperText->LinkCount()); + } + } + return -1; +} + +static gint getLinkIndexCB(AtkHypertext* aText, gint aCharIndex) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return -1; + } + HyperTextAccessibleBase* hyperText = acc->AsHyperTextBase(); + if (!hyperText) { + return -1; + } + return hyperText->LinkIndexAtOffset(aCharIndex); +} + +} // extern "C" + +void hypertextInterfaceInitCB(AtkHypertextIface* aIface) { + NS_ASSERTION(aIface, "no interface!"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_link = getLinkCB; + aIface->get_n_links = getLinkCountCB; + aIface->get_link_index = getLinkIndexCB; +} diff --git a/accessible/atk/nsMaiInterfaceImage.cpp b/accessible/atk/nsMaiInterfaceImage.cpp new file mode 100644 index 0000000000..dee28f109f --- /dev/null +++ b/accessible/atk/nsMaiInterfaceImage.cpp @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "AccessibleWrap.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/Likely.h" +#include "nsMai.h" +#include "nsIAccessibleTypes.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { +const gchar* getDescriptionCB(AtkObject* aAtkObj); + +static void getImagePositionCB(AtkImage* aImage, gint* aAccX, gint* aAccY, + AtkCoordType aCoordType) { + LayoutDeviceIntPoint pos(-1, -1); + uint32_t geckoCoordType = + (aCoordType == ATK_XY_WINDOW) + ? nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE + : nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; + + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aImage))) { + pos = acc->Position(geckoCoordType); + } + + *aAccX = pos.x; + *aAccY = pos.y; +} + +static const gchar* getImageDescriptionCB(AtkImage* aImage) { + return getDescriptionCB(ATK_OBJECT(aImage)); +} + +static void getImageSizeCB(AtkImage* aImage, gint* aAccWidth, + gint* aAccHeight) { + LayoutDeviceIntSize size(-1, -1); + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aImage))) { + size = acc->Size(); + } + + *aAccWidth = size.width; + *aAccHeight = size.height; +} + +} // extern "C" + +void imageInterfaceInitCB(AtkImageIface* aIface) { + NS_ASSERTION(aIface, "no interface!"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_image_position = getImagePositionCB; + aIface->get_image_description = getImageDescriptionCB; + aIface->get_image_size = getImageSizeCB; +} diff --git a/accessible/atk/nsMaiInterfaceSelection.cpp b/accessible/atk/nsMaiInterfaceSelection.cpp new file mode 100644 index 0000000000..80b4d260f1 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceSelection.cpp @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "LocalAccessible-inl.h" +#include "AccessibleWrap.h" +#include "nsMai.h" +#include "mozilla/Likely.h" + +#include +#include + +using namespace mozilla::a11y; + +extern "C" { + +static gboolean addSelectionCB(AtkSelection* aSelection, gint i) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->AddItemToSelection(i); + } + + return FALSE; +} + +static gboolean clearSelectionCB(AtkSelection* aSelection) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->UnselectAll(); + } + + return FALSE; +} + +static AtkObject* refSelectionCB(AtkSelection* aSelection, gint i) { + AtkObject* atkObj = nullptr; + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + Accessible* selectedItem = acc->GetSelectedItem(i); + if (selectedItem) { + atkObj = GetWrapperFor(selectedItem); + } + + if (atkObj) { + g_object_ref(atkObj); + } + + return atkObj; +} + +static gint getSelectionCountCB(AtkSelection* aSelection) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->SelectedItemCount(); + } + + return -1; +} + +static gboolean isChildSelectedCB(AtkSelection* aSelection, gint i) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->IsItemSelected(i); + } + + return FALSE; +} + +static gboolean removeSelectionCB(AtkSelection* aSelection, gint i) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->RemoveItemFromSelection(i); + } + + return FALSE; +} + +static gboolean selectAllSelectionCB(AtkSelection* aSelection) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aSelection)); + if (acc && acc->IsSelect()) { + return acc->SelectAll(); + } + + return FALSE; +} +} + +void selectionInterfaceInitCB(AtkSelectionIface* aIface) { + NS_ASSERTION(aIface, "Invalid aIface"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->add_selection = addSelectionCB; + aIface->clear_selection = clearSelectionCB; + aIface->ref_selection = refSelectionCB; + aIface->get_selection_count = getSelectionCountCB; + aIface->is_child_selected = isChildSelectedCB; + aIface->remove_selection = removeSelectionCB; + aIface->select_all_selection = selectAllSelectionCB; +} diff --git a/accessible/atk/nsMaiInterfaceTable.cpp b/accessible/atk/nsMaiInterfaceTable.cpp new file mode 100644 index 0000000000..cfba9e78d1 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceTable.cpp @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "AccessibleWrap.h" +#include "mozilla/a11y/TableAccessible.h" +#include "nsAccessibilityService.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "nsTArray.h" + +#include "mozilla/Likely.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { +static AtkObject* refAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { + if (aRowIdx < 0 || aColIdx < 0) { + return nullptr; + } + + AtkObject* cellAtkObj = nullptr; + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + Accessible* cell = acc->AsTable()->CellAt(aRowIdx, aColIdx); + if (!cell) { + return nullptr; + } + + cellAtkObj = GetWrapperFor(cell); + + if (cellAtkObj) { + g_object_ref(cellAtkObj); + } + + return cellAtkObj; +} + +static gint getIndexAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { + if (aRowIdx < 0 || aColIdx < 0) { + return -1; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->CellIndexAt(aRowIdx, aColIdx)); +} + +static gint getColumnAtIndexCB(AtkTable* aTable, gint aIdx) { + if (aIdx < 0) { + return -1; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->ColIndexAt(aIdx)); +} + +static gint getRowAtIndexCB(AtkTable* aTable, gint aIdx) { + if (aIdx < 0) { + return -1; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->RowIndexAt(aIdx)); +} + +static gint getColumnCountCB(AtkTable* aTable) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->ColCount()); +} + +static gint getRowCountCB(AtkTable* aTable) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->RowCount()); +} + +static gint getColumnExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { + if (aRowIdx < 0 || aColIdx < 0) { + return -1; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->ColExtentAt(aRowIdx, aColIdx)); +} + +static gint getRowExtentAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return -1; + } + return static_cast(acc->AsTable()->RowExtentAt(aRowIdx, aColIdx)); +} + +static AtkObject* getCaptionCB(AtkTable* aTable) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + Accessible* caption = acc->AsTable()->Caption(); + return caption ? GetWrapperFor(caption) : nullptr; +} + +static const gchar* getColumnDescriptionCB(AtkTable* aTable, gint aColumn) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + nsAutoString autoStr; + acc->AsTable()->ColDescription(aColumn, autoStr); + return AccessibleWrap::ReturnString(autoStr); +} + +static AtkObject* getColumnHeaderCB(AtkTable* aTable, gint aColIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + Accessible* header = AccessibleWrap::GetColumnHeader(acc->AsTable(), aColIdx); + return header ? GetWrapperFor(header) : nullptr; +} + +static const gchar* getRowDescriptionCB(AtkTable* aTable, gint aRow) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + nsAutoString autoStr; + acc->AsTable()->RowDescription(aRow, autoStr); + return AccessibleWrap::ReturnString(autoStr); +} + +static AtkObject* getRowHeaderCB(AtkTable* aTable, gint aRowIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return nullptr; + } + Accessible* header = AccessibleWrap::GetRowHeader(acc->AsTable(), aRowIdx); + return header ? GetWrapperFor(header) : nullptr; +} + +static AtkObject* getSummaryCB(AtkTable* aTable) { + // Neither html:table nor xul:tree nor ARIA grid/tree have an ability to + // link an accessible object to specify a summary. There is closes method + // in TableAccessible::summary to get a summary as a string which is not + // mapped directly to ATK. + return nullptr; +} + +static gint getSelectedColumnsCB(AtkTable* aTable, gint** aSelected) { + *aSelected = nullptr; + + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return 0; + } + AutoTArray cols; + acc->AsTable()->SelectedColIndices(&cols); + + if (cols.IsEmpty()) return 0; + + gint* atkColumns = g_new(gint, cols.Length()); + if (!atkColumns) { + NS_WARNING("OUT OF MEMORY"); + return 0; + } + + memcpy(atkColumns, cols.Elements(), cols.Length() * sizeof(uint32_t)); + *aSelected = atkColumns; + return cols.Length(); +} + +static gint getSelectedRowsCB(AtkTable* aTable, gint** aSelected) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return 0; + } + AutoTArray rows; + acc->AsTable()->SelectedRowIndices(&rows); + + gint* atkRows = g_new(gint, rows.Length()); + if (!atkRows) { + NS_WARNING("OUT OF MEMORY"); + return 0; + } + + memcpy(atkRows, rows.Elements(), rows.Length() * sizeof(uint32_t)); + *aSelected = atkRows; + return rows.Length(); +} + +static gboolean isColumnSelectedCB(AtkTable* aTable, gint aColIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; + } + return static_cast(acc->AsTable()->IsColSelected(aColIdx)); +} + +static gboolean isRowSelectedCB(AtkTable* aTable, gint aRowIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; + } + return static_cast(acc->AsTable()->IsRowSelected(aRowIdx)); +} + +static gboolean isCellSelectedCB(AtkTable* aTable, gint aRowIdx, gint aColIdx) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTable)); + if (!acc) { + return FALSE; + } + return static_cast( + acc->AsTable()->IsCellSelected(aRowIdx, aColIdx)); +} +} + +void tableInterfaceInitCB(AtkTableIface* aIface) { + NS_ASSERTION(aIface, "no interface!"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->ref_at = refAtCB; + aIface->get_index_at = getIndexAtCB; + aIface->get_column_at_index = getColumnAtIndexCB; + aIface->get_row_at_index = getRowAtIndexCB; + aIface->get_n_columns = getColumnCountCB; + aIface->get_n_rows = getRowCountCB; + aIface->get_column_extent_at = getColumnExtentAtCB; + aIface->get_row_extent_at = getRowExtentAtCB; + aIface->get_caption = getCaptionCB; + aIface->get_column_description = getColumnDescriptionCB; + aIface->get_column_header = getColumnHeaderCB; + aIface->get_row_description = getRowDescriptionCB; + aIface->get_row_header = getRowHeaderCB; + aIface->get_summary = getSummaryCB; + aIface->get_selected_columns = getSelectedColumnsCB; + aIface->get_selected_rows = getSelectedRowsCB; + aIface->is_column_selected = isColumnSelectedCB; + aIface->is_row_selected = isRowSelectedCB; + aIface->is_selected = isCellSelectedCB; +} diff --git a/accessible/atk/nsMaiInterfaceTableCell.cpp b/accessible/atk/nsMaiInterfaceTableCell.cpp new file mode 100644 index 0000000000..6d61344051 --- /dev/null +++ b/accessible/atk/nsMaiInterfaceTableCell.cpp @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "mozilla/a11y/TableAccessible.h" +#include "mozilla/a11y/TableCellAccessible.h" +#include "nsAccessibilityService.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "nsTArray.h" + +#include "mozilla/Likely.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { +static gint GetColumnSpanCB(AtkTableCell* aCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return 0; + } + return static_cast(acc->AsTableCell()->ColExtent()); +} + +static gint GetRowSpanCB(AtkTableCell* aCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return 0; + } + return static_cast(acc->AsTableCell()->RowExtent()); +} + +static gboolean GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return false; + } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return false; + } + *aRow = static_cast(cell->RowIdx()); + *aCol = static_cast(cell->ColIdx()); + return true; +} + +static gboolean GetRowColumnSpanCB(AtkTableCell* aCell, gint* aRow, gint* aCol, + gint* aRowExtent, gint* aColExtent) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return false; + } + TableCellAccessible* cellAcc = acc->AsTableCell(); + if (!cellAcc) { + return false; + } + *aCol = static_cast(cellAcc->ColIdx()); + *aRow = static_cast(cellAcc->RowIdx()); + *aColExtent = static_cast(cellAcc->ColExtent()); + *aRowExtent = static_cast(cellAcc->RowExtent()); + return true; +} + +static AtkObject* GetTableCB(AtkTableCell* aTableCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aTableCell)); + if (!acc) { + return nullptr; + } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } + TableAccessible* table = cell->Table(); + if (!table) { + return nullptr; + } + Accessible* tableAcc = table->AsAccessible(); + return tableAcc ? GetWrapperFor(tableAcc) : nullptr; +} + +static GPtrArray* GetColumnHeaderCellsCB(AtkTableCell* aCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return nullptr; + } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } + AutoTArray headers; + cell->ColHeaderCells(&headers); + if (headers.IsEmpty()) { + return nullptr; + } + + GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length()); + for (Accessible* header : headers) { + AtkObject* atkHeader = GetWrapperFor(header); + g_object_ref(atkHeader); + g_ptr_array_add(atkHeaders, atkHeader); + } + + return atkHeaders; +} + +static GPtrArray* GetRowHeaderCellsCB(AtkTableCell* aCell) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aCell)); + if (!acc) { + return nullptr; + } + TableCellAccessible* cell = acc->AsTableCell(); + if (!cell) { + return nullptr; + } + AutoTArray headers; + cell->RowHeaderCells(&headers); + if (headers.IsEmpty()) { + return nullptr; + } + + GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length()); + for (Accessible* header : headers) { + AtkObject* atkHeader = GetWrapperFor(header); + g_object_ref(atkHeader); + g_ptr_array_add(atkHeaders, atkHeader); + } + + return atkHeaders; +} +} + +void tableCellInterfaceInitCB(AtkTableCellIface* aIface) { + NS_ASSERTION(aIface, "no interface!"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_column_span = GetColumnSpanCB; + aIface->get_column_header_cells = GetColumnHeaderCellsCB; + aIface->get_position = GetPositionCB; + aIface->get_row_span = GetRowSpanCB; + aIface->get_row_header_cells = GetRowHeaderCellsCB; + aIface->get_row_column_span = GetRowColumnSpanCB; + aIface->get_table = GetTableCB; +} diff --git a/accessible/atk/nsMaiInterfaceText.cpp b/accessible/atk/nsMaiInterfaceText.cpp new file mode 100644 index 0000000000..b5c0dcc38d --- /dev/null +++ b/accessible/atk/nsMaiInterfaceText.cpp @@ -0,0 +1,557 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" +#include "mozilla/a11y/PDocAccessible.h" +#include "nsAccessibilityService.h" +#include "LocalAccessible-inl.h" +#include "HyperTextAccessible-inl.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "AccAttributes.h" + +#include "nsIAccessibleTypes.h" +#include "nsISimpleEnumerator.h" +#include "nsUTF8Utils.h" + +#include "mozilla/Likely.h" + +#include "DOMtoATK.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED]; + +static AtkAttributeSet* ConvertToAtkTextAttributeSet( + AccAttributes* aAttributes) { + if (!aAttributes) { + // This can happen if an Accessible dies in the content process, but the + // parent hasn't been udpated yet. + return nullptr; + } + + AtkAttributeSet* atkAttributeSet = nullptr; + + for (auto iter : *aAttributes) { + AtkAttribute* atkAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute)); + nsAutoString value; + // We set atkAttr->name directly for each case. For the value, we set the + // value string for each case. atkAttr->value is set at the end based on the + // value string. + + // Set atkAttr->name to a specific ATK attribute name. + auto atkName = [&atkAttr](AtkTextAttribute aAttrNum) { + atkAttr->name = g_strdup(sAtkTextAttrNames[aAttrNum]); + }; + // Set value to a formatted ATK color value. + auto colorValue = [&iter, &value] { + // The format of the atk attribute is r,g,b and the gecko one is + // rgb(r, g, b). + auto color = iter.Value(); + MOZ_ASSERT(color); + value.AppendInt(NS_GET_R(color->mValue)); + value.Append(','); + value.AppendInt(NS_GET_G(color->mValue)); + value.Append(','); + value.AppendInt(NS_GET_B(color->mValue)); + }; + + nsAtom* name = iter.Name(); + if (name == nsGkAtoms::color) { + atkName(ATK_TEXT_ATTR_FG_COLOR); + colorValue(); + } else if (name == nsGkAtoms::backgroundColor) { + atkName(ATK_TEXT_ATTR_BG_COLOR); + colorValue(); + } else if (name == nsGkAtoms::font_family) { + atkName(ATK_TEXT_ATTR_FAMILY_NAME); + iter.ValueAsString(value); + } else if (name == nsGkAtoms::font_size) { + atkName(ATK_TEXT_ATTR_SIZE); + // ATK wants the number of points without pt at the end. + auto fontSize = iter.Value(); + MOZ_ASSERT(fontSize); + value.AppendInt(fontSize->mValue); + } else if (name == nsGkAtoms::fontWeight) { + atkName(ATK_TEXT_ATTR_WEIGHT); + iter.ValueAsString(value); + } else if (name == nsGkAtoms::invalid) { + atkName(ATK_TEXT_ATTR_INVALID); + iter.ValueAsString(value); + } else { + nsAutoString nameStr; + iter.NameAsString(nameStr); + atkAttr->name = g_strdup(NS_ConvertUTF16toUTF8(nameStr).get()); + iter.ValueAsString(value); + } + + atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get()); + atkAttributeSet = g_slist_prepend(atkAttributeSet, atkAttr); + } + + // libatk-adaptor will free it + return atkAttributeSet; +} + +extern "C" { + +static gchar* getTextCB(AtkText* aText, gint aStartOffset, gint aEndOffset) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc || !acc->IsTextRole()) { + return nullptr; + } + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text) { + return nullptr; + } + return DOMtoATK::NewATKString(text, aStartOffset, aEndOffset); +} + +static gint getCharacterCountCB(AtkText* aText); + +// Note: this does not support magic offsets, which is fine for its callers +// which do not implement any. +static gchar* getCharTextAtOffset(AtkText* aText, gint aOffset, + gint* aStartOffset, gint* aEndOffset) { + gint end = aOffset + 1; + gint count = getCharacterCountCB(aText); + + if (aOffset > count) { + aOffset = count; + } + if (end > count) { + end = count; + } + if (aOffset < 0) { + aOffset = 0; + } + if (end < 0) { + end = 0; + } + *aStartOffset = aOffset; + *aEndOffset = end; + + return getTextCB(aText, aOffset, end); +} + +static gchar* getTextAfterOffsetCB(AtkText* aText, gint aOffset, + AtkTextBoundary aBoundaryType, + gint* aStartOffset, gint* aEndOffset) { + if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) { + return getCharTextAtOffset(aText, aOffset + 1, aStartOffset, aEndOffset); + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + nsAutoString autoStr; + int32_t startOffset = 0, endOffset = 0; + text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, + autoStr); + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + // libspi will free it. + return DOMtoATK::Convert(autoStr); +} + +static gchar* getTextAtOffsetCB(AtkText* aText, gint aOffset, + AtkTextBoundary aBoundaryType, + gint* aStartOffset, gint* aEndOffset) { + if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) { + return getCharTextAtOffset(aText, aOffset, aStartOffset, aEndOffset); + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + nsAutoString autoStr; + int32_t startOffset = 0, endOffset = 0; + text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr); + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + // libspi will free it. + return DOMtoATK::Convert(autoStr); +} + +static gunichar getCharacterAtOffsetCB(AtkText* aText, gint aOffset) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return 0; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (text) { + return DOMtoATK::ATKCharacter(text, aOffset); + } + + return 0; +} + +static gchar* getTextBeforeOffsetCB(AtkText* aText, gint aOffset, + AtkTextBoundary aBoundaryType, + gint* aStartOffset, gint* aEndOffset) { + if (aBoundaryType == ATK_TEXT_BOUNDARY_CHAR) { + return getCharTextAtOffset(aText, aOffset - 1, aStartOffset, aEndOffset); + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + nsAutoString autoStr; + int32_t startOffset = 0, endOffset = 0; + text->TextBeforeOffset(aOffset, aBoundaryType, &startOffset, &endOffset, + autoStr); + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + // libspi will free it. + return DOMtoATK::Convert(autoStr); +} + +static gint getCaretOffsetCB(AtkText* aText) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return -1; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return -1; + } + + return static_cast(text->CaretOffset()); +} + +static AtkAttributeSet* getRunAttributesCB(AtkText* aText, gint aOffset, + gint* aStartOffset, + gint* aEndOffset) { + *aStartOffset = -1; + *aEndOffset = -1; + int32_t startOffset = 0, endOffset = 0; + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + RefPtr attributes = + text->TextAttributes(false, aOffset, &startOffset, &endOffset); + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + return ConvertToAtkTextAttributeSet(attributes); +} + +static AtkAttributeSet* getDefaultAttributesCB(AtkText* aText) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + RefPtr attributes = text->DefaultTextAttributes(); + return ConvertToAtkTextAttributeSet(attributes); +} + +static void getCharacterExtentsCB(AtkText* aText, gint aOffset, gint* aX, + gint* aY, gint* aWidth, gint* aHeight, + AtkCoordType aCoords) { + if (!aX || !aY || !aWidth || !aHeight) { + return; + } + *aX = *aY = *aWidth = *aHeight = -1; + + uint32_t geckoCoordType; + if (aCoords == ATK_XY_SCREEN) { + geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; + } else { + geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return; + } + + LayoutDeviceIntRect rect = text->CharBounds(aOffset, geckoCoordType); + + *aX = rect.x; + *aY = rect.y; + *aWidth = rect.width; + *aHeight = rect.height; +} + +static void getRangeExtentsCB(AtkText* aText, gint aStartOffset, + gint aEndOffset, AtkCoordType aCoords, + AtkTextRectangle* aRect) { + if (!aRect) { + return; + } + aRect->x = aRect->y = aRect->width = aRect->height = -1; + + uint32_t geckoCoordType; + if (aCoords == ATK_XY_SCREEN) { + geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE; + } else { + geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE; + } + + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return; + } + + LayoutDeviceIntRect rect = + text->TextBounds(aStartOffset, aEndOffset, geckoCoordType); + + aRect->x = rect.x; + aRect->y = rect.y; + aRect->width = rect.width; + aRect->height = rect.height; +} + +static gint getCharacterCountCB(AtkText* aText) { + if (Accessible* acc = GetInternalObj(ATK_OBJECT(aText))) { + if (HyperTextAccessibleBase* text = acc->AsHyperTextBase()) { + return static_cast(text->CharacterCount()); + } + } + return 0; +} + +static gint getOffsetAtPointCB(AtkText* aText, gint aX, gint aY, + AtkCoordType aCoords) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return -1; + } + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return -1; + } + return static_cast(text->OffsetAtPoint( + aX, aY, + (aCoords == ATK_XY_SCREEN + ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE + : nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE))); +} + +static gint getTextSelectionCountCB(AtkText* aText) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return 0; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return 0; + } + + return text->SelectionCount(); +} + +static gchar* getTextSelectionCB(AtkText* aText, gint aSelectionNum, + gint* aStartOffset, gint* aEndOffset) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return nullptr; + } + + int32_t startOffset = 0, endOffset = 0; + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return nullptr; + } + + text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset); + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + return getTextCB(aText, *aStartOffset, *aEndOffset); +} + +// set methods +static gboolean addTextSelectionCB(AtkText* aText, gint aStartOffset, + gint aEndOffset) { + AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); + if (accWrap) { + HyperTextAccessible* text = accWrap->AsHyperText(); + if (!text || !text->IsTextRole()) { + return FALSE; + } + + return text->AddToSelection(aStartOffset, aEndOffset); + } + if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { + return proxy->AddToSelection(aStartOffset, aEndOffset); + } + + return FALSE; +} + +static gboolean removeTextSelectionCB(AtkText* aText, gint aSelectionNum) { + AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText)); + if (accWrap) { + HyperTextAccessible* text = accWrap->AsHyperText(); + if (!text || !text->IsTextRole()) { + return FALSE; + } + + return text->RemoveFromSelection(aSelectionNum); + } + if (RemoteAccessible* proxy = GetProxy(ATK_OBJECT(aText))) { + return proxy->RemoveFromSelection(aSelectionNum); + } + + return FALSE; +} + +static gboolean setTextSelectionCB(AtkText* aText, gint aSelectionNum, + gint aStartOffset, gint aEndOffset) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc || !acc->IsTextRole()) { + return FALSE; + } + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text) { + return FALSE; + } + return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset); +} + +static gboolean setCaretOffsetCB(AtkText* aText, gint aOffset) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return FALSE; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text || !acc->IsTextRole()) { + return FALSE; + } + + text->SetCaretOffset(aOffset); + return TRUE; +} + +static gboolean scrollSubstringToCB(AtkText* aText, gint aStartOffset, + gint aEndOffset, AtkScrollType aType) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return FALSE; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text) { + return FALSE; + } + + text->ScrollSubstringTo(aStartOffset, aEndOffset, aType); + + return TRUE; +} + +static gboolean scrollSubstringToPointCB(AtkText* aText, gint aStartOffset, + gint aEndOffset, AtkCoordType aCoords, + gint aX, gint aY) { + Accessible* acc = GetInternalObj(ATK_OBJECT(aText)); + if (!acc) { + return FALSE; + } + + HyperTextAccessibleBase* text = acc->AsHyperTextBase(); + if (!text) { + return FALSE; + } + + text->ScrollSubstringToPoint(aStartOffset, aEndOffset, aCoords, aX, aY); + return TRUE; +} +} + +void textInterfaceInitCB(AtkTextIface* aIface) { + NS_ASSERTION(aIface, "Invalid aIface"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_text = getTextCB; + aIface->get_text_after_offset = getTextAfterOffsetCB; + aIface->get_text_at_offset = getTextAtOffsetCB; + aIface->get_character_at_offset = getCharacterAtOffsetCB; + aIface->get_text_before_offset = getTextBeforeOffsetCB; + aIface->get_caret_offset = getCaretOffsetCB; + aIface->get_run_attributes = getRunAttributesCB; + aIface->get_default_attributes = getDefaultAttributesCB; + aIface->get_character_extents = getCharacterExtentsCB; + aIface->get_range_extents = getRangeExtentsCB; + aIface->get_character_count = getCharacterCountCB; + aIface->get_offset_at_point = getOffsetAtPointCB; + aIface->get_n_selections = getTextSelectionCountCB; + aIface->get_selection = getTextSelectionCB; + + // set methods + aIface->add_selection = addTextSelectionCB; + aIface->remove_selection = removeTextSelectionCB; + aIface->set_selection = setTextSelectionCB; + aIface->set_caret_offset = setCaretOffsetCB; + + if (IsAtkVersionAtLeast(2, 32)) { + aIface->scroll_substring_to = scrollSubstringToCB; + aIface->scroll_substring_to_point = scrollSubstringToPointCB; + } + + // Cache the string values of the atk text attribute names. + for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++) { + sAtkTextAttrNames[i] = + atk_text_attribute_get_name(static_cast(i)); + } +} diff --git a/accessible/atk/nsMaiInterfaceValue.cpp b/accessible/atk/nsMaiInterfaceValue.cpp new file mode 100644 index 0000000000..05a7da171e --- /dev/null +++ b/accessible/atk/nsMaiInterfaceValue.cpp @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "InterfaceInitFuncs.h" + +#include "AccessibleWrap.h" +#include "nsMai.h" +#include "RemoteAccessible.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/Likely.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +extern "C" { + +static void getCurrentValueCB(AtkValue* obj, GValue* value) { + Accessible* acc = GetInternalObj(ATK_OBJECT(obj)); + if (!acc) { + return; + } + + memset(value, 0, sizeof(GValue)); + double accValue = acc->CurValue(); + if (std::isnan(accValue)) return; + + g_value_init(value, G_TYPE_DOUBLE); + g_value_set_double(value, accValue); +} + +static void getMaximumValueCB(AtkValue* obj, GValue* value) { + Accessible* acc = GetInternalObj(ATK_OBJECT(obj)); + if (!acc) { + return; + } + + memset(value, 0, sizeof(GValue)); + double accValue = acc->MaxValue(); + if (std::isnan(accValue)) return; + + g_value_init(value, G_TYPE_DOUBLE); + g_value_set_double(value, accValue); +} + +static void getMinimumValueCB(AtkValue* obj, GValue* value) { + Accessible* acc = GetInternalObj(ATK_OBJECT(obj)); + if (!acc) { + return; + } + + memset(value, 0, sizeof(GValue)); + double accValue = acc->MinValue(); + if (std::isnan(accValue)) return; + + g_value_init(value, G_TYPE_DOUBLE); + g_value_set_double(value, accValue); +} + +static void getMinimumIncrementCB(AtkValue* obj, GValue* minimumIncrement) { + Accessible* acc = GetInternalObj(ATK_OBJECT(obj)); + if (!acc) { + return; + } + + memset(minimumIncrement, 0, sizeof(GValue)); + double accValue = acc->Step(); + if (std::isnan(accValue)) { + accValue = 0; // zero if the minimum increment is undefined + } + + g_value_init(minimumIncrement, G_TYPE_DOUBLE); + g_value_set_double(minimumIncrement, accValue); +} + +static gboolean setCurrentValueCB(AtkValue* obj, const GValue* value) { + Accessible* acc = GetInternalObj(ATK_OBJECT(obj)); + if (!acc) { + return false; + } + + double accValue = g_value_get_double(value); + return acc->SetCurValue(accValue); +} + +void valueInterfaceInitCB(AtkValueIface* aIface) { + NS_ASSERTION(aIface, "Invalid aIface"); + if (MOZ_UNLIKELY(!aIface)) return; + + aIface->get_current_value = getCurrentValueCB; + aIface->get_maximum_value = getMaximumValueCB; + aIface->get_minimum_value = getMinimumValueCB; + aIface->get_minimum_increment = getMinimumIncrementCB; + aIface->set_current_value = setCurrentValueCB; +} +} diff --git a/accessible/atk/nsStateMap.h b/accessible/atk/nsStateMap.h new file mode 100644 index 0000000000..3587ccd6cf --- /dev/null +++ b/accessible/atk/nsStateMap.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include "AccessibleWrap.h" + +#include + +// clang-format off +/****************************************************************************** +The following accessible states aren't translated, just ignored: + STATE_READONLY: Supported indirectly via EXT_STATE_EDITABLE + STATE_HOTTRACKED: No ATK equivalent. No known use case. + The nsIAccessible state is not currently supported. + STATE_FLOATING: No ATK equivalent. No known use case. + The nsIAccessible state is not currently supported. + STATE_MOVEABLE: No ATK equivalent. No known use case. + The nsIAccessible state is not currently supported. + STATE_SELFVOICING: No ATK equivalent -- the object has self-TTS. + The nsIAccessible state is not currently supported. + STATE_LINKED: The object is formatted as a hyperlink. Supported via ATK_ROLE_LINK. + STATE_EXTSELECTABLE: Indicates that an object extends its selection. + This is supported via STATE_MULTISELECTABLE. + STATE_PROTECTED: The object is a password-protected edit control. + Supported via ATK_ROLE_PASSWORD_TEXT + STATE_PINNED: The object is pinned, usually indicating it is fixed in + place and has permanence. No ATK equivalent. The + accessible state is not currently supported. + +The following ATK states are not supported: + ATK_STATE_ARMED: No clear use case, used briefly when button is activated + ATK_STATE_HAS_TOOLTIP: No clear use case, no IA2 equivalent + ATK_STATE_ICONIFIED: Mozilla does not have elements which are collapsable into icons + ATK_STATE_TRUNCATED: No clear use case. Indicates that an object's onscreen content is truncated, + e.g. a text value in a spreadsheet cell. No IA2 state. +******************************************************************************/ +// clang-format on + +enum EStateMapEntryType { + kMapDirectly, + kMapOpposite, // For example, UNAVAILABLE is the opposite of ENABLED + kNoStateChange, // Don't fire state change event +}; + +const AtkStateType kNone = ATK_STATE_INVALID; + +struct AtkStateMap { + AtkStateType atkState; + EStateMapEntryType stateMapEntryType; +}; + +// Map array from cross platform states to ATK states +static const AtkStateMap gAtkStateMap[] = + { + // Cross Platform States + // clang-format off + { kNone, kMapOpposite }, // states::UNAVAILABLE = 1 << 0 + { ATK_STATE_SELECTED, kMapDirectly }, // states::SELECTED = 1 << 1 + { ATK_STATE_FOCUSED, kMapDirectly }, // states::FOCUSED = 1 << 2 + { ATK_STATE_PRESSED, kMapDirectly }, // states::PRESSED = 1 << 3 + { ATK_STATE_CHECKED, kMapDirectly }, // states::CHECKED = 1 << 4 + { ATK_STATE_INDETERMINATE, kMapDirectly }, // states::MIXED = 1 << 5 + { kNone, kMapDirectly }, // states::READONLY = 1 << 6 + { kNone, kMapDirectly }, // states::HOTTRACKED = 1 << 7 + { ATK_STATE_DEFAULT, kMapDirectly }, // states::DEFAULT = 1 << 8 + { ATK_STATE_EXPANDED, kMapDirectly }, // states::EXPANDED = 1 << 9 + { kNone, kNoStateChange }, // states::COLLAPSED = 1 << 10 + { ATK_STATE_BUSY, kMapDirectly }, // states::BUSY = 1 << 11 + { kNone, kMapDirectly }, // states::FLOATING = 1 << 12 + { ATK_STATE_CHECKABLE, kMapDirectly }, // states::CHECKABLE = 1 << 13 + { ATK_STATE_ANIMATED, kMapDirectly }, // states::ANIMATED = 1 << 14 + { ATK_STATE_VISIBLE, kMapOpposite }, // states::INVISIBLE = 1 << 15 + { ATK_STATE_SHOWING, kMapOpposite }, // states::OFFSCREEN = 1 << 16 + { ATK_STATE_RESIZABLE, kMapDirectly }, // states::SIZEABLE = 1 << 17 + { kNone, kMapDirectly }, // states::MOVEABLE = 1 << 18 + { kNone, kMapDirectly }, // states::SELFVOICING = 1 << 19 + { ATK_STATE_FOCUSABLE, kMapDirectly }, // states::FOCUSABLE = 1 << 20 + { ATK_STATE_SELECTABLE, kMapDirectly }, // states::SELECTABLE = 1 << 21 + { kNone, kMapDirectly }, // states::LINKED = 1 << 22 + { ATK_STATE_VISITED, kMapDirectly }, // states::TRAVERSED = 1 << 23 + { ATK_STATE_MULTISELECTABLE, kMapDirectly }, // states::MULTISELECTABLE = 1 << 24 + { kNone, kMapDirectly }, // states::EXTSELECTABLE = 1 << 25 + { ATK_STATE_REQUIRED, kMapDirectly }, // states::STATE_REQUIRED = 1 << 26 + { kNone, kMapDirectly }, // states::ALERT_MEDIUM = 1 << 27 + { ATK_STATE_INVALID_ENTRY, kMapDirectly }, // states::INVALID = 1 << 28 + { kNone, kMapDirectly }, // states::PROTECTED = 1 << 29 + { ATK_STATE_HAS_POPUP, kMapDirectly }, // states::HASPOPUP = 1 << 30 + { ATK_STATE_SUPPORTS_AUTOCOMPLETION, kMapDirectly }, // states::SUPPORTS_AUTOCOMPLETION = 1 << 31 + { ATK_STATE_DEFUNCT, kMapDirectly }, // states::DEFUNCT = 1 << 32 + { ATK_STATE_SELECTABLE_TEXT, kMapDirectly }, // states::SELECTABLE_TEXT = 1 << 33 + { ATK_STATE_EDITABLE, kMapDirectly }, // states::EDITABLE = 1 << 34 + { ATK_STATE_ACTIVE, kMapDirectly }, // states::ACTIVE = 1 << 35 + { ATK_STATE_MODAL, kMapDirectly }, // states::MODAL = 1 << 36 + { ATK_STATE_MULTI_LINE, kMapDirectly }, // states::MULTI_LINE = 1 << 37 + { ATK_STATE_HORIZONTAL, kMapDirectly }, // states::HORIZONTAL = 1 << 38 + { ATK_STATE_OPAQUE, kMapDirectly }, // states::OPAQUE = 1 << 39 + { ATK_STATE_SINGLE_LINE, kMapDirectly }, // states::SINGLE_LINE = 1 << 40 + { ATK_STATE_TRANSIENT, kMapDirectly }, // states::TRANSIENT = 1 << 41 + { ATK_STATE_VERTICAL, kMapDirectly }, // states::VERTICAL = 1 << 42 + { ATK_STATE_STALE, kMapDirectly }, // states::STALE = 1 << 43 + { ATK_STATE_ENABLED, kMapDirectly }, // states::ENABLED = 1 << 44 + { ATK_STATE_SENSITIVE, kMapDirectly }, // states::SENSITIVE = 1 << 45 + { ATK_STATE_EXPANDABLE, kMapDirectly }, // states::EXPANDABLE = 1 << 46 + { kNone, kMapDirectly }, // states::PINNED = 1 << 47 + { ATK_STATE_ACTIVE, kMapDirectly } // states::CURRENT = 1 << 48 + // clang-format on +}; + +static const auto gAtkStateMapLen = std::extent::value; + +static_assert(((uint64_t)0x1) << (gAtkStateMapLen - 1) == + mozilla::a11y::states::LAST_ENTRY, + "ATK states map is out of sync with internal states"); diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp new file mode 100644 index 0000000000..01cc5d0417 --- /dev/null +++ b/accessible/base/ARIAMap.cpp @@ -0,0 +1,1687 @@ +/* -*- 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 "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". + * + * 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, + 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 + }, + { // 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::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 + }, + { // 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::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 + }, + { // 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 }, + {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(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; +} diff --git a/accessible/base/ARIAMap.h b/accessible/base/ARIAMap.h new file mode 100644 index 0000000000..30cc1f0814 --- /dev/null +++ b/accessible/base/ARIAMap.h @@ -0,0 +1,335 @@ +/* -*- 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/. */ + +#ifndef mozilla_a11y_aria_ARIAMap_h_ +#define mozilla_a11y_aria_ARIAMap_h_ + +#include "ARIAStateMap.h" +#include "mozilla/a11y/AccTypes.h" +#include "mozilla/a11y/Role.h" + +#include "nsAtom.h" +#include "nsIContent.h" +#include "nsTHashSet.h" + +class nsINode; + +namespace mozilla::dom { +class Element; +} + +//////////////////////////////////////////////////////////////////////////////// +// Value constants + +/** + * Used to define if role requires to expose Value interface. + */ +enum EValueRule { + /** + * Value interface isn't exposed. + */ + eNoValue, + + /** + * Value interface is implemented, supports value, min and max from + * aria-valuenow, aria-valuemin and aria-valuemax. + */ + eHasValueMinMax, + + /** + * Value interface is implemented, but only if the element is focusable. + * For instance, in ARIA 1.1 the ability for authors to create adjustable + * splitters was provided by supporting the value interface on separators + * that are focusable. Non-focusable separators expose no value information. + */ + eHasValueMinMaxIfFocusable +}; + +//////////////////////////////////////////////////////////////////////////////// +// Action constants + +/** + * Used to define if the role requires to expose action. + */ +enum EActionRule { + eNoAction, + eActivateAction, + eClickAction, + ePressAction, + eCheckUncheckAction, + eExpandAction, + eJumpAction, + eOpenCloseAction, + eSelectAction, + eSortAction, + eSwitchAction +}; + +//////////////////////////////////////////////////////////////////////////////// +// Live region constants + +/** + * Used to define if role exposes default value of aria-live attribute. + */ +enum ELiveAttrRule { + eNoLiveAttr, + eOffLiveAttr, + ePoliteLiveAttr, + eAssertiveLiveAttr +}; + +//////////////////////////////////////////////////////////////////////////////// +// Role constants + +/** + * ARIA role overrides role from native markup. + */ +const bool kUseMapRole = true; + +/** + * ARIA role doesn't override the role from native markup. + */ +const bool kUseNativeRole = false; + +//////////////////////////////////////////////////////////////////////////////// +// ARIA attribute characteristic masks + +/** + * This mask indicates the attribute should not be exposed as an object + * attribute via the catch-all logic in Accessible::Attributes(). + * This means it either isn't mean't to be exposed as an object attribute, or + * that it should, but is already handled in other code. + */ +const uint8_t ATTR_BYPASSOBJ = 0x1 << 0; +const uint8_t ATTR_BYPASSOBJ_IF_FALSE = 0x1 << 1; + +/** + * This mask indicates the attribute is expected to have an NMTOKEN or bool + * value. (See for example usage in Accessible::Attributes()) + */ +const uint8_t ATTR_VALTOKEN = 0x1 << 2; + +/** + * Indicate the attribute is global state or property (refer to + * http://www.w3.org/TR/wai-aria/states_and_properties#global_states). + */ +const uint8_t ATTR_GLOBAL = 0x1 << 3; + +/** + * Indicates that the attribute should have an integer value. + */ +const uint8_t ATTR_VALINT = 0x1 << 4; + +//////////////////////////////////////////////////////////////////////////////// +// State map entry + +/** + * Used in nsRoleMapEntry.state if no nsIAccessibleStates are automatic for + * a given role. + */ +#define kNoReqStates 0 + +//////////////////////////////////////////////////////////////////////////////// +// Role map entry + +/** + * For each ARIA role, this maps the nsIAccessible information. + */ +struct nsRoleMapEntry { + /** + * Return true if matches to the given ARIA role. + */ + bool Is(nsAtom* aARIARole) const { return roleAtom == aARIARole; } + + /** + * Return true if ARIA role has the given accessible type. + */ + bool IsOfType(mozilla::a11y::AccGenericType aType) const { + return accTypes & aType; + } + + /** + * Return ARIA role. + */ + const nsDependentAtomString ARIARoleString() const { + return nsDependentAtomString(roleAtom); + } + + // ARIA role: string representation such as "button" + nsStaticAtom* const roleAtom; + + // Role mapping rule: maps to enum Role + mozilla::a11y::role role; + + // Role rule: whether to use mapped role or native semantics + bool roleRule; + + // Value mapping rule: how to compute accessible value + EValueRule valueRule; + + // Action mapping rule, how to expose accessible action + EActionRule actionRule; + + // 'live' and 'container-live' object attributes mapping rule: how to expose + // these object attributes if ARIA 'live' attribute is missed. + ELiveAttrRule liveAttRule; + + // LocalAccessible types this role belongs to. + uint32_t accTypes; + + // Automatic state mapping rule: always include in states + uint64_t state; // or kNoReqStates if no default state for this role + + // ARIA properties supported for this role (in other words, the aria-foo + // attribute to accessible states mapping rules). + // Currently you cannot have unlimited mappings, because + // a variable sized array would not allow the use of + // C++'s struct initialization feature. + mozilla::a11y::aria::EStateRule attributeMap1; + mozilla::a11y::aria::EStateRule attributeMap2; + mozilla::a11y::aria::EStateRule attributeMap3; + mozilla::a11y::aria::EStateRule attributeMap4; +}; + +//////////////////////////////////////////////////////////////////////////////// +// ARIA map + +/** + * These provide the mappings for WAI-ARIA roles, states and properties using + * the structs defined in this file and ARIAStateMap files. + */ +namespace mozilla { +namespace a11y { +class AccAttributes; + +namespace aria { + +/** + * Empty role map entry. Used by accessibility service to create an accessible + * if the accessible can't use role of used accessible class. For example, + * it is used for table cells that aren't contained by table. + */ +extern nsRoleMapEntry gEmptyRoleMap; + +/** + * Constants for the role map entry index to indicate that the role map entry + * isn't in sWAIRoleMaps, but rather is a special entry: nullptr, + * gEmptyRoleMap, and sLandmarkRoleMap + */ +const uint8_t NO_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 2; +const uint8_t EMPTY_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 1; +const uint8_t LANDMARK_ROLE_MAP_ENTRY_INDEX = UINT8_MAX; + +/** + * Get the role map entry for a given DOM node. This will use the first + * ARIA role if the role attribute provides a space delimited list of roles. + * + * @param aEl [in] the DOM node to get the role map entry for + * @return a pointer to the role map entry for the ARIA role, or nullptr + * if none + */ +const nsRoleMapEntry* GetRoleMap(dom::Element* aEl); + +/** + * Get the role map entry pointer's index for a given DOM node. This will use + * the first ARIA role if the role attribute provides a space delimited list of + * roles. + * + * @param aEl [in] the DOM node to get the role map entry for + * @return the index of the pointer to the role map entry for the ARIA + * role, or NO_ROLE_MAP_ENTRY_INDEX if none + */ +uint8_t GetRoleMapIndex(dom::Element* aEl); + +/** + * Get the role map entry pointer for a given role map entry index. + * + * @param aRoleMapIndex [in] the role map index to get the role map entry + * pointer for + * @return a pointer to the role map entry for the ARIA role, + * or nullptr, if none + */ +const nsRoleMapEntry* GetRoleMapFromIndex(uint8_t aRoleMapIndex); + +/** + * Get the role map entry index for a given role map entry pointer. If the role + * map entry is within sWAIRoleMaps, return the index within that array, + * otherwise return one of the special index constants listed above. + * + * @param aRoleMap [in] the role map entry pointer to get the index for + * @return the index of the pointer to the role map entry, or + * NO_ROLE_MAP_ENTRY_INDEX if none + */ +uint8_t GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMap); + +/** + * Determine whether a role map entry index is valid. + */ +bool IsRoleMapIndexValid(uint8_t aRoleMapIndex); + +/** + * Return accessible state from ARIA universal states applied to the given + * element. + */ +uint64_t UniversalStatesFor(dom::Element* aElement); + +/** + * Get the ARIA attribute characteristics for a given ARIA attribute. + * + * @param aAtom ARIA attribute + * @return A bitflag representing the attribute characteristics + * (see above for possible bit masks, prefixed "ATTR_") + */ +uint8_t AttrCharacteristicsFor(nsAtom* aAtom); + +/** + * Return true if the element has defined aria-hidden. + */ +bool HasDefinedARIAHidden(nsIContent* aContent); + +/** + * Represents a simple enumerator for iterating through ARIA attributes + * exposed as object attributes on a given accessible. + */ +class AttrIterator { + public: + explicit AttrIterator(nsIContent* aContent); + + bool Next(); + + nsAtom* AttrName() const; + + void AttrValue(nsAString& aAttrValue) const; + + /** + * Expose this ARIA attribute in a specified AccAttributes. The appropriate + * type will be used for the attribute; e.g. an atom for a token value. + */ + bool ExposeAttr(AccAttributes* aTargetAttrs) const; + + private: + AttrIterator() = delete; + AttrIterator(const AttrIterator&) = delete; + AttrIterator& operator=(const AttrIterator&) = delete; + + dom::Element* mElement; + + bool mIteratingDefaults; + nsTHashSet> mOverriddenAttrs; + + const AttrArray* mAttrs; + uint32_t mAttrIdx; + uint32_t mAttrCount; + RefPtr mAttrAtom; + uint8_t mAttrCharacteristics; +}; + +} // namespace aria +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/base/ARIAStateMap.cpp b/accessible/base/ARIAStateMap.cpp new file mode 100644 index 0000000000..6bf20cf1cc --- /dev/null +++ b/accessible/base/ARIAStateMap.cpp @@ -0,0 +1,334 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ARIAMap.h" +#include "nsAccUtils.h" +#include "States.h" + +#include "mozilla/dom/Element.h" + +using namespace mozilla; +using namespace mozilla::a11y; +using namespace mozilla::a11y::aria; + +/** + * Used to store state map rule data for ARIA attribute of enum type. + */ +struct EnumTypeData { + // ARIA attribute name. + nsStaticAtom* const mAttrName; + + // States if the attribute value is matched to the enum value. Used as + // Element::AttrValuesArray, last item must be nullptr. + nsStaticAtom* const mValues[4]; + + // States applied if corresponding enum values are matched. + const uint64_t mStates[3]; + + // States to clear in case of match. + const uint64_t mClearState; +}; + +enum ETokenType { + eBoolType = 0, + eMixedType = 1, // can take 'mixed' value + eDefinedIfAbsent = 2 // permanent and false state are applied if absent +}; + +/** + * Used to store state map rule data for ARIA attribute of token type (including + * mixed value). + */ +struct TokenTypeData { + TokenTypeData(nsAtom* aAttrName, uint32_t aType, uint64_t aPermanentState, + uint64_t aTrueState, uint64_t aFalseState = 0) + : mAttrName(aAttrName), + mType(aType), + mPermanentState(aPermanentState), + mTrueState(aTrueState), + mFalseState(aFalseState) {} + + // ARIA attribute name. + nsAtom* const mAttrName; + + // Type. + const uint32_t mType; + + // State applied if the attribute is defined or mType doesn't have + // eDefinedIfAbsent flag set. + const uint64_t mPermanentState; + + // States applied if the attribute value is true/false. + const uint64_t mTrueState; + const uint64_t mFalseState; +}; + +/** + * Map enum type attribute value to accessible state. + */ +static void MapEnumType(dom::Element* aElement, uint64_t* aState, + const EnumTypeData& aData); + +/** + * Map token type attribute value to states. + */ +static void MapTokenType(dom::Element* aContent, uint64_t* aState, + const TokenTypeData& aData); + +bool aria::MapToState(EStateRule aRule, dom::Element* aElement, + uint64_t* aState) { + switch (aRule) { + case eARIAAutoComplete: { + static const EnumTypeData data = { + nsGkAtoms::aria_autocomplete, + {nsGkAtoms::inlinevalue, nsGkAtoms::list_, nsGkAtoms::both, nullptr}, + {states::SUPPORTS_AUTOCOMPLETION, + states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION, + states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION}, + 0}; + + MapEnumType(aElement, aState, data); + return true; + } + + case eARIABusy: { + static const EnumTypeData data = { + nsGkAtoms::aria_busy, + {nsGkAtoms::_true, nsGkAtoms::error, nullptr}, + {states::BUSY, states::INVALID}, + 0}; + + MapEnumType(aElement, aState, data); + return true; + } + + case eARIACheckableBool: { + static const TokenTypeData data(nsGkAtoms::aria_checked, + eBoolType | eDefinedIfAbsent, + states::CHECKABLE, states::CHECKED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIACheckableMixed: { + static const TokenTypeData data(nsGkAtoms::aria_checked, + eMixedType | eDefinedIfAbsent, + states::CHECKABLE, states::CHECKED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIACheckedMixed: { + static const TokenTypeData data(nsGkAtoms::aria_checked, eMixedType, + states::CHECKABLE, states::CHECKED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIACurrent: { + static const TokenTypeData data(nsGkAtoms::aria_current, eBoolType, 0, + states::CURRENT); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIADisabled: { + static const TokenTypeData data(nsGkAtoms::aria_disabled, eBoolType, 0, + states::UNAVAILABLE); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAExpanded: { + static const TokenTypeData data(nsGkAtoms::aria_expanded, eBoolType, 0, + states::EXPANDED, states::COLLAPSED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAHasPopup: { + static const TokenTypeData data(nsGkAtoms::aria_haspopup, eBoolType, 0, + states::HASPOPUP); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAInvalid: { + static const TokenTypeData data(nsGkAtoms::aria_invalid, eBoolType, 0, + states::INVALID); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAModal: { + static const TokenTypeData data(nsGkAtoms::aria_modal, eBoolType, 0, + states::MODAL); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAMultiline: { + static const TokenTypeData data(nsGkAtoms::aria_multiline, + eBoolType | eDefinedIfAbsent, 0, + states::MULTI_LINE, states::SINGLE_LINE); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAMultiSelectable: { + static const TokenTypeData data( + nsGkAtoms::aria_multiselectable, eBoolType, 0, + states::MULTISELECTABLE | states::EXTSELECTABLE); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAOrientation: { + static const EnumTypeData data = { + nsGkAtoms::aria_orientation, + {nsGkAtoms::horizontal, nsGkAtoms::vertical, nullptr}, + {states::HORIZONTAL, states::VERTICAL}, + states::HORIZONTAL | states::VERTICAL}; + + MapEnumType(aElement, aState, data); + return true; + } + + case eARIAPressed: { + static const TokenTypeData data(nsGkAtoms::aria_pressed, eMixedType, 0, + states::PRESSED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAReadonly: { + static const TokenTypeData data(nsGkAtoms::aria_readonly, eBoolType, 0, + states::READONLY); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIAReadonlyOrEditable: { + static const TokenTypeData data(nsGkAtoms::aria_readonly, + eBoolType | eDefinedIfAbsent, 0, + states::READONLY, states::EDITABLE); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIARequired: { + static const TokenTypeData data(nsGkAtoms::aria_required, eBoolType, 0, + states::REQUIRED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIASelectable: { + static const TokenTypeData data(nsGkAtoms::aria_selected, + eBoolType | eDefinedIfAbsent, + states::SELECTABLE, states::SELECTED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eARIASelectableIfDefined: { + static const TokenTypeData data(nsGkAtoms::aria_selected, eBoolType, + states::SELECTABLE, states::SELECTED); + + MapTokenType(aElement, aState, data); + return true; + } + + case eReadonlyUntilEditable: { + if (!(*aState & states::EDITABLE)) *aState |= states::READONLY; + + return true; + } + + case eIndeterminateIfNoValue: { + if (!nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuenow) && + !nsAccUtils::HasARIAAttr(aElement, nsGkAtoms::aria_valuetext)) { + *aState |= states::MIXED; + } + + return true; + } + + case eFocusableUntilDisabled: { + if (!nsAccUtils::HasDefinedARIAToken(aElement, + nsGkAtoms::aria_disabled) || + nsAccUtils::ARIAAttrValueIs(aElement, nsGkAtoms::aria_disabled, + nsGkAtoms::_false, eCaseMatters)) { + *aState |= states::FOCUSABLE; + } + + return true; + } + + default: + return false; + } +} + +static void MapEnumType(dom::Element* aElement, uint64_t* aState, + const EnumTypeData& aData) { + switch (nsAccUtils::FindARIAAttrValueIn(aElement, aData.mAttrName, + aData.mValues, eCaseMatters)) { + case 0: + *aState = (*aState & ~aData.mClearState) | aData.mStates[0]; + return; + case 1: + *aState = (*aState & ~aData.mClearState) | aData.mStates[1]; + return; + case 2: + *aState = (*aState & ~aData.mClearState) | aData.mStates[2]; + return; + } +} + +static void MapTokenType(dom::Element* aElement, uint64_t* aState, + const TokenTypeData& aData) { + if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) { + if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName, nsGkAtoms::mixed, + eCaseMatters)) { + if (aData.mType & eMixedType) { + *aState |= aData.mPermanentState | states::MIXED; + } else { // unsupported use of 'mixed' is an authoring error + *aState |= aData.mPermanentState | aData.mFalseState; + } + return; + } + + if (nsAccUtils::ARIAAttrValueIs(aElement, aData.mAttrName, + nsGkAtoms::_false, eCaseMatters)) { + *aState |= aData.mPermanentState | aData.mFalseState; + return; + } + + *aState |= aData.mPermanentState | aData.mTrueState; + return; + } + + if (aData.mType & eDefinedIfAbsent) { + *aState |= aData.mPermanentState | aData.mFalseState; + } +} diff --git a/accessible/base/ARIAStateMap.h b/accessible/base/ARIAStateMap.h new file mode 100644 index 0000000000..20490aa901 --- /dev/null +++ b/accessible/base/ARIAStateMap.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef _mozilla_a11y_aria_ARIAStateMap_h_ +#define _mozilla_a11y_aria_ARIAStateMap_h_ + +#include + +namespace mozilla { + +namespace dom { +class Element; +} + +namespace a11y { +namespace aria { + +/** + * List of the ARIA state mapping rules. + */ +enum EStateRule { + eARIANone, + eARIAAutoComplete, + eARIABusy, + eARIACheckableBool, + eARIACheckableMixed, + eARIACheckedMixed, + eARIACurrent, + eARIADisabled, + eARIAExpanded, + eARIAHasPopup, + eARIAInvalid, + eARIAModal, + eARIAMultiline, + eARIAMultiSelectable, + eARIAOrientation, + eARIAPressed, + eARIAReadonly, + eARIAReadonlyOrEditable, + eARIARequired, + eARIASelectable, + eARIASelectableIfDefined, + eReadonlyUntilEditable, + eIndeterminateIfNoValue, + eFocusableUntilDisabled +}; + +/** + * Expose the accessible states for the given element accordingly to state + * mapping rule. + * + * @param aRule [in] state mapping rule ID + * @param aElement [in] node of the accessible + * @param aState [in/out] accessible states + * @return true if state map rule ID is valid + */ +bool MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState); + +} // namespace aria +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/base/AccAttributes.cpp b/accessible/base/AccAttributes.cpp new file mode 100644 index 0000000000..4018f09074 --- /dev/null +++ b/accessible/base/AccAttributes.cpp @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 "AccAttributes.h" +#include "StyleInfo.h" +#include "mozilla/ToString.h" +#include "nsAtom.h" + +using namespace mozilla::a11y; + +bool AccAttributes::GetAttribute(nsAtom* aAttrName, + nsAString& aAttrValue) const { + if (auto value = mData.Lookup(aAttrName)) { + StringFromValueAndName(aAttrName, *value, aAttrValue); + return true; + } + + return false; +} + +void AccAttributes::StringFromValueAndName(nsAtom* aAttrName, + const AttrValueType& aValue, + nsAString& aValueString) { + aValueString.Truncate(); + + aValue.match( + [&aValueString](const bool& val) { + aValueString.Assign(val ? u"true" : u"false"); + }, + [&aValueString](const float& val) { + aValueString.AppendFloat(val * 100); + aValueString.Append(u"%"); + }, + [&aValueString](const double& val) { aValueString.AppendFloat(val); }, + [&aValueString](const int32_t& val) { aValueString.AppendInt(val); }, + [&aValueString](const RefPtr& val) { + val->ToString(aValueString); + }, + [&aValueString](const nsTArray& val) { + if (const size_t len = val.Length()) { + for (size_t i = 0; i < len - 1; i++) { + aValueString.AppendInt(val[i]); + aValueString.Append(u", "); + } + aValueString.AppendInt(val[len - 1]); + } else { + // The array is empty + NS_WARNING( + "Hmm, should we have used a DeleteEntry() for this instead?"); + aValueString.Append(u"[ ]"); + } + }, + [&aValueString](const CSSCoord& val) { + aValueString.AppendFloat(val); + aValueString.Append(u"px"); + }, + [&aValueString](const FontSize& val) { + aValueString.AppendInt(val.mValue); + aValueString.Append(u"pt"); + }, + [&aValueString](const Color& val) { + StyleInfo::FormatColor(val.mValue, aValueString); + }, + [&aValueString](const DeleteEntry& val) { + aValueString.Append(u"-delete-entry-"); + }, + [&aValueString](const UniquePtr& val) { + aValueString.Assign(*val); + }, + [&aValueString](const RefPtr& val) { + aValueString.Assign(u"AccAttributes{...}"); + }, + [&aValueString](const uint64_t& val) { aValueString.AppendInt(val); }, + [&aValueString](const UniquePtr& val) { + aValueString.Assign(u"AccGroupInfo{...}"); + }, + [&aValueString](const UniquePtr& val) { + aValueString.AppendPrintf("Matrix4x4=%s", ToString(*val).c_str()); + }, + [&aValueString](const nsTArray& val) { + if (const size_t len = val.Length()) { + for (size_t i = 0; i < len - 1; i++) { + aValueString.AppendInt(val[i]); + aValueString.Append(u", "); + } + aValueString.AppendInt(val[len - 1]); + } else { + // The array is empty + NS_WARNING( + "Hmm, should we have used a DeleteEntry() for this instead?"); + aValueString.Append(u"[ ]"); + } + }); +} + +void AccAttributes::Update(AccAttributes* aOther) { + for (auto iter = aOther->mData.Iter(); !iter.Done(); iter.Next()) { + if (iter.Data().is()) { + mData.Remove(iter.Key()); + } else { + mData.InsertOrUpdate(iter.Key(), std::move(iter.Data())); + } + iter.Remove(); + } +} + +bool AccAttributes::Equal(const AccAttributes* aOther) const { + if (Count() != aOther->Count()) { + return false; + } + for (auto iter = mData.ConstIter(); !iter.Done(); iter.Next()) { + const auto otherEntry = aOther->mData.Lookup(iter.Key()); + if (!otherEntry) { + return false; + } + if (iter.Data().is>()) { + // Because we store nsString in a UniquePtr, we must handle it specially + // so we compare the string and not the pointer. + if (!otherEntry->is>()) { + return false; + } + const auto& thisStr = iter.Data().as>(); + const auto& otherStr = otherEntry->as>(); + if (*thisStr != *otherStr) { + return false; + } + } else if (iter.Data() != otherEntry.Data()) { + return false; + } + } + return true; +} + +void AccAttributes::CopyTo(AccAttributes* aDest) const { + for (auto iter = mData.ConstIter(); !iter.Done(); iter.Next()) { + iter.Data().match( + [&iter, &aDest](const bool& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const float& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const double& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const int32_t& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const RefPtr& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [](const nsTArray& val) { + // We don't copy arrays. + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing an array"); + }, + [&iter, &aDest](const CSSCoord& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const FontSize& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [&iter, &aDest](const Color& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [](const DeleteEntry& val) { + // We don't copy DeleteEntry. + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing a DeleteEntry"); + }, + [&iter, &aDest](const UniquePtr& val) { + aDest->SetAttributeStringCopy(iter.Key(), *val); + }, + [](const RefPtr& val) { + // We don't copy nested AccAttributes. + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing an AccAttributes"); + }, + [&iter, &aDest](const uint64_t& val) { + aDest->mData.InsertOrUpdate(iter.Key(), AsVariant(val)); + }, + [](const UniquePtr& val) { + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing an AccGroupInfo"); + }, + [](const UniquePtr& val) { + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing a matrix"); + }, + [](const nsTArray& val) { + // We don't copy arrays. + MOZ_ASSERT_UNREACHABLE( + "Trying to copy an AccAttributes containing an array"); + }); + } +} + +#ifdef A11Y_LOG +void AccAttributes::DebugPrint(const char* aPrefix, + const AccAttributes& aAttributes) { + nsAutoString prettyString; + prettyString.AssignLiteral("{\n"); + for (const auto& iter : aAttributes) { + nsAutoString name; + iter.NameAsString(name); + + nsAutoString value; + iter.ValueAsString(value); + prettyString.AppendLiteral(" "); + prettyString.Append(name); + prettyString.AppendLiteral(": "); + prettyString.Append(value); + prettyString.AppendLiteral("\n"); + } + + prettyString.AppendLiteral("}"); + printf("%s %s\n", aPrefix, NS_ConvertUTF16toUTF8(prettyString).get()); +} +#endif + +size_t AccAttributes::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { + size_t size = + aMallocSizeOf(this) + mData.ShallowSizeOfExcludingThis(aMallocSizeOf); + + for (auto iter : *this) { + size += iter.SizeOfExcludingThis(aMallocSizeOf); + } + + return size; +} + +size_t AccAttributes::Entry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) { + size_t size = 0; + + // We don't count the size of Name() since it's counted by the atoms table + // memory reporter. + + if (mValue->is>()) { + size += mValue->as>().ShallowSizeOfExcludingThis( + aMallocSizeOf); + } else if (mValue->is>()) { + // String data will never be shared. + size += mValue->as>()->SizeOfIncludingThisIfUnshared( + aMallocSizeOf); + } else if (mValue->is>()) { + size += + mValue->as>()->SizeOfIncludingThis(aMallocSizeOf); + } else if (mValue->is>()) { + size += mValue->as>()->SizeOfIncludingThis( + aMallocSizeOf); + } else if (mValue->is>()) { + size += aMallocSizeOf(mValue->as>().get()); + } else if (mValue->is>()) { + size += mValue->as>().ShallowSizeOfExcludingThis( + aMallocSizeOf); + } else { + // This type is stored directly and already counted or is an atom and + // stored and counted in the atoms table. + // Assert that we have exhausted all the remaining variant types. + MOZ_ASSERT(mValue->is>() || mValue->is() || + mValue->is() || mValue->is() || + mValue->is() || mValue->is() || + mValue->is() || mValue->is() || + mValue->is() || mValue->is()); + } + + return size; +} diff --git a/accessible/base/AccAttributes.h b/accessible/base/AccAttributes.h new file mode 100644 index 0000000000..0d7610b358 --- /dev/null +++ b/accessible/base/AccAttributes.h @@ -0,0 +1,293 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef AccAttributes_h_ +#define AccAttributes_h_ + +#include "mozilla/ServoStyleConsts.h" +#include "mozilla/a11y/AccGroupInfo.h" +#include "mozilla/Variant.h" +#include "nsTHashMap.h" +#include "nsStringFwd.h" +#include "mozilla/gfx/Matrix.h" + +class nsVariant; + +namespace IPC { +template +struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace dom { +class Element; +} + +namespace a11y { + +struct FontSize { + int32_t mValue; + + bool operator==(const FontSize& aOther) const { + return mValue == aOther.mValue; + } + + bool operator!=(const FontSize& aOther) const { + return mValue != aOther.mValue; + } +}; + +struct Color { + nscolor mValue; + + bool operator==(const Color& aOther) const { return mValue == aOther.mValue; } + + bool operator!=(const Color& aOther) const { return mValue != aOther.mValue; } +}; + +// A special type. If an entry has a value of this type, it instructs the +// target instance of an Update to remove the entry with the same key value. +struct DeleteEntry { + DeleteEntry() : mValue(true) {} + bool mValue; + + bool operator==(const DeleteEntry& aOther) const { return true; } + + bool operator!=(const DeleteEntry& aOther) const { return false; } +}; + +class AccAttributes { + // Warning! An AccAttributes can contain another AccAttributes. This is + // intended for object and text attributes. However, the nested + // AccAttributes should never itself contain another AccAttributes, nor + // should it create a cycle. We don't do cycle collection here for + // performance reasons, so violating this rule will cause leaks! + using AttrValueType = + Variant, nsTArray, + CSSCoord, FontSize, Color, DeleteEntry, UniquePtr, + RefPtr, uint64_t, UniquePtr, + UniquePtr, nsTArray>; + static_assert(sizeof(AttrValueType) <= 16); + using AtomVariantMap = nsTHashMap, AttrValueType>; + + protected: + ~AccAttributes() = default; + + public: + AccAttributes() = default; + AccAttributes(const AccAttributes&) = delete; + AccAttributes& operator=(const AccAttributes&) = delete; + + NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::AccAttributes) + + template + void SetAttribute(nsAtom* aAttrName, T&& aAttrValue) { + using ValType = + std::remove_const_t>; + if constexpr (std::is_convertible_v) { + static_assert(std::is_rvalue_reference_v, + "Please only move strings into this function. To make a " + "copy, use SetAttributeStringCopy."); + UniquePtr value = MakeUnique(std::move(aAttrValue)); + mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value))); + } else if constexpr (std::is_same_v) { + UniquePtr value = MakeUnique(aAttrValue); + mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value))); + } else if constexpr (std::is_same_v) { + UniquePtr value(aAttrValue); + mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value))); + } else if constexpr (std::is_convertible_v) { + mData.InsertOrUpdate(aAttrName, AsVariant(RefPtr(aAttrValue))); + } else { + mData.InsertOrUpdate(aAttrName, AsVariant(std::forward(aAttrValue))); + } + } + + void SetAttributeStringCopy(nsAtom* aAttrName, nsString aAttrValue) { + SetAttribute(aAttrName, std::move(aAttrValue)); + } + + template + Maybe GetAttribute(nsAtom* aAttrName) const { + if (auto value = mData.Lookup(aAttrName)) { + if constexpr (std::is_same_v) { + if (value->is>()) { + const T& val = *(value->as>().get()); + return SomeRef(val); + } + } else if constexpr (std::is_same_v) { + if (value->is>()) { + const T& val = *(value->as>()); + return SomeRef(val); + } + } else { + if (value->is()) { + const T& val = value->as(); + return SomeRef(val); + } + } + } + return Nothing(); + } + + template + RefPtr GetAttributeRefPtr(nsAtom* aAttrName) const { + if (auto value = mData.Lookup(aAttrName)) { + if (value->is>()) { + RefPtr ref = value->as>(); + return ref; + } + } + return nullptr; + } + + template + Maybe GetMutableAttribute(nsAtom* aAttrName) const { + static_assert(std::is_same_v, T> || + std::is_same_v, T>, + "Only arrays should be mutable attributes"); + if (auto value = mData.Lookup(aAttrName)) { + if (value->is()) { + T& val = value->as(); + return SomeRef(val); + } + } + return Nothing(); + } + + // Get stringified value + bool GetAttribute(nsAtom* aAttrName, nsAString& aAttrValue) const; + + bool HasAttribute(nsAtom* aAttrName) const { + return mData.Contains(aAttrName); + } + + bool Remove(nsAtom* aAttrName) { return mData.Remove(aAttrName); } + + uint32_t Count() const { return mData.Count(); } + + // Update one instance with the entries in another. The supplied AccAttributes + // will be emptied. + void Update(AccAttributes* aOther); + + /** + * Return true if all the attributes in this instance are equal to all the + * attributes in another instance. + */ + bool Equal(const AccAttributes* aOther) const; + + /** + * Copy attributes from this instance to another instance. + * This should only be used in very specific cases; e.g. merging two sets of + * cached attributes without modifying the cache. It can only copy simple + * value types; e.g. it can't copy array values. Attempting to copy an + * AccAttributes with uncopyable values will cause an assertion. + */ + void CopyTo(AccAttributes* aDest) const; + + // An entry class for our iterator. + class Entry { + public: + Entry(nsAtom* aAttrName, const AttrValueType* aAttrValue) + : mName(aAttrName), mValue(aAttrValue) {} + + nsAtom* Name() const { return mName; } + + template + Maybe Value() const { + if constexpr (std::is_same_v) { + if (mValue->is>()) { + const T& val = *(mValue->as>().get()); + return SomeRef(val); + } + } else if constexpr (std::is_same_v) { + if (mValue->is>()) { + const T& val = *(mValue->as>()); + return SomeRef(val); + } + } else { + if (mValue->is()) { + const T& val = mValue->as(); + return SomeRef(val); + } + } + return Nothing(); + } + + void NameAsString(nsString& aName) const { + mName->ToString(aName); + if (StringBeginsWith(aName, u"aria-"_ns)) { + // Found 'aria-' + aName.ReplaceLiteral(0, 5, u""); + } + } + + void ValueAsString(nsAString& aValueString) const { + StringFromValueAndName(mName, *mValue, aValueString); + } + + // Size of the pair in the hash table. + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf); + + private: + nsAtom* mName; + const AttrValueType* mValue; + + friend class AccAttributes; + }; + + class Iterator { + public: + explicit Iterator(AtomVariantMap::const_iterator aIter) + : mHashIterator(aIter) {} + + Iterator() = delete; + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + + bool operator!=(const Iterator& aOther) const { + return mHashIterator != aOther.mHashIterator; + } + + Iterator& operator++() { + mHashIterator++; + return *this; + } + + Entry operator*() const { + auto& entry = *mHashIterator; + return Entry(entry.GetKey(), &entry.GetData()); + } + + private: + AtomVariantMap::const_iterator mHashIterator; + }; + + friend class Iterator; + + Iterator begin() const { return Iterator(mData.begin()); } + Iterator end() const { return Iterator(mData.end()); } + +#ifdef A11Y_LOG + static void DebugPrint(const char* aPrefix, const AccAttributes& aAttributes); +#endif + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf); + + private: + static void StringFromValueAndName(nsAtom* aAttrName, + const AttrValueType& aValue, + nsAString& aValueString); + + AtomVariantMap mData; + + friend struct IPC::ParamTraits; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/base/AccEvent.cpp b/accessible/base/AccEvent.cpp new file mode 100644 index 0000000000..1d1b4386f8 --- /dev/null +++ b/accessible/base/AccEvent.cpp @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "AccEvent.h" + +#include "nsAccUtils.h" +#include "xpcAccEvents.h" +#include "States.h" +#include "TextRange.h" +#include "xpcAccessibleDocument.h" +#include "xpcAccessibleTextRange.h" + +#include "mozilla/dom/Selection.h" +#include "mozilla/dom/UserActivation.h" + +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +static_assert(static_cast(eNoUserInput) == false && + static_cast(eFromUserInput) == true, + "EIsFromUserInput cannot be casted to bool"); + +//////////////////////////////////////////////////////////////////////////////// +// AccEvent +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// AccEvent constructors + +AccEvent::AccEvent(uint32_t aEventType, LocalAccessible* aAccessible, + EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) + : mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible) { + if (aIsFromUserInput == eAutoDetect) { + mIsFromUserInput = dom::UserActivation::IsHandlingUserInput(); + } else { + mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// AccEvent cycle collection + +NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible) + if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) { + tmEvent->SetNextEvent(nullptr); + tmEvent->SetPrevEvent(nullptr); + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible) + if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) { + CycleCollectionNoteChild(cb, tmEvent->NextEvent(), "mNext"); + CycleCollectionNoteChild(cb, tmEvent->PrevEvent(), "mPrevEvent"); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// AccTextChangeEvent +//////////////////////////////////////////////////////////////////////////////// + +// Note: we pass in eAllowDupes to the base class because we don't support text +// events coalescence. We fire delayed text change events in DocAccessible but +// we continue to base the event off the accessible object rather than just the +// node. This means we won't try to create an accessible based on the node when +// we are ready to fire the event and so we will no longer assert at that point +// if the node was removed from the document. Either way, the AT won't work with +// a defunct accessible so the behaviour should be equivalent. +AccTextChangeEvent::AccTextChangeEvent(LocalAccessible* aAccessible, + int32_t aStart, + const nsAString& aModifiedText, + bool aIsInserted, + EIsFromUserInput aIsFromUserInput) + : AccEvent( + aIsInserted + ? static_cast(nsIAccessibleEvent::EVENT_TEXT_INSERTED) + : static_cast(nsIAccessibleEvent::EVENT_TEXT_REMOVED), + aAccessible, aIsFromUserInput, eAllowDupes), + mStart(aStart), + mIsInserted(aIsInserted), + mModifiedText(aModifiedText) { + // XXX We should use IsFromUserInput here, but that isn't always correct + // when the text change isn't related to content insertion or removal. + mIsFromUserInput = + mAccessible->State() & (states::FOCUSED | states::EDITABLE); +} + +//////////////////////////////////////////////////////////////////////////////// +// AccHideEvent +//////////////////////////////////////////////////////////////////////////////// + +AccHideEvent::AccHideEvent(LocalAccessible* aTarget, bool aNeedsShutdown) + : AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget), + mNeedsShutdown(aNeedsShutdown) { + mNextSibling = mAccessible->LocalNextSibling(); + mPrevSibling = mAccessible->LocalPrevSibling(); +} + +//////////////////////////////////////////////////////////////////////////////// +// AccShowEvent +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// AccTextSelChangeEvent +//////////////////////////////////////////////////////////////////////////////// + +AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget, + dom::Selection* aSelection, + int32_t aReason, + int32_t aGranularity) + : AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget, + eAutoDetect, eCoalesceTextSelChange), + mSel(aSelection), + mReason(aReason), + mGranularity(aGranularity) {} + +AccTextSelChangeEvent::~AccTextSelChangeEvent() {} + +bool AccTextSelChangeEvent::IsCaretMoveOnly() const { + return mSel->RangeCount() == 1 && mSel->IsCollapsed() && + ((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON | + nsISelectionListener::COLLAPSETOEND_REASON)) == 0); +} + +void AccTextSelChangeEvent::SelectionRanges( + nsTArray* aRanges) const { + TextRange::TextRangesFromSelection(mSel, aRanges); +} + +//////////////////////////////////////////////////////////////////////////////// +// AccSelChangeEvent +//////////////////////////////////////////////////////////////////////////////// + +AccSelChangeEvent::AccSelChangeEvent(LocalAccessible* aWidget, + LocalAccessible* aItem, + SelChangeType aSelChangeType) + : AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange), + mWidget(aWidget), + mItem(aItem), + mSelChangeType(aSelChangeType), + mPreceedingCount(0), + mPackedEvent(nullptr) { + if (aSelChangeType == eSelectionAdd) { + if (mWidget->GetSelectedItem(1)) { + mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD; + } else { + mEventType = nsIAccessibleEvent::EVENT_SELECTION; + } + } else { + mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE; + } +} + +already_AddRefed a11y::MakeXPCEvent(AccEvent* aEvent) { + DocAccessible* doc = aEvent->Document(); + LocalAccessible* acc = aEvent->GetAccessible(); + nsINode* node = acc->GetNode(); + bool fromUser = aEvent->IsFromUserInput(); + uint32_t type = aEvent->GetEventType(); + uint32_t eventGroup = aEvent->GetEventGroups(); + nsCOMPtr xpEvent; + + if (eventGroup & (1 << AccEvent::eStateChangeEvent)) { + AccStateChangeEvent* sc = downcast_accEvent(aEvent); + bool extra = false; + uint32_t state = nsAccUtils::To32States(sc->GetState(), &extra); + xpEvent = new xpcAccStateChangeEvent(type, ToXPC(acc), ToXPCDocument(doc), + node, fromUser, state, extra, + sc->IsStateEnabled()); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eTextChangeEvent)) { + AccTextChangeEvent* tc = downcast_accEvent(aEvent); + nsString text; + tc->GetModifiedText(text); + xpEvent = new xpcAccTextChangeEvent( + type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, + tc->GetStartOffset(), tc->GetLength(), tc->IsTextInserted(), text); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eHideEvent)) { + AccHideEvent* hideEvent = downcast_accEvent(aEvent); + xpEvent = new xpcAccHideEvent(type, ToXPC(acc), ToXPCDocument(doc), node, + fromUser, ToXPC(hideEvent->TargetParent()), + ToXPC(hideEvent->TargetNextSibling()), + ToXPC(hideEvent->TargetPrevSibling())); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eCaretMoveEvent)) { + AccCaretMoveEvent* cm = downcast_accEvent(aEvent); + xpEvent = new xpcAccCaretMoveEvent( + type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, + cm->GetCaretOffset(), cm->IsSelectionCollapsed(), cm->IsAtEndOfLine(), + cm->GetGranularity()); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eTextSelChangeEvent)) { + AccTextSelChangeEvent* tsc = downcast_accEvent(aEvent); + AutoTArray ranges; + tsc->SelectionRanges(&ranges); + + nsCOMPtr xpcRanges = + do_CreateInstance(NS_ARRAY_CONTRACTID); + uint32_t len = ranges.Length(); + for (uint32_t idx = 0; idx < len; idx++) { + xpcRanges->AppendElement(new xpcAccessibleTextRange(ranges[idx])); + } + + xpEvent = new xpcAccTextSelectionChangeEvent( + type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, xpcRanges); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eObjectAttrChangedEvent)) { + AccObjectAttrChangedEvent* oac = downcast_accEvent(aEvent); + nsString attribute; + oac->GetAttribute()->ToString(attribute); + xpEvent = new xpcAccObjectAttributeChangedEvent( + type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, attribute); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eScrollingEvent)) { + AccScrollingEvent* sa = downcast_accEvent(aEvent); + xpEvent = new xpcAccScrollingEvent( + type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, sa->ScrollX(), + sa->ScrollY(), sa->MaxScrollX(), sa->MaxScrollY()); + return xpEvent.forget(); + } + + if (eventGroup & (1 << AccEvent::eAnnouncementEvent)) { + AccAnnouncementEvent* aa = downcast_accEvent(aEvent); + xpEvent = new xpcAccAnnouncementEvent(type, ToXPC(acc), ToXPCDocument(doc), + node, fromUser, aa->Announcement(), + aa->Priority()); + return xpEvent.forget(); + } + + xpEvent = + new xpcAccEvent(type, ToXPC(acc), ToXPCDocument(doc), node, fromUser); + return xpEvent.forget(); +} diff --git a/accessible/base/AccEvent.h b/accessible/base/AccEvent.h new file mode 100644 index 0000000000..a4ff82916a --- /dev/null +++ b/accessible/base/AccEvent.h @@ -0,0 +1,562 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#ifndef _AccEvent_H_ +#define _AccEvent_H_ + +#include "nsIAccessibleEvent.h" + +#include "mozilla/a11y/LocalAccessible.h" + +class nsEventShell; +namespace mozilla { + +namespace dom { +class Selection; +} + +namespace a11y { + +class DocAccessible; +class EventQueue; +class TextRange; + +// Constants used to point whether the event is from user input. +enum EIsFromUserInput { + // eNoUserInput: event is not from user input + eNoUserInput = 0, + // eFromUserInput: event is from user input + eFromUserInput = 1, + // eAutoDetect: the value should be obtained from event state manager + eAutoDetect = -1 +}; + +/** + * Generic accessible event. + */ +class AccEvent { + public: + // Rule for accessible events. + // The rule will be applied when flushing pending events. + enum EEventRule { + // eAllowDupes : More than one event of the same type is allowed. + // This event will always be emitted. This flag is used for events that + // don't support coalescence. + eAllowDupes, + + // eCoalesceReorder : For reorder events from the same subtree or the same + // node, only the umbrella event on the ancestor will be emitted. + eCoalesceReorder, + + // eCoalesceOfSameType : For events of the same type, only the newest event + // will be processed. + eCoalesceOfSameType, + + // eCoalesceSelectionChange: coalescence of selection change events. + eCoalesceSelectionChange, + + // eCoalesceStateChange: coalesce state change events. + eCoalesceStateChange, + + // eCoalesceTextSelChange: coalescence of text selection change events. + eCoalesceTextSelChange, + + // eRemoveDupes : For repeat events, only the newest event in queue + // will be emitted. + eRemoveDupes, + + // eDoNotEmit : This event is confirmed as a duplicate, do not emit it. + eDoNotEmit + }; + + // Initialize with an accessible. + AccEvent(uint32_t aEventType, LocalAccessible* aAccessible, + EIsFromUserInput aIsFromUserInput = eAutoDetect, + EEventRule aEventRule = eRemoveDupes); + + // AccEvent + uint32_t GetEventType() const { return mEventType; } + EEventRule GetEventRule() const { return mEventRule; } + bool IsFromUserInput() const { return mIsFromUserInput; } + EIsFromUserInput FromUserInput() const { + return static_cast(mIsFromUserInput); + } + + LocalAccessible* GetAccessible() const { return mAccessible; } + DocAccessible* Document() const { return mAccessible->Document(); } + + /** + * Down casting. + */ + enum EventGroup { + eGenericEvent, + eStateChangeEvent, + eTextChangeEvent, + eTreeMutationEvent, + eMutationEvent, + eReorderEvent, + eHideEvent, + eShowEvent, + eCaretMoveEvent, + eTextSelChangeEvent, + eSelectionChangeEvent, + eObjectAttrChangedEvent, + eScrollingEvent, + eAnnouncementEvent, + }; + + static const EventGroup kEventGroup = eGenericEvent; + virtual unsigned int GetEventGroups() const { return 1U << eGenericEvent; } + + /** + * Reference counting and cycle collection. + */ + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AccEvent) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent) + + protected: + virtual ~AccEvent() {} + + bool mIsFromUserInput; + uint32_t mEventType; + EEventRule mEventRule; + RefPtr mAccessible; + + friend class EventQueue; + friend class EventTree; + friend class ::nsEventShell; + friend class NotificationController; +}; + +/** + * Accessible state change event. + */ +class AccStateChangeEvent : public AccEvent { + public: + AccStateChangeEvent(LocalAccessible* aAccessible, uint64_t aState, + bool aIsEnabled, + EIsFromUserInput aIsFromUserInput = eAutoDetect) + : AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, + aIsFromUserInput, eCoalesceStateChange), + mState(aState), + mIsEnabled(aIsEnabled) {} + + AccStateChangeEvent(LocalAccessible* aAccessible, uint64_t aState) + : AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, + eAutoDetect, eCoalesceStateChange), + mState(aState) { + mIsEnabled = (mAccessible->State() & mState) != 0; + } + + // AccEvent + static const EventGroup kEventGroup = eStateChangeEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eStateChangeEvent); + } + + // AccStateChangeEvent + uint64_t GetState() const { return mState; } + bool IsStateEnabled() const { return mIsEnabled; } + + private: + uint64_t mState; + bool mIsEnabled; + + friend class EventQueue; +}; + +/** + * Accessible text change event. + */ +class AccTextChangeEvent : public AccEvent { + public: + AccTextChangeEvent(LocalAccessible* aAccessible, int32_t aStart, + const nsAString& aModifiedText, bool aIsInserted, + EIsFromUserInput aIsFromUserInput = eAutoDetect); + + // AccEvent + static const EventGroup kEventGroup = eTextChangeEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eTextChangeEvent); + } + + // AccTextChangeEvent + int32_t GetStartOffset() const { return mStart; } + uint32_t GetLength() const { return mModifiedText.Length(); } + bool IsTextInserted() const { return mIsInserted; } + void GetModifiedText(nsAString& aModifiedText) { + aModifiedText = mModifiedText; + } + const nsString& ModifiedText() const { return mModifiedText; } + + private: + int32_t mStart; + bool mIsInserted; + nsString mModifiedText; + + friend class EventTree; + friend class NotificationController; +}; + +/** + * A base class for events related to tree mutation, either an AccMutation + * event, or an AccReorderEvent. + */ +class AccTreeMutationEvent : public AccEvent { + public: + AccTreeMutationEvent(uint32_t aEventType, LocalAccessible* aTarget) + : AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceReorder), + mGeneration(0) {} + + // Event + static const EventGroup kEventGroup = eTreeMutationEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eTreeMutationEvent); + } + + void SetNextEvent(AccTreeMutationEvent* aNext) { mNextEvent = aNext; } + void SetPrevEvent(AccTreeMutationEvent* aPrev) { mPrevEvent = aPrev; } + AccTreeMutationEvent* NextEvent() const { return mNextEvent; } + AccTreeMutationEvent* PrevEvent() const { return mPrevEvent; } + + /** + * A sequence number to know when this event was fired. + */ + uint32_t EventGeneration() const { return mGeneration; } + void SetEventGeneration(uint32_t aGeneration) { mGeneration = aGeneration; } + + private: + RefPtr mNextEvent; + RefPtr mPrevEvent; + uint32_t mGeneration; +}; + +/** + * Base class for show and hide accessible events. + */ +class AccMutationEvent : public AccTreeMutationEvent { + public: + AccMutationEvent(uint32_t aEventType, LocalAccessible* aTarget) + : AccTreeMutationEvent(aEventType, aTarget) { + // Don't coalesce these since they are coalesced by reorder event. Coalesce + // contained text change events. + mParent = mAccessible->LocalParent(); + } + virtual ~AccMutationEvent() {} + + // Event + static const EventGroup kEventGroup = eMutationEvent; + virtual unsigned int GetEventGroups() const override { + return AccTreeMutationEvent::GetEventGroups() | (1U << eMutationEvent); + } + + // MutationEvent + bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; } + bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; } + + LocalAccessible* LocalParent() const { return mParent; } + + protected: + RefPtr mParent; + RefPtr mTextChangeEvent; + + friend class EventTree; + friend class NotificationController; +}; + +/** + * Accessible hide event. + */ +class AccHideEvent : public AccMutationEvent { + public: + explicit AccHideEvent(LocalAccessible* aTarget, bool aNeedsShutdown = true); + + // Event + static const EventGroup kEventGroup = eHideEvent; + virtual unsigned int GetEventGroups() const override { + return AccMutationEvent::GetEventGroups() | (1U << eHideEvent); + } + + // AccHideEvent + LocalAccessible* TargetParent() const { return mParent; } + LocalAccessible* TargetNextSibling() const { return mNextSibling; } + LocalAccessible* TargetPrevSibling() const { return mPrevSibling; } + bool NeedsShutdown() const { return mNeedsShutdown; } + + protected: + bool mNeedsShutdown; + RefPtr mNextSibling; + RefPtr mPrevSibling; + + friend class EventTree; + friend class NotificationController; +}; + +/** + * Accessible show event. + */ +class AccShowEvent : public AccMutationEvent { + public: + explicit AccShowEvent(LocalAccessible* aTarget) + : AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget) {} + + // Event + static const EventGroup kEventGroup = eShowEvent; + virtual unsigned int GetEventGroups() const override { + return AccMutationEvent::GetEventGroups() | (1U << eShowEvent); + } +}; + +/** + * Class for reorder accessible event. Takes care about + */ +class AccReorderEvent : public AccTreeMutationEvent { + public: + explicit AccReorderEvent(LocalAccessible* aTarget) + : AccTreeMutationEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget) {} + virtual ~AccReorderEvent() {} + + // Event + static const EventGroup kEventGroup = eReorderEvent; + virtual unsigned int GetEventGroups() const override { + return AccTreeMutationEvent::GetEventGroups() | (1U << eReorderEvent); + } + + /* + * Make this an inner reorder event that is coalesced into + * a reorder event of an ancestor. + */ + void SetInner() { mEventType = ::nsIAccessibleEvent::EVENT_INNER_REORDER; } +}; + +/** + * Accessible caret move event. + */ +class AccCaretMoveEvent : public AccEvent { + public: + AccCaretMoveEvent(LocalAccessible* aAccessible, int32_t aCaretOffset, + bool aIsSelectionCollapsed, bool aIsAtEndOfLine, + int32_t aGranularity, + EIsFromUserInput aIsFromUserInput = eAutoDetect) + : AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, + aIsFromUserInput), + mCaretOffset(aCaretOffset), + mIsSelectionCollapsed(aIsSelectionCollapsed), + mIsAtEndOfLine(aIsAtEndOfLine), + mGranularity(aGranularity) {} + virtual ~AccCaretMoveEvent() {} + + // AccEvent + static const EventGroup kEventGroup = eCaretMoveEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent); + } + + // AccCaretMoveEvent + int32_t GetCaretOffset() const { return mCaretOffset; } + + bool IsSelectionCollapsed() const { return mIsSelectionCollapsed; } + bool IsAtEndOfLine() { return mIsAtEndOfLine; } + + int32_t GetGranularity() const { return mGranularity; } + + private: + int32_t mCaretOffset; + + bool mIsSelectionCollapsed; + bool mIsAtEndOfLine; + int32_t mGranularity; +}; + +/** + * Accessible text selection change event. + */ +class AccTextSelChangeEvent : public AccEvent { + public: + AccTextSelChangeEvent(HyperTextAccessible* aTarget, + dom::Selection* aSelection, int32_t aReason, + int32_t aGranularity); + virtual ~AccTextSelChangeEvent(); + + // AccEvent + static const EventGroup kEventGroup = eTextSelChangeEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent); + } + + // AccTextSelChangeEvent + + /** + * Return true if the text selection change wasn't caused by pure caret move. + */ + bool IsCaretMoveOnly() const; + + int32_t GetGranularity() const { return mGranularity; } + + /** + * Return selection ranges in document/control. + */ + void SelectionRanges(nsTArray* aRanges) const; + + private: + RefPtr mSel; + int32_t mReason; + int32_t mGranularity; + + friend class EventQueue; + friend class SelectionManager; +}; + +/** + * Accessible widget selection change event. + */ +class AccSelChangeEvent : public AccEvent { + public: + enum SelChangeType { eSelectionAdd, eSelectionRemove }; + + AccSelChangeEvent(LocalAccessible* aWidget, LocalAccessible* aItem, + SelChangeType aSelChangeType); + + virtual ~AccSelChangeEvent() {} + + // AccEvent + static const EventGroup kEventGroup = eSelectionChangeEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent); + } + + // AccSelChangeEvent + LocalAccessible* Widget() const { return mWidget; } + + private: + RefPtr mWidget; + RefPtr mItem; + SelChangeType mSelChangeType; + uint32_t mPreceedingCount; + AccSelChangeEvent* mPackedEvent; + + friend class EventQueue; +}; + +/** + * Accessible object attribute changed event. + */ +class AccObjectAttrChangedEvent : public AccEvent { + public: + AccObjectAttrChangedEvent(LocalAccessible* aAccessible, nsAtom* aAttribute) + : AccEvent(::nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, + aAccessible), + mAttribute(aAttribute) {} + + // AccEvent + static const EventGroup kEventGroup = eObjectAttrChangedEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eObjectAttrChangedEvent); + } + + // AccObjectAttrChangedEvent + nsAtom* GetAttribute() const { return mAttribute; } + + private: + RefPtr mAttribute; + + virtual ~AccObjectAttrChangedEvent() {} +}; + +/** + * Accessible scroll event. + */ +class AccScrollingEvent : public AccEvent { + public: + AccScrollingEvent(uint32_t aEventType, LocalAccessible* aAccessible, + uint32_t aScrollX, uint32_t aScrollY, uint32_t aMaxScrollX, + uint32_t aMaxScrollY) + : AccEvent(aEventType, aAccessible), + mScrollX(aScrollX), + mScrollY(aScrollY), + mMaxScrollX(aMaxScrollX), + mMaxScrollY(aMaxScrollY) {} + + virtual ~AccScrollingEvent() {} + + // AccEvent + static const EventGroup kEventGroup = eScrollingEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eScrollingEvent); + } + + // The X scrolling offset of the container when the event was fired. + uint32_t ScrollX() { return mScrollX; } + // The Y scrolling offset of the container when the event was fired. + uint32_t ScrollY() { return mScrollY; } + // The max X offset of the container. + uint32_t MaxScrollX() { return mMaxScrollX; } + // The max Y offset of the container. + uint32_t MaxScrollY() { return mMaxScrollY; } + + private: + uint32_t mScrollX; + uint32_t mScrollY; + uint32_t mMaxScrollX; + uint32_t mMaxScrollY; +}; + +/** + * Accessible announcement event. + */ +class AccAnnouncementEvent : public AccEvent { + public: + AccAnnouncementEvent(LocalAccessible* aAccessible, + const nsAString& aAnnouncement, uint16_t aPriority) + : AccEvent(nsIAccessibleEvent::EVENT_ANNOUNCEMENT, aAccessible), + mAnnouncement(aAnnouncement), + mPriority(aPriority) {} + + virtual ~AccAnnouncementEvent() {} + + // AccEvent + static const EventGroup kEventGroup = eAnnouncementEvent; + virtual unsigned int GetEventGroups() const override { + return AccEvent::GetEventGroups() | (1U << eAnnouncementEvent); + } + + const nsString& Announcement() const { return mAnnouncement; } + + uint16_t Priority() { return mPriority; } + + private: + nsString mAnnouncement; + uint16_t mPriority; +}; + +/** + * Downcast the generic accessible event object to derived type. + */ +class downcast_accEvent { + public: + explicit downcast_accEvent(AccEvent* e) : mRawPtr(e) {} + + template + operator Destination*() { + if (!mRawPtr) return nullptr; + + return mRawPtr->GetEventGroups() & (1U << Destination::kEventGroup) + ? static_cast(mRawPtr) + : nullptr; + } + + private: + AccEvent* mRawPtr; +}; + +/** + * Return a new xpcom accessible event for the given internal one. + */ +already_AddRefed MakeXPCEvent(AccEvent* aEvent); + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/base/AccGroupInfo.cpp b/accessible/base/AccGroupInfo.cpp new file mode 100644 index 0000000000..3b536b1aa4 --- /dev/null +++ b/accessible/base/AccGroupInfo.cpp @@ -0,0 +1,397 @@ +/* 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 "AccGroupInfo.h" +#include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/TableAccessible.h" + +#include "nsAccUtils.h" +#include "nsIAccessiblePivot.h" + +#include "Pivot.h" +#include "States.h" + +using namespace mozilla::a11y; + +static role BaseRole(role aRole); + +// This rule finds candidate siblings for compound widget children. +class CompoundWidgetSiblingRule : public PivotRule { + public: + CompoundWidgetSiblingRule() = delete; + explicit CompoundWidgetSiblingRule(role aRole) : mRole(aRole) {} + + uint16_t Match(Accessible* aAcc) override { + // If the acc has a matching role, that's a valid sibling. If the acc is + // separator then the group is ended. Return a match for separators with + // the assumption that the caller will check for the role of the returned + // accessible. + const role accRole = aAcc->Role(); + if (BaseRole(accRole) == mRole || accRole == role::SEPARATOR) { + return nsIAccessibleTraversalRule::FILTER_MATCH | + nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + + // Ignore generic accessibles, but keep searching through the subtree for + // siblings. + if (aAcc->IsGeneric()) { + return nsIAccessibleTraversalRule::FILTER_IGNORE; + } + + return nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE; + } + + private: + role mRole = role::NOTHING; +}; + +AccGroupInfo::AccGroupInfo(const Accessible* aItem, role aRole) + : mPosInSet(0), mSetSize(0), mParentId(0), mItem(aItem), mRole(aRole) { + MOZ_COUNT_CTOR(AccGroupInfo); + Update(); +} + +void AccGroupInfo::Update() { + mParentId = 0; + + Accessible* parent = mItem->GetNonGenericParent(); + if (!parent) { + return; + } + + const int32_t level = GetARIAOrDefaultLevel(mItem); + + // Compute position in set. + mPosInSet = 1; + + // Search backwards through the tree for candidate siblings. + Accessible* candidateSibling = const_cast(mItem); + Pivot pivot{parent}; + CompoundWidgetSiblingRule widgetSiblingRule{mRole}; + while ((candidateSibling = pivot.Prev(candidateSibling, widgetSiblingRule)) && + candidateSibling != parent) { + // If the sibling is separator then the group is ended. + if (candidateSibling->Role() == roles::SEPARATOR) { + break; + } + + const AccGroupInfo* siblingGroupInfo = candidateSibling->GetGroupInfo(); + // Skip invisible siblings. + // If the sibling has calculated group info, that means it's visible. + if (!siblingGroupInfo && candidateSibling->State() & states::INVISIBLE) { + continue; + } + + // Check if it's hierarchical flatten structure, i.e. if the sibling + // level is lesser than this one then group is ended, if the sibling level + // is greater than this one then the group is split by some child elements + // (group will be continued). + const int32_t siblingLevel = GetARIAOrDefaultLevel(candidateSibling); + if (siblingLevel < level) { + mParentId = candidateSibling->ID(); + break; + } + + // Skip subset. + if (siblingLevel > level) { + continue; + } + + // If the previous item in the group has calculated group information then + // build group information for this item based on found one. + if (siblingGroupInfo) { + mPosInSet += siblingGroupInfo->mPosInSet; + mParentId = siblingGroupInfo->mParentId; + mSetSize = siblingGroupInfo->mSetSize; + return; + } + + mPosInSet++; + } + + // Compute set size. + mSetSize = mPosInSet; + + candidateSibling = const_cast(mItem); + while ((candidateSibling = pivot.Next(candidateSibling, widgetSiblingRule)) && + candidateSibling != parent) { + // If the sibling is separator then the group is ended. + if (candidateSibling->Role() == roles::SEPARATOR) { + break; + } + + const AccGroupInfo* siblingGroupInfo = candidateSibling->GetGroupInfo(); + // Skip invisible siblings. + // If the sibling has calculated group info, that means it's visible. + if (!siblingGroupInfo && candidateSibling->State() & states::INVISIBLE) { + continue; + } + + // and check if it's hierarchical flatten structure. + const int32_t siblingLevel = GetARIAOrDefaultLevel(candidateSibling); + if (siblingLevel < level) { + break; + } + + // Skip subset. + if (siblingLevel > level) { + continue; + } + + // If the next item in the group has calculated group information then + // build group information for this item based on found one. + if (siblingGroupInfo) { + mParentId = siblingGroupInfo->mParentId; + mSetSize = siblingGroupInfo->mSetSize; + return; + } + + mSetSize++; + } + + if (mParentId) { + return; + } + + roles::Role parentRole = parent->Role(); + if (ShouldReportRelations(mRole, parentRole)) { + mParentId = parent->ID(); + } + + // ARIA tree and list can be arranged by using ARIA groups to organize levels. + if (parentRole != roles::GROUPING) { + return; + } + + // Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a + // parent. In other words the parent of the tree item will be a group and + // the previous tree item of the group is a conceptual parent of the tree + // item. + if (mRole == roles::OUTLINEITEM) { + // Find the relevant grandparent of the item. Use that parent as the root + // and find the previous outline item sibling within that root. + Accessible* grandParent = parent->GetNonGenericParent(); + MOZ_ASSERT(grandParent); + Pivot pivot{grandParent}; + CompoundWidgetSiblingRule parentSiblingRule{mRole}; + Accessible* parentPrevSibling = pivot.Prev(parent, widgetSiblingRule); + if (parentPrevSibling && parentPrevSibling->Role() == mRole) { + mParentId = parentPrevSibling->ID(); + return; + } + } + + // Way #2 for ARIA list and tree: group is a child of an item. In other words + // the parent of the item will be a group and containing item of the group is + // a conceptual parent of the item. + if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) { + Accessible* grandParent = parent->GetNonGenericParent(); + if (grandParent && grandParent->Role() == mRole) { + mParentId = grandParent->ID(); + } + } +} + +AccGroupInfo* AccGroupInfo::CreateGroupInfo(const Accessible* aAccessible) { + mozilla::a11y::role role = aAccessible->Role(); + if (role != mozilla::a11y::roles::ROW && + role != mozilla::a11y::roles::OUTLINEITEM && + role != mozilla::a11y::roles::OPTION && + role != mozilla::a11y::roles::LISTITEM && + role != mozilla::a11y::roles::MENUITEM && + role != mozilla::a11y::roles::COMBOBOX_OPTION && + role != mozilla::a11y::roles::RICH_OPTION && + role != mozilla::a11y::roles::CHECK_RICH_OPTION && + role != mozilla::a11y::roles::PARENT_MENUITEM && + role != mozilla::a11y::roles::CHECK_MENU_ITEM && + role != mozilla::a11y::roles::RADIO_MENU_ITEM && + role != mozilla::a11y::roles::RADIOBUTTON && + role != mozilla::a11y::roles::PAGETAB && + role != mozilla::a11y::roles::COMMENT) { + return nullptr; + } + + AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role)); + return info; +} + +Accessible* AccGroupInfo::FirstItemOf(const Accessible* aContainer) { + // ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a + // group is a parent) or by aria-level. + a11y::role containerRole = aContainer->Role(); + Accessible* item = aContainer->NextSibling(); + if (item) { + if (containerRole == roles::OUTLINEITEM && + item->Role() == roles::GROUPING) { + item = item->FirstChild(); + } + + if (item) { + AccGroupInfo* itemGroupInfo = item->GetOrCreateGroupInfo(); + if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer) { + return item; + } + } + } + + // ARIA list and tree can be arranged by ARIA groups case #2 (group is + // a child of an item). + item = aContainer->LastChild(); + if (!item) return nullptr; + + if (item->Role() == roles::GROUPING && + (containerRole == roles::LISTITEM || + containerRole == roles::OUTLINEITEM)) { + item = item->FirstChild(); + if (item) { + AccGroupInfo* itemGroupInfo = item->GetOrCreateGroupInfo(); + if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer) { + return item; + } + } + } + + // Otherwise, it can be a direct child if the container is a list or tree. + item = aContainer->FirstChild(); + if (ShouldReportRelations(item->Role(), containerRole)) return item; + + return nullptr; +} + +uint32_t AccGroupInfo::TotalItemCount(Accessible* aContainer, + bool* aIsHierarchical) { + uint32_t itemCount = 0; + switch (aContainer->Role()) { + case roles::TABLE: + if (auto val = aContainer->GetIntARIAAttr(nsGkAtoms::aria_rowcount)) { + if (*val >= 0) { + return *val; + } + } + if (TableAccessible* tableAcc = aContainer->AsTable()) { + return tableAcc->RowCount(); + } + break; + case roles::ROW: + if (Accessible* table = nsAccUtils::TableFor(aContainer)) { + if (auto val = table->GetIntARIAAttr(nsGkAtoms::aria_colcount)) { + if (*val >= 0) { + return *val; + } + } + if (TableAccessible* tableAcc = table->AsTable()) { + return tableAcc->ColCount(); + } + } + break; + case roles::OUTLINE: + case roles::LIST: + case roles::MENUBAR: + case roles::MENUPOPUP: + case roles::COMBOBOX: + case roles::GROUPING: + case roles::TREE_TABLE: + case roles::COMBOBOX_LIST: + case roles::LISTBOX: + case roles::DEFINITION_LIST: + case roles::EDITCOMBOBOX: + case roles::RADIO_GROUP: + case roles::PAGETABLIST: { + Accessible* childItem = AccGroupInfo::FirstItemOf(aContainer); + if (!childItem) { + childItem = aContainer->FirstChild(); + if (childItem && childItem->IsTextLeaf()) { + // First child can be a text leaf, check its sibling for an item. + childItem = childItem->NextSibling(); + } + } + + if (childItem) { + GroupPos groupPos = childItem->GroupPosition(); + itemCount = groupPos.setSize; + if (groupPos.level && aIsHierarchical) { + *aIsHierarchical = true; + } + } + break; + } + default: + break; + } + + return itemCount; +} + +Accessible* AccGroupInfo::NextItemTo(Accessible* aItem) { + AccGroupInfo* groupInfo = aItem->GetOrCreateGroupInfo(); + if (!groupInfo) return nullptr; + + // If the item in middle of the group then search next item in siblings. + if (groupInfo->PosInSet() >= groupInfo->SetSize()) return nullptr; + + Accessible* parent = aItem->Parent(); + uint32_t childCount = parent->ChildCount(); + for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) { + Accessible* nextItem = parent->ChildAt(idx); + AccGroupInfo* nextGroupInfo = nextItem->GetOrCreateGroupInfo(); + if (nextGroupInfo && + nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) { + return nextItem; + } + } + + MOZ_ASSERT_UNREACHABLE( + "Item in the middle of the group but there's no next item!"); + return nullptr; +} + +size_t AccGroupInfo::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) { + // We don't count mParentId or mItem since they (should be) counted + // as part of the document. + return aMallocSizeOf(this); +} + +bool AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole) { + // We only want to report hierarchy-based node relations for items in tree or + // list form. ARIA level/owns relations are always reported. + if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM) return true; + if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW) return true; + if (aParentRole == roles::LIST && aRole == roles::LISTITEM) return true; + + return false; +} + +int32_t AccGroupInfo::GetARIAOrDefaultLevel(const Accessible* aAccessible) { + int32_t level = 0; + aAccessible->ARIAGroupPosition(&level, nullptr, nullptr); + + if (level != 0) return level; + + return aAccessible->GetLevel(true); +} + +Accessible* AccGroupInfo::ConceptualParent() const { + if (!mParentId) { + // The conceptual parent can never be the document, so id 0 means none. + return nullptr; + } + if (Accessible* doc = + nsAccUtils::DocumentFor(const_cast(mItem))) { + return nsAccUtils::GetAccessibleByID(doc, mParentId); + } + return nullptr; +} + +static role BaseRole(role aRole) { + if (aRole == roles::CHECK_MENU_ITEM || aRole == roles::PARENT_MENUITEM || + aRole == roles::RADIO_MENU_ITEM) { + return roles::MENUITEM; + } + + if (aRole == roles::CHECK_RICH_OPTION) { + return roles::RICH_OPTION; + } + + return aRole; +} diff --git a/accessible/base/AccGroupInfo.h b/accessible/base/AccGroupInfo.h new file mode 100644 index 0000000000..a9afa14b8e --- /dev/null +++ b/accessible/base/AccGroupInfo.h @@ -0,0 +1,101 @@ +/* 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/. */ + +#ifndef AccGroupInfo_h_ +#define AccGroupInfo_h_ + +#include "nsISupportsImpl.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/a11y/Role.h" + +namespace mozilla { +namespace a11y { + +class Accessible; + +/** + * Calculate and store group information. + */ +class AccGroupInfo { + public: + MOZ_COUNTED_DTOR(AccGroupInfo) + + AccGroupInfo() = default; + AccGroupInfo(AccGroupInfo&&) = default; + AccGroupInfo& operator=(AccGroupInfo&&) = default; + + /** + * Return 1-based position in the group. + */ + uint32_t PosInSet() const { return mPosInSet; } + + /** + * Return a number of items in the group. + */ + uint32_t SetSize() const { return mSetSize; } + + /** + * Return a direct or logical parent of the accessible that this group info is + * created for. + */ + Accessible* ConceptualParent() const; + + /** + * Update group information. + */ + void Update(); + + /** + * Create group info. + */ + static AccGroupInfo* CreateGroupInfo(const Accessible* aAccessible); + + /** + * Return a first item for the given container. + */ + static Accessible* FirstItemOf(const Accessible* aContainer); + + /** + * Return total number of items in container, and if it is has nested + * collections. + */ + static uint32_t TotalItemCount(Accessible* aContainer, bool* aIsHierarchical); + + /** + * Return next item of the same group to the given item. + */ + static Accessible* NextItemTo(Accessible* aItem); + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf); + + protected: + AccGroupInfo(const Accessible* aItem, a11y::role aRole); + + private: + AccGroupInfo(const AccGroupInfo&) = delete; + AccGroupInfo& operator=(const AccGroupInfo&) = delete; + + /** + * Return true if the given parent and child roles should have their node + * relations reported. + */ + static bool ShouldReportRelations(a11y::role aRole, a11y::role aParentRole); + + /** + * Return ARIA level value or the default one if ARIA is missed for the + * given accessible. + */ + static int32_t GetARIAOrDefaultLevel(const Accessible* aAccessible); + + uint32_t mPosInSet; + uint32_t mSetSize; + uint64_t mParentId; + const Accessible* mItem; + a11y::role mRole; +}; + +} // namespace a11y +} // namespace mozilla + +#endif diff --git a/accessible/base/AccIterator.cpp b/accessible/base/AccIterator.cpp new file mode 100644 index 0000000000..badd34c0d5 --- /dev/null +++ b/accessible/base/AccIterator.cpp @@ -0,0 +1,360 @@ +/* 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 "AccIterator.h" + +#include "AccGroupInfo.h" +#include "DocAccessible-inl.h" +#include "LocalAccessible-inl.h" +#include "XULTreeAccessible.h" + +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/dom/DocumentOrShadowRoot.h" +#include "mozilla/dom/HTMLLabelElement.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// AccIterator +//////////////////////////////////////////////////////////////////////////////// + +AccIterator::AccIterator(const LocalAccessible* aAccessible, + filters::FilterFuncPtr aFilterFunc) + : mFilterFunc(aFilterFunc) { + mState = new IteratorState(aAccessible); +} + +AccIterator::~AccIterator() { + while (mState) { + IteratorState* tmp = mState; + mState = tmp->mParentState; + delete tmp; + } +} + +LocalAccessible* AccIterator::Next() { + while (mState) { + LocalAccessible* child = mState->mParent->LocalChildAt(mState->mIndex++); + if (!child) { + IteratorState* tmp = mState; + mState = mState->mParentState; + delete tmp; + + continue; + } + + uint32_t result = mFilterFunc(child); + if (result & filters::eMatch) return child; + + if (!(result & filters::eSkipSubtree)) { + IteratorState* childState = new IteratorState(child, mState); + mState = childState; + } + } + + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsAccIterator::IteratorState + +AccIterator::IteratorState::IteratorState(const LocalAccessible* aParent, + IteratorState* mParentState) + : mParent(aParent), mIndex(0), mParentState(mParentState) {} + +//////////////////////////////////////////////////////////////////////////////// +// RelatedAccIterator +//////////////////////////////////////////////////////////////////////////////// + +RelatedAccIterator::RelatedAccIterator(DocAccessible* aDocument, + nsIContent* aDependentContent, + nsAtom* aRelAttr) + : mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nullptr), mIndex(0) { + nsAutoString id; + if (aDependentContent->IsElement() && + aDependentContent->AsElement()->GetAttr(nsGkAtoms::id, id)) { + mProviders = mDocument->GetRelProviders(aDependentContent->AsElement(), id); + } +} + +LocalAccessible* RelatedAccIterator::Next() { + if (!mProviders) return nullptr; + + while (mIndex < mProviders->Length()) { + const auto& provider = (*mProviders)[mIndex++]; + + // Return related accessible for the given attribute. + if (provider->mRelAttr == mRelAttr) { + LocalAccessible* related = mDocument->GetAccessible(provider->mContent); + if (related) { + return related; + } + + // If the document content is pointed by relation then return the + // document itself. + if (provider->mContent == mDocument->GetContent()) { + return mDocument; + } + } + } + + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// HTMLLabelIterator +//////////////////////////////////////////////////////////////////////////////// + +HTMLLabelIterator::HTMLLabelIterator(DocAccessible* aDocument, + const LocalAccessible* aAccessible, + LabelFilter aFilter) + : mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for), + mAcc(aAccessible), + mLabelFilter(aFilter) {} + +bool HTMLLabelIterator::IsLabel(LocalAccessible* aLabel) { + dom::HTMLLabelElement* labelEl = + dom::HTMLLabelElement::FromNode(aLabel->GetContent()); + return labelEl && labelEl->GetControl() == mAcc->GetContent(); +} + +LocalAccessible* HTMLLabelIterator::Next() { + // Get either