From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- accessible/tests/browser/.eslintrc.js | 28 + accessible/tests/browser/Common.jsm | 456 ++++ accessible/tests/browser/Layout.jsm | 181 ++ accessible/tests/browser/bounds/browser.ini | 13 + .../browser/bounds/browser_test_resolution.js | 72 + .../tests/browser/bounds/browser_test_zoom.js | 69 + .../tests/browser/bounds/browser_test_zoom_text.js | 76 + accessible/tests/browser/bounds/head.js | 21 + accessible/tests/browser/browser.ini | 34 + .../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 | 90 + ...owser_shutdown_multi_proxy_acc_reference_obj.js | 90 + .../browser/browser_shutdown_multi_reference.js | 58 + .../browser_shutdown_parent_own_reference.js | 104 + accessible/tests/browser/browser_shutdown_pref.js | 72 + .../browser_shutdown_proxy_acc_reference.js | 76 + .../browser_shutdown_proxy_doc_acc_reference.js | 78 + .../browser_shutdown_remote_no_reference.js | 151 ++ .../tests/browser/browser_shutdown_remote_only.js | 56 + .../browser_shutdown_remote_own_reference.js | 191 ++ .../browser/browser_shutdown_scope_lifecycle.js | 28 + .../browser/browser_shutdown_start_restart.js | 51 + accessible/tests/browser/e10s/browser.ini | 60 + .../browser/e10s/browser_caching_attributes.js | 134 + .../browser/e10s/browser_caching_description.js | 214 ++ .../tests/browser/e10s/browser_caching_name.js | 539 ++++ .../browser/e10s/browser_caching_relations.js | 96 + .../tests/browser/e10s/browser_caching_states.js | 154 ++ .../tests/browser/e10s/browser_caching_uniqueid.js | 30 + .../tests/browser/e10s/browser_caching_value.js | 195 ++ .../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 | 114 + .../tests/browser/e10s/browser_events_vcchange.js | 87 + .../e10s/browser_text_paragraph_boundary.js | 22 + .../browser/e10s/browser_treeupdate_ariadialog.js | 45 + .../browser/e10s/browser_treeupdate_ariaowns.js | 294 +++ .../browser/e10s/browser_treeupdate_canvas.js | 28 + .../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 + .../browser/e10s/browser_treeupdate_imagemap.js | 187 ++ .../tests/browser/e10s/browser_treeupdate_list.js | 52 + .../e10s/browser_treeupdate_list_editabledoc.js | 48 + .../browser/e10s/browser_treeupdate_listener.js | 38 + .../browser/e10s/browser_treeupdate_optgroup.js | 103 + .../browser/e10s/browser_treeupdate_removal.js | 58 + .../e10s/browser_treeupdate_select_dropdown.js | 77 + .../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 | 68 + .../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/head.js | 19 + accessible/tests/browser/events/browser.ini | 17 + .../events/browser_test_A11yUtils_announce.js | 57 + .../tests/browser/events/browser_test_docload.js | 120 + .../browser/events/browser_test_focus_browserui.js | 52 + .../browser/events/browser_test_focus_dialog.js | 76 + .../browser/events/browser_test_focus_urlbar.js | 410 +++ .../tests/browser/events/browser_test_scrolling.js | 96 + .../events/browser_test_selection_urlbar.js | 58 + .../tests/browser/events/browser_test_textcaret.js | 58 + accessible/tests/browser/events/head.js | 19 + accessible/tests/browser/fission/browser.ini | 14 + .../tests/browser/fission/browser_content_tree.js | 75 + .../tests/browser/fission/browser_hidden_iframe.js | 81 + .../tests/browser/fission/browser_nested_iframe.js | 153 ++ .../tests/browser/fission/browser_reframe_root.js | 95 + .../browser/fission/browser_reframe_visibility.js | 116 + .../tests/browser/fission/browser_src_change.js | 61 + .../tests/browser/fission/browser_take_focus.js | 26 + accessible/tests/browser/fission/head.js | 19 + accessible/tests/browser/general/browser.ini | 9 + .../browser/general/browser_test_doc_creation.js | 55 + .../tests/browser/general/browser_test_urlbar.js | 40 + accessible/tests/browser/general/head.js | 72 + accessible/tests/browser/head.js | 147 ++ accessible/tests/browser/hittest/browser.ini | 15 + .../tests/browser/hittest/browser_test_browser.js | 68 + .../hittest/browser_test_canvas_hitregion.js | 92 + .../tests/browser/hittest/browser_test_general.js | 123 + .../browser/hittest/browser_test_shadowroot.js | 61 + .../tests/browser/hittest/browser_test_zoom.js | 38 + .../browser/hittest/browser_test_zoom_text.js | 76 + accessible/tests/browser/hittest/head.js | 89 + accessible/tests/browser/mac/browser.ini | 50 + accessible/tests/browser/mac/browser_app.js | 292 +++ accessible/tests/browser/mac/browser_aria_busy.js | 44 + .../browser/mac/browser_aria_controls_flowto.js | 84 + .../tests/browser/mac/browser_aria_current.js | 60 + .../tests/browser/mac/browser_aria_haspopup.js | 330 +++ .../tests/browser/mac/browser_details_summary.js | 60 + accessible/tests/browser/mac/browser_focus.js | 44 + accessible/tests/browser/mac/browser_hierarchy.js | 75 + accessible/tests/browser/mac/browser_input.js | 225 ++ .../tests/browser/mac/browser_label_title.js | 79 + accessible/tests/browser/mac/browser_link.js | 125 + .../tests/browser/mac/browser_live_regions.js | 165 ++ accessible/tests/browser/mac/browser_mathml.js | 151 ++ accessible/tests/browser/mac/browser_menulist.js | 52 + accessible/tests/browser/mac/browser_navigate.js | 394 +++ accessible/tests/browser/mac/browser_outline.js | 459 ++++ .../tests/browser/mac/browser_outline_xul.js | 274 ++ .../tests/browser/mac/browser_popupbutton.js | 153 ++ .../tests/browser/mac/browser_radio_position.js | 321 +++ accessible/tests/browser/mac/browser_range.js | 92 + accessible/tests/browser/mac/browser_required.js | 145 ++ .../tests/browser/mac/browser_rich_listbox.js | 73 + .../tests/browser/mac/browser_roles_elements.js | 309 +++ accessible/tests/browser/mac/browser_rootgroup.js | 196 ++ accessible/tests/browser/mac/browser_rotor.js | 1697 +++++++++++++ .../tests/browser/mac/browser_selectables.js | 336 +++ accessible/tests/browser/mac/browser_table.js | 281 +++ .../tests/browser/mac/browser_text_basics.js | 282 +++ accessible/tests/browser/mac/browser_text_input.js | 412 +++ accessible/tests/browser/mac/browser_text_leaf.js | 75 + .../tests/browser/mac/browser_text_selection.js | 187 ++ .../browser/mac/browser_toggle_radio_check.js | 140 ++ accessible/tests/browser/mac/browser_webarea.js | 76 + 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 | 2424 ++++++++++++++++++ accessible/tests/browser/mac/doc_tree.xhtml | 59 + accessible/tests/browser/mac/head.js | 114 + accessible/tests/browser/scroll/browser.ini | 9 + .../tests/browser/scroll/browser_test_zoom_text.js | 150 ++ accessible/tests/browser/scroll/head.js | 19 + accessible/tests/browser/shared-head.js | 779 ++++++ accessible/tests/browser/states/browser.ini | 17 + .../browser_deck_has_out_of_process_iframe.js | 133 + ...r_offscreen_element_in_out_of_process_iframe.js | 98 + .../tests/browser/states/browser_test_link.js | 47 + .../browser/states/browser_test_visibility.js | 52 + accessible/tests/browser/states/head.js | 81 + accessible/tests/browser/states/target.html | 3 + .../test_deck_has_out_of_process_iframe.xhtml | 10 + accessible/tests/browser/telemetry/browser.ini | 3 + .../browser/telemetry/browser_HCM_telemetry.js | 177 ++ accessible/tests/browser/tree/browser.ini | 11 + accessible/tests/browser/tree/browser_aria_owns.js | 278 +++ accessible/tests/browser/tree/browser_searchbar.js | 52 + accessible/tests/browser/tree/browser_shadowdom.js | 64 + accessible/tests/browser/tree/head.js | 34 + accessible/tests/crashtests/.eslintrc.js | 7 + 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/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/448064.xhtml | 70 + accessible/tests/crashtests/471493.xhtml | 55 + accessible/tests/crashtests/884202.html | 21 + accessible/tests/crashtests/890760.html | 14 + accessible/tests/crashtests/893515.html | 15 + accessible/tests/crashtests/crashtests.list | 21 + .../crashtests/last_test_to_unload_testsuite.xhtml | 36 + accessible/tests/mochitest/.eslintrc.js | 24 + accessible/tests/mochitest/a11y.ini | 18 + accessible/tests/mochitest/actions.js | 230 ++ accessible/tests/mochitest/actions/a11y.ini | 17 + .../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 | 172 ++ accessible/tests/mochitest/actions/test_keys.html | 57 + accessible/tests/mochitest/actions/test_keys.xhtml | 107 + accessible/tests/mochitest/actions/test_link.html | 141 ++ accessible/tests/mochitest/actions/test_media.html | 130 + .../tests/mochitest/actions/test_select.html | 103 + accessible/tests/mochitest/actions/test_tree.xhtml | 127 + .../tests/mochitest/actions/test_treegrid.xhtml | 190 ++ accessible/tests/mochitest/aom/a11y.ini | 3 + accessible/tests/mochitest/aom/test_general.html | 208 ++ accessible/tests/mochitest/attributes.js | 490 ++++ accessible/tests/mochitest/attributes/a11y.ini | 15 + .../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 | 300 +++ .../tests/mochitest/attributes/test_obj_css.html | 225 ++ .../tests/mochitest/attributes/test_obj_css.xhtml | 57 + .../tests/mochitest/attributes/test_obj_group.html | 566 +++++ .../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 | 195 ++ accessible/tests/mochitest/bounds/a11y.ini | 6 + accessible/tests/mochitest/bounds/test_list.html | 78 + accessible/tests/mochitest/bounds/test_select.html | 78 + accessible/tests/mochitest/browser.js | 157 ++ accessible/tests/mochitest/common.js | 1076 ++++++++ accessible/tests/mochitest/dumbfile.zip | Bin 0 -> 22 bytes accessible/tests/mochitest/editabletext/a11y.ini | 7 + .../tests/mochitest/editabletext/editabletext.js | 409 +++ .../tests/mochitest/editabletext/test_1.html | 140 ++ .../tests/mochitest/editabletext/test_2.html | 61 + accessible/tests/mochitest/elm/a11y.ini | 15 + accessible/tests/mochitest/elm/test_HTMLSpec.html | 1972 +++++++++++++++ .../tests/mochitest/elm/test_MathMLSpec.html | 617 +++++ accessible/tests/mochitest/elm/test_canvas.html | 55 + 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 | 2631 ++++++++++++++++++++ accessible/tests/mochitest/events/a11y.ini | 63 + accessible/tests/mochitest/events/docload/a11y.ini | 13 + .../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 | 126 + .../events/docload/test_docload_shutdown.html | 143 ++ accessible/tests/mochitest/events/focus.html | 10 + accessible/tests/mochitest/events/scroll.html | 181 ++ .../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 | 224 ++ 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 | 125 + .../tests/mochitest/events/test_descrchange.html | 79 + .../tests/mochitest/events/test_dragndrop.html | 106 + accessible/tests/mochitest/events/test_flush.html | 74 + .../events/test_focus_aria_activedescendant.html | 260 ++ .../mochitest/events/test_focus_autocomplete.xhtml | 507 ++++ .../tests/mochitest/events/test_focus_canvas.html | 58 + .../mochitest/events/test_focus_contextmenu.xhtml | 95 + .../mochitest/events/test_focus_controls.html | 76 + .../tests/mochitest/events/test_focus_doc.html | 92 + .../tests/mochitest/events/test_focus_general.html | 164 ++ .../mochitest/events/test_focus_general.xhtml | 118 + .../mochitest/events/test_focus_listcontrols.xhtml | 152 ++ .../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 | 115 + .../tests/mochitest/events/test_focus_tabbox.xhtml | 102 + .../tests/mochitest/events/test_focus_tree.xhtml | 117 + .../events/test_focusable_statechange.html | 96 + .../tests/mochitest/events/test_fromUserInput.html | 112 + accessible/tests/mochitest/events/test_label.xhtml | 176 ++ accessible/tests/mochitest/events/test_menu.xhtml | 200 ++ .../tests/mochitest/events/test_mutation.html | 580 +++++ .../tests/mochitest/events/test_namechange.html | 119 + .../tests/mochitest/events/test_namechange.xhtml | 90 + .../tests/mochitest/events/test_scroll.xhtml | 141 ++ .../tests/mochitest/events/test_scroll_caret.xhtml | 91 + .../tests/mochitest/events/test_selection.html | 115 + .../tests/mochitest/events/test_selection.xhtml | 254 ++ .../mochitest/events/test_selection_aria.html | 122 + .../tests/mochitest/events/test_statechange.html | 317 +++ .../events/test_statechange_tabpanels.xhtml | 98 + accessible/tests/mochitest/events/test_text.html | 310 +++ .../tests/mochitest/events/test_text_alg.html | 246 ++ .../mochitest/events/test_textattrchange.html | 105 + .../tests/mochitest/events/test_textselchange.html | 82 + accessible/tests/mochitest/events/test_tree.xhtml | 358 +++ .../tests/mochitest/events/test_valuechange.html | 279 +++ accessible/tests/mochitest/focus/a11y.ini | 9 + .../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.ini | 15 + .../tests/mochitest/hittest/test_browser.html | 61 + .../mochitest/hittest/test_canvas_hitregion.html | 85 + .../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 | 58 + 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.ini | 7 + accessible/tests/mochitest/hyperlink/hyperlink.js | 46 + .../tests/mochitest/hyperlink/test_general.html | 273 ++ .../tests/mochitest/hyperlink/test_general.xhtml | 96 + accessible/tests/mochitest/hypertext/a11y.ini | 7 + .../tests/mochitest/hypertext/test_general.html | 150 ++ .../tests/mochitest/hypertext/test_update.html | 214 ++ accessible/tests/mochitest/layout.js | 385 +++ accessible/tests/mochitest/letters.gif | Bin 0 -> 5596 bytes accessible/tests/mochitest/longdesc_src.html | 8 + accessible/tests/mochitest/moz.build | 37 + accessible/tests/mochitest/moz.png | Bin 0 -> 1991 bytes accessible/tests/mochitest/name.js | 38 + accessible/tests/mochitest/name/a11y.ini | 18 + accessible/tests/mochitest/name/markup.js | 438 ++++ accessible/tests/mochitest/name/markuprules.xml | 385 +++ .../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 | 706 ++++++ accessible/tests/mochitest/name/test_general.xhtml | 339 +++ accessible/tests/mochitest/name/test_link.html | 87 + accessible/tests/mochitest/name/test_list.html | 89 + 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/pivot.js | 665 +++++ accessible/tests/mochitest/pivot/a11y.ini | 8 + .../tests/mochitest/pivot/doc_virtualcursor.html | 38 + .../mochitest/pivot/doc_virtualcursor_text.html | 33 + .../tests/mochitest/pivot/test_virtualcursor.html | 119 + .../mochitest/pivot/test_virtualcursor_text.html | 267 ++ accessible/tests/mochitest/promisified-events.js | 262 ++ accessible/tests/mochitest/relations.js | 203 ++ accessible/tests/mochitest/relations/a11y.ini | 14 + .../tests/mochitest/relations/test_embeds.xhtml | 128 + .../tests/mochitest/relations/test_general.html | 408 +++ .../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 | 197 ++ accessible/tests/mochitest/role/a11y.ini | 13 + .../mochitest/role/chrome_body_role_alert.xhtml | 6 + accessible/tests/mochitest/role/test_aria.html | 439 ++++ 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 | 88 + accessible/tests/mochitest/scroll/a11y.ini | 5 + accessible/tests/mochitest/scroll/test_zoom.html | 145 ++ accessible/tests/mochitest/selectable.js | 123 + accessible/tests/mochitest/selectable/a11y.ini | 12 + .../tests/mochitest/selectable/test_aria.html | 246 ++ .../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_select.html | 241 ++ .../tests/mochitest/selectable/test_tabs.xhtml | 93 + .../tests/mochitest/selectable/test_tree.xhtml | 171 ++ accessible/tests/mochitest/states.js | 363 +++ accessible/tests/mochitest/states/a11y.ini | 36 + accessible/tests/mochitest/states/test_aria.html | 655 +++++ 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 | 113 + .../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 | 268 ++ accessible/tests/mochitest/states/test_link.html | 85 + accessible/tests/mochitest/states/test_popup.xhtml | 54 + .../tests/mochitest/states/test_selects.html | 196 ++ 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 | 151 ++ 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 | 1033 ++++++++ accessible/tests/mochitest/table/a11y.ini | 24 + .../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 | 554 +++++ accessible/tests/mochitest/table/test_mtable.html | 126 + .../tests/mochitest/table/test_sels_ariagrid.html | 159 ++ .../tests/mochitest/table/test_sels_table.html | 178 ++ .../tests/mochitest/table/test_sels_tree.xhtml | 78 + .../mochitest/table/test_struct_ariagrid.html | 147 ++ .../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 + 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.ini | 19 + accessible/tests/mochitest/text/doc.html | 9 + .../tests/mochitest/text/test_atcaretoffset.html | 423 ++++ .../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 | 149 ++ .../tests/mochitest/text/test_lineboundary.html | 341 +++ .../mochitest/text/test_paragraphboundary.html | 140 ++ .../tests/mochitest/text/test_passwords.html | 72 + .../tests/mochitest/text/test_selection.html | 96 + .../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 | 131 + accessible/tests/mochitest/textattrs/a11y.ini | 8 + .../tests/mochitest/textattrs/test_general.html | 737 ++++++ .../tests/mochitest/textattrs/test_invalid.html | 59 + .../tests/mochitest/textattrs/test_spelling.html | 52 + accessible/tests/mochitest/textcaret/a11y.ini | 5 + .../tests/mochitest/textcaret/test_general.html | 174 ++ accessible/tests/mochitest/textrange/a11y.ini | 7 + .../tests/mochitest/textrange/test_general.html | 106 + .../tests/mochitest/textrange/test_selection.html | 129 + accessible/tests/mochitest/textselection/a11y.ini | 6 + .../mochitest/textselection/test_general.html | 221 ++ .../mochitest/textselection/test_userinput.html | 76 + accessible/tests/mochitest/tree/a11y.ini | 56 + accessible/tests/mochitest/tree/dockids.html | 30 + .../tests/mochitest/tree/test_applicationacc.xhtml | 73 + .../tests/mochitest/tree/test_aria_globals.html | 124 + .../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 | 72 + accessible/tests/mochitest/tree/test_canvas.html | 53 + .../tests/mochitest/tree/test_combobox.xhtml | 117 + .../tests/mochitest/tree/test_cssflexbox.html | 78 + .../tests/mochitest/tree/test_cssoverflow.html | 135 + .../mochitest/tree/test_display_contents.html | 49 + accessible/tests/mochitest/tree/test_divs.html | 333 +++ .../tests/mochitest/tree/test_dochierarchy.html | 84 + accessible/tests/mochitest/tree/test_dockids.html | 62 + accessible/tests/mochitest/tree/test_filectrl.html | 56 + 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 + 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 | 354 +++ accessible/tests/mochitest/tree/test_map.html | 81 + accessible/tests/mochitest/tree/test_media.html | 121 + accessible/tests/mochitest/tree/test_select.html | 137 + accessible/tests/mochitest/tree/test_svg.html | 127 + accessible/tests/mochitest/tree/test_tabbox.xhtml | 98 + .../tests/mochitest/tree/test_tabbrowser.xhtml | 261 ++ accessible/tests/mochitest/tree/test_table.html | 458 ++++ accessible/tests/mochitest/tree/test_table_2.html | 240 ++ accessible/tests/mochitest/tree/test_table_3.html | 242 ++ accessible/tests/mochitest/tree/test_tree.xhtml | 180 ++ 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.ini | 44 + .../mochitest/treeupdate/test_ariadialog.html | 113 + .../mochitest/treeupdate/test_ariahidden.html | 118 + .../tests/mochitest/treeupdate/test_ariaowns.html | 797 ++++++ .../mochitest/treeupdate/test_bug1040735.html | 40 + .../mochitest/treeupdate/test_bug1100602.html | 103 + .../mochitest/treeupdate/test_bug1175913.html | 95 + .../mochitest/treeupdate/test_bug1189277.html | 81 + .../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 | 150 ++ .../tests/mochitest/treeupdate/test_deck.xhtml | 154 ++ .../mochitest/treeupdate/test_delayed_removal.html | 453 ++++ .../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 | 399 +++ .../tests/mochitest/treeupdate/test_list.html | 139 ++ .../treeupdate/test_list_editabledoc.html | 100 + .../tests/mochitest/treeupdate/test_listbox.xhtml | 181 ++ .../tests/mochitest/treeupdate/test_menu.xhtml | 127 + .../mochitest/treeupdate/test_menubutton.xhtml | 140 ++ .../tests/mochitest/treeupdate/test_optgroup.html | 126 + .../mochitest/treeupdate/test_recreation.html | 91 + .../tests/mochitest/treeupdate/test_select.html | 136 + .../mochitest/treeupdate/test_shadow_slots.html | 467 ++++ .../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 | 71 + .../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.ini | 9 + .../tests/mochitest/value/test_ariavalue.html | 68 + accessible/tests/mochitest/value/test_general.html | 159 ++ accessible/tests/mochitest/value/test_number.html | 56 + .../tests/mochitest/value/test_progress.html | 61 + accessible/tests/mochitest/value/test_range.html | 59 + 555 files changed, 81920 insertions(+) create mode 100644 accessible/tests/browser/.eslintrc.js create mode 100644 accessible/tests/browser/Common.jsm create mode 100644 accessible/tests/browser/Layout.jsm create mode 100644 accessible/tests/browser/bounds/browser.ini create mode 100644 accessible/tests/browser/bounds/browser_test_resolution.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/head.js create mode 100644 accessible/tests/browser/browser.ini 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.ini 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_name.js create mode 100644 accessible/tests/browser/e10s/browser_caching_relations.js create mode 100644 accessible/tests/browser/e10s/browser_caching_states.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_events_vcchange.js create mode 100644 accessible/tests/browser/e10s/browser_text_paragraph_boundary.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_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_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_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/head.js create mode 100644 accessible/tests/browser/events/browser.ini create mode 100644 accessible/tests/browser/events/browser_test_A11yUtils_announce.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_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.ini 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.ini 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.ini create mode 100644 accessible/tests/browser/hittest/browser_test_browser.js create mode 100644 accessible/tests/browser/hittest/browser_test_canvas_hitregion.js create mode 100644 accessible/tests/browser/hittest/browser_test_general.js create mode 100644 accessible/tests/browser/hittest/browser_test_shadowroot.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.ini 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_haspopup.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_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/scroll/browser.ini 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/shared-head.js create mode 100644 accessible/tests/browser/states/browser.ini create mode 100644 accessible/tests/browser/states/browser_deck_has_out_of_process_iframe.js create mode 100644 accessible/tests/browser/states/browser_offscreen_element_in_out_of_process_iframe.js create mode 100644 accessible/tests/browser/states/browser_test_link.js create mode 100644 accessible/tests/browser/states/browser_test_visibility.js create mode 100644 accessible/tests/browser/states/head.js create mode 100644 accessible/tests/browser/states/target.html create mode 100644 accessible/tests/browser/states/test_deck_has_out_of_process_iframe.xhtml create mode 100644 accessible/tests/browser/telemetry/browser.ini create mode 100644 accessible/tests/browser/telemetry/browser_HCM_telemetry.js create mode 100644 accessible/tests/browser/tree/browser.ini create mode 100644 accessible/tests/browser/tree/browser_aria_owns.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/head.js create mode 100644 accessible/tests/crashtests/.eslintrc.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/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/448064.xhtml create mode 100644 accessible/tests/crashtests/471493.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.ini create mode 100644 accessible/tests/mochitest/actions.js create mode 100644 accessible/tests/mochitest/actions/a11y.ini 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_link.html 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.ini 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.ini 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_css.xhtml 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.ini create mode 100644 accessible/tests/mochitest/bounds/test_list.html create mode 100644 accessible/tests/mochitest/bounds/test_select.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/editabletext/a11y.ini create mode 100644 accessible/tests/mochitest/editabletext/editabletext.js create mode 100644 accessible/tests/mochitest/editabletext/test_1.html create mode 100644 accessible/tests/mochitest/editabletext/test_2.html create mode 100644 accessible/tests/mochitest/elm/a11y.ini 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_canvas.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.ini create mode 100644 accessible/tests/mochitest/events/docload/a11y.ini 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/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_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.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_tabpanels.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.ini 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.ini create mode 100644 accessible/tests/mochitest/hittest/test_browser.html create mode 100644 accessible/tests/mochitest/hittest/test_canvas_hitregion.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.ini 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.ini 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.ini 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/pivot.js create mode 100644 accessible/tests/mochitest/pivot/a11y.ini create mode 100644 accessible/tests/mochitest/pivot/doc_virtualcursor.html create mode 100644 accessible/tests/mochitest/pivot/doc_virtualcursor_text.html create mode 100644 accessible/tests/mochitest/pivot/test_virtualcursor.html create mode 100644 accessible/tests/mochitest/pivot/test_virtualcursor_text.html 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.ini 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.ini 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.ini 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.ini create mode 100644 accessible/tests/mochitest/selectable/test_aria.html 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_select.html 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.ini 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.ini 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_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.ini 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.ini create mode 100644 accessible/tests/mochitest/textattrs/test_general.html create mode 100644 accessible/tests/mochitest/textattrs/test_invalid.html create mode 100644 accessible/tests/mochitest/textattrs/test_spelling.html create mode 100644 accessible/tests/mochitest/textcaret/a11y.ini create mode 100644 accessible/tests/mochitest/textcaret/test_general.html create mode 100644 accessible/tests/mochitest/textrange/a11y.ini 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.ini 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.ini 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_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_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.ini 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_bug1100602.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_list.html create mode 100644 accessible/tests/mochitest/treeupdate/test_list_editabledoc.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.ini create mode 100644 accessible/tests/mochitest/value/test_ariavalue.html create mode 100644 accessible/tests/mochitest/value/test_general.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 (limited to 'accessible/tests') diff --git a/accessible/tests/browser/.eslintrc.js b/accessible/tests/browser/.eslintrc.js new file mode 100644 index 0000000000..528797cb91 --- /dev/null +++ b/accessible/tests/browser/.eslintrc.js @@ -0,0 +1,28 @@ +"use strict"; + +module.exports = { + rules: { + "mozilla/no-aArgs": "error", + "mozilla/reject-importGlobalProperties": ["error", "everything"], + "mozilla/var-only-at-top-level": "error", + + "block-scoped-var": "error", + camelcase: ["error", { properties: "never" }], + complexity: ["error", 20], + + "handle-callback-err": ["error", "er"], + "max-nested-callbacks": ["error", 4], + "new-cap": ["error", { capIsNew: false }], + "no-fallthrough": "error", + "no-multi-str": "error", + "no-proto": "error", + "no-return-assign": "error", + "no-shadow": "error", + "no-unused-vars": ["error", { vars: "all", args: "none" }], + "one-var": ["error", "never"], + radix: "error", + strict: ["error", "global"], + yoda: "error", + "no-undef-init": "error", + }, +}; diff --git a/accessible/tests/browser/Common.jsm b/accessible/tests/browser/Common.jsm new file mode 100644 index 0000000000..527e301be9 --- /dev/null +++ b/accessible/tests/browser/Common.jsm @@ -0,0 +1,456 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["CommonUtils"]; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm"); + +const MAX_TRIM_LENGTH = 100; + +const CommonUtils = { + /** + * Constant passed to getAccessible to indicate that it shouldn't fail if + * there is no accessible. + */ + DONOTFAIL_IF_NO_ACC: 1, + + /** + * Constant passed to getAccessible to indicate that it shouldn't fail if it + * does not support an interface. + */ + DONOTFAIL_IF_NO_INTERFACE: 2, + + /** + * nsIAccessibilityService service. + */ + get accService() { + if (!this._accService) { + this._accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + } + + return this._accService; + }, + + clearAccService() { + this._accService = null; + Cu.forceGC(); + }, + + /** + * Adds an observer for an 'a11y-consumers-changed' event. + */ + addAccConsumersChangedObserver() { + const deferred = {}; + this._accConsumersChanged = new Promise(resolve => { + deferred.resolve = resolve; + }); + const observe = (subject, topic, data) => { + Services.obs.removeObserver(observe, "a11y-consumers-changed"); + deferred.resolve(JSON.parse(data)); + }; + Services.obs.addObserver(observe, "a11y-consumers-changed"); + }, + + /** + * Returns a promise that resolves when 'a11y-consumers-changed' event is + * fired. + * + * @return {Promise} + * event promise evaluating to event's data + */ + observeAccConsumersChanged() { + return this._accConsumersChanged; + }, + + /** + * Adds an observer for an 'a11y-init-or-shutdown' event with a value of "1" + * which indicates that an accessibility service is initialized in the current + * process. + */ + addAccServiceInitializedObserver() { + const deferred = {}; + this._accServiceInitialized = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + const observe = (subject, topic, data) => { + if (data === "1") { + Services.obs.removeObserver(observe, "a11y-init-or-shutdown"); + deferred.resolve(); + } else { + deferred.reject("Accessibility service is shutdown unexpectedly."); + } + }; + Services.obs.addObserver(observe, "a11y-init-or-shutdown"); + }, + + /** + * Returns a promise that resolves when an accessibility service is + * initialized in the current process. Otherwise (if the service is shutdown) + * the promise is rejected. + */ + observeAccServiceInitialized() { + return this._accServiceInitialized; + }, + + /** + * Adds an observer for an 'a11y-init-or-shutdown' event with a value of "0" + * which indicates that an accessibility service is shutdown in the current + * process. + */ + addAccServiceShutdownObserver() { + const deferred = {}; + this._accServiceShutdown = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + const observe = (subject, topic, data) => { + if (data === "0") { + Services.obs.removeObserver(observe, "a11y-init-or-shutdown"); + deferred.resolve(); + } else { + deferred.reject("Accessibility service is initialized unexpectedly."); + } + }; + Services.obs.addObserver(observe, "a11y-init-or-shutdown"); + }, + + /** + * Returns a promise that resolves when an accessibility service is shutdown + * in the current process. Otherwise (if the service is initialized) the + * promise is rejected. + */ + observeAccServiceShutdown() { + return this._accServiceShutdown; + }, + + /** + * Extract DOMNode id from an accessible. If the accessible is in the remote + * process, DOMNode is not present in parent process. However, if specified by + * the author, DOMNode id will be attached to an accessible object. + * + * @param {nsIAccessible} accessible accessible + * @return {String?} DOMNode id if available + */ + getAccessibleDOMNodeID(accessible) { + if (accessible instanceof Ci.nsIAccessibleDocument) { + // If accessible is a document, trying to find its document body id. + try { + return accessible.DOMNode.body.id; + } catch (e) { + /* This only works if accessible is not a proxy. */ + } + } + try { + return accessible.DOMNode.id; + } catch (e) { + /* This will fail if DOMNode is in different process. */ + } + try { + // When e10s is enabled, accessible will have an "id" property if its + // corresponding DOMNode has an id. If accessible is a document, its "id" + // property corresponds to the "id" of its body element. + return accessible.id; + } catch (e) { + /* This will fail if accessible is not a proxy. */ + } + + return null; + }, + + getObjAddress(obj) { + const exp = /native\s*@\s*(0x[a-f0-9]+)/g; + const match = exp.exec(obj.toString()); + if (match) { + return match[1]; + } + + return obj.toString(); + }, + + getNodePrettyName(node) { + try { + let tag = ""; + if (node.nodeType == Node.DOCUMENT_NODE) { + tag = "document"; + } else { + tag = node.localName; + if (node.nodeType == Node.ELEMENT_NODE && node.hasAttribute("id")) { + tag += `@id="${node.getAttribute("id")}"`; + } + } + + return `"${tag} node", address: ${this.getObjAddress(node)}`; + } catch (e) { + return `" no node info "`; + } + }, + + /** + * Convert role to human readable string. + */ + roleToString(role) { + return this.accService.getStringRole(role); + }, + + /** + * Shorten a long string if it exceeds MAX_TRIM_LENGTH. + * + * @param aString the string to shorten. + * + * @returns the shortened string. + */ + shortenString(str) { + if (str.length <= MAX_TRIM_LENGTH) { + return str; + } + + // Trim the string if its length is > MAX_TRIM_LENGTH characters. + const trimOffset = MAX_TRIM_LENGTH / 2; + + return `${str.substring(0, trimOffset - 1)}…${str.substring( + str.length - trimOffset, + str.length + )}`; + }, + + normalizeAccTreeObj(obj) { + const key = Object.keys(obj)[0]; + const roleName = `ROLE_${key}`; + if (roleName in Ci.nsIAccessibleRole) { + return { + role: Ci.nsIAccessibleRole[roleName], + children: obj[key], + }; + } + + return obj; + }, + + stringifyTree(obj) { + let text = this.roleToString(obj.role) + ": [ "; + if ("children" in obj) { + for (let i = 0; i < obj.children.length; i++) { + const c = this.normalizeAccTreeObj(obj.children[i]); + text += this.stringifyTree(c); + if (i < obj.children.length - 1) { + text += ", "; + } + } + } + + return `${text}] `; + }, + + /** + * Return pretty name for identifier, it may be ID, DOM node or accessible. + */ + prettyName(identifier) { + if (identifier instanceof Array) { + let msg = ""; + for (let idx = 0; idx < identifier.length; idx++) { + if (msg != "") { + msg += ", "; + } + + msg += this.prettyName(identifier[idx]); + } + return msg; + } + + if (identifier instanceof Ci.nsIAccessible) { + const acc = this.getAccessible(identifier); + const domID = this.getAccessibleDOMNodeID(acc); + let msg = "["; + try { + if (Services.appinfo.browserTabsRemoteAutostart) { + if (domID) { + msg += `DOM node id: ${domID}, `; + } + } else { + msg += `${this.getNodePrettyName(acc.DOMNode)}, `; + } + msg += `role: ${this.roleToString(acc.role)}`; + if (acc.name) { + msg += `, name: "${this.shortenString(acc.name)}"`; + } + } catch (e) { + msg += "defunct"; + } + + if (acc) { + msg += `, address: ${this.getObjAddress(acc)}`; + } + msg += "]"; + + return msg; + } + + if (Node.isInstance(identifier)) { + return `[ ${this.getNodePrettyName(identifier)} ]`; + } + + if (identifier && typeof identifier === "object") { + const treeObj = this.normalizeAccTreeObj(identifier); + if ("role" in treeObj) { + return `{ ${this.stringifyTree(treeObj)} }`; + } + + return JSON.stringify(identifier); + } + + return ` "${identifier}" `; + }, + + /** + * Return accessible for the given identifier (may be ID attribute or DOM + * element or accessible object) or null. + * + * @param accOrElmOrID + * identifier to get an accessible implementing the given interfaces + * @param aInterfaces + * [optional] the interface or an array interfaces to query it/them + * from obtained accessible + * @param elmObj + * [optional] object to store DOM element which accessible is obtained + * for + * @param doNotFailIf + * [optional] no error for special cases (see DONOTFAIL_IF_NO_ACC, + * DONOTFAIL_IF_NO_INTERFACE) + * @param doc + * [optional] document for when accOrElmOrID is an ID. + */ + getAccessible(accOrElmOrID, interfaces, elmObj, doNotFailIf, doc) { + if (!accOrElmOrID) { + return null; + } + + let elm = null; + if (accOrElmOrID instanceof Ci.nsIAccessible) { + try { + elm = accOrElmOrID.DOMNode; + } catch (e) {} + } else if (Node.isInstance(accOrElmOrID)) { + elm = accOrElmOrID; + } else { + elm = doc.getElementById(accOrElmOrID); + if (!elm) { + Assert.ok(false, `Can't get DOM element for ${accOrElmOrID}`); + return null; + } + } + + if (elmObj && typeof elmObj == "object") { + elmObj.value = elm; + } + + let acc = accOrElmOrID instanceof Ci.nsIAccessible ? accOrElmOrID : null; + if (!acc) { + try { + acc = this.accService.getAccessibleFor(elm); + } catch (e) {} + + if (!acc) { + if (!(doNotFailIf & this.DONOTFAIL_IF_NO_ACC)) { + Assert.ok( + false, + `Can't get accessible for ${this.prettyName(accOrElmOrID)}` + ); + } + + return null; + } + } + + if (!interfaces) { + return acc; + } + + if (!(interfaces instanceof Array)) { + interfaces = [interfaces]; + } + + for (let index = 0; index < interfaces.length; index++) { + if (acc instanceof interfaces[index]) { + continue; + } + + try { + acc.QueryInterface(interfaces[index]); + } catch (e) { + if (!(doNotFailIf & this.DONOTFAIL_IF_NO_INTERFACE)) { + Assert.ok( + false, + `Can't query ${interfaces[index]} for ${accOrElmOrID}` + ); + } + + return null; + } + } + + return acc; + }, + + /** + * Return the DOM node by identifier (may be accessible, DOM node or ID). + */ + getNode(accOrNodeOrID, doc) { + if (!accOrNodeOrID) { + return null; + } + + if (Node.isInstance(accOrNodeOrID)) { + return accOrNodeOrID; + } + + if (accOrNodeOrID instanceof Ci.nsIAccessible) { + return accOrNodeOrID.DOMNode; + } + + const node = doc.getElementById(accOrNodeOrID); + if (!node) { + Assert.ok(false, `Can't get DOM element for ${accOrNodeOrID}`); + return null; + } + + return node; + }, + + /** + * Return root accessible. + * + * @param {DOMNode} doc + * Chrome document. + * + * @return {nsIAccessible} + * Accessible object for chrome window. + */ + getRootAccessible(doc) { + const acc = this.getAccessible(doc); + return acc ? acc.rootDocument.QueryInterface(Ci.nsIAccessible) : null; + }, + + /** + * Analogy of SimpleTest.is function used to compare objects. + */ + isObject(obj, expectedObj, msg) { + if (obj == expectedObj) { + Assert.ok(true, msg); + return; + } + + Assert.ok( + false, + `${msg} - got "${this.prettyName(obj)}", expected "${this.prettyName( + expectedObj + )}"` + ); + }, +}; diff --git a/accessible/tests/browser/Layout.jsm b/accessible/tests/browser/Layout.jsm new file mode 100644 index 0000000000..d51644ec57 --- /dev/null +++ b/accessible/tests/browser/Layout.jsm @@ -0,0 +1,181 @@ +/* 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/. */ + +"use strict"; + +const EXPORTED_SYMBOLS = ["Layout"]; + +const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm"); +const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" +); + +const Layout = { + /** + * Zoom the given document. + */ + zoomDocument(doc, zoom) { + const bc = BrowsingContext.getFromWindow(doc.defaultView); + bc.fullZoom = zoom; + }, + + /** + * Set the relative resolution of this document. This is what apz does. + * On non-mobile platforms you won't see a visible change. + */ + setResolution(doc, zoom) { + const windowUtils = doc.defaultView.windowUtils; + windowUtils.setResolutionAndScaleTo(zoom); + }, + + /** + * Assert.is() function checking the expected value is within the range. + */ + isWithin(expected, got, within, msg) { + if (Math.abs(got - expected) <= within) { + Assert.ok(true, `${msg} - Got ${got}`); + } else { + Assert.ok( + false, + `${msg} - Got ${got}, expected ${expected} with error of ${within}` + ); + } + }, + + /** + * Return the accessible coordinates relative to the screen in device pixels. + */ + getPos(id) { + const accessible = CommonUtils.getAccessible(id); + const x = {}; + const y = {}; + accessible.getBounds(x, y, {}, {}); + + return [x.value, y.value]; + }, + + /** + * Return the accessible coordinates and size relative to the screen in device + * pixels. This methods also retrieves coordinates in CSS pixels and ensures that they + * match Dev pixels with a given device pixel ratio. + */ + getBounds(id, dpr) { + const accessible = CommonUtils.getAccessible(id); + const x = {}; + const y = {}; + const width = {}; + const height = {}; + const xInCSS = {}; + const yInCSS = {}; + const widthInCSS = {}; + const heightInCSS = {}; + accessible.getBounds(x, y, width, height); + accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS); + + this.isWithin( + x.value / dpr, + xInCSS.value, + 1, + "X in CSS pixels is calculated correctly" + ); + this.isWithin( + y.value / dpr, + yInCSS.value, + 1, + "Y in CSS pixels is calculated correctly" + ); + this.isWithin( + width.value / dpr, + widthInCSS.value, + 1, + "Width in CSS pixels is calculated correctly" + ); + this.isWithin( + height.value / dpr, + heightInCSS.value, + 1, + "Height in CSS pixels is calculated correctly" + ); + + return [x.value, y.value, width.value, height.value]; + }, + + getRangeExtents(id, startOffset, endOffset, coordOrigin) { + const hyperText = CommonUtils.getAccessible(id, [Ci.nsIAccessibleText]); + const x = {}; + const y = {}; + const width = {}; + const height = {}; + hyperText.getRangeExtents( + startOffset, + endOffset, + x, + y, + width, + height, + coordOrigin + ); + + return [x.value, y.value, width.value, height.value]; + }, + + CSSToDevicePixels(win, x, y, width, height) { + const winUtil = win.windowUtils; + const ratio = winUtil.screenPixelsPerCSSPixel; + + // CSS pixels and ratio can be not integer. Device pixels are always integer. + // Do our best and hope it works. + return [ + Math.round(x * ratio), + Math.round(y * ratio), + Math.round(width * ratio), + Math.round(height * ratio), + ]; + }, + + /** + * Return DOM node coordinates relative the screen and its size in device + * pixels. + */ + getBoundsForDOMElm(id, doc) { + let x = 0; + let y = 0; + let width = 0; + let height = 0; + + const elm = CommonUtils.getNode(id, doc); + const elmWindow = elm.ownerGlobal; + if (elm.localName == "area") { + const mapName = elm.parentNode.getAttribute("name"); + const selector = `[usemap="#${mapName}"]`; + const img = elm.ownerDocument.querySelector(selector); + + const areaCoords = elm.coords.split(","); + const areaX = parseInt(areaCoords[0], 10); + const areaY = parseInt(areaCoords[1], 10); + const areaWidth = parseInt(areaCoords[2], 10) - areaX; + const areaHeight = parseInt(areaCoords[3], 10) - areaY; + + const rect = img.getBoundingClientRect(); + x = rect.left + areaX; + y = rect.top + areaY; + width = areaWidth; + height = areaHeight; + } else { + const rect = elm.getBoundingClientRect(); + x = rect.left; + y = rect.top; + width = rect.width; + height = rect.height; + } + + return this.CSSToDevicePixels( + elmWindow, + x + elmWindow.mozInnerScreenX, + y + elmWindow.mozInnerScreenY, + width, + height + ); + }, +}; diff --git a/accessible/tests/browser/bounds/browser.ini b/accessible/tests/browser/bounds/browser.ini new file mode 100644 index 0000000000..1997e92ff1 --- /dev/null +++ b/accessible/tests/browser/bounds/browser.ini @@ -0,0 +1,13 @@ +[DEFAULT] +support-files = + head.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/browser/*.jsm + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + +[browser_test_resolution.js] +skip-if = e10s && os == 'win' # bug 1372296 +[browser_test_zoom.js] +[browser_test_zoom_text.js] +skip-if = e10s && os == 'win' # bug 1372296 diff --git a/accessible/tests/browser/bounds/browser_test_resolution.js b/accessible/tests/browser/bounds/browser_test_resolution.js new file mode 100644 index 0000000000..bdcb3e1792 --- /dev/null +++ b/accessible/tests/browser/bounds/browser_test_resolution.js @@ -0,0 +1,72 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ + +async function testScaledBounds(browser, accDoc, scale, id, type = "object") { + let acc = findAccessibleChildByID(accDoc, id); + + // Get document offset + let [docX, docY] = getBounds(accDoc); + + // Get the unscaled bounds of the accessible + let [x, y, width, height] = + type == "text" + ? getRangeExtents(acc, 0, -1, COORDTYPE_SCREEN_RELATIVE) + : getBounds(acc); + + await invokeContentTask(browser, [scale], _scale => { + const { Layout } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + Layout.setResolution(content.document, _scale); + }); + + let [scaledX, scaledY, scaledWidth, scaledHeight] = + type == "text" + ? getRangeExtents(acc, 0, -1, COORDTYPE_SCREEN_RELATIVE) + : getBounds(acc); + + let name = prettyName(acc); + isWithin(scaledWidth, width * scale, 2, "Wrong scaled width of " + name); + isWithin(scaledHeight, height * scale, 2, "Wrong scaled height of " + name); + isWithin(scaledX - docX, (x - docX) * scale, 2, "Wrong scaled x of " + name); + isWithin(scaledY - docY, (y - docY) * scale, 2, "Wrong scaled y of " + name); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + Layout.setResolution(content.document, 1.0); + }); +} + +async function runTests(browser, accDoc) { + // The scrollbars get in the way of container bounds calculation. + await SpecialPowers.pushPrefEnv({ + set: [["ui.useOverlayScrollbars", 1]], + }); + + await testScaledBounds(browser, accDoc, 2.0, "p1"); + await testScaledBounds(browser, accDoc, 0.5, "p2"); + await testScaledBounds(browser, accDoc, 3.5, "b1"); + + await testScaledBounds(browser, accDoc, 2.0, "p1", "text"); + await testScaledBounds(browser, accDoc, 0.75, "p2", "text"); +} + +/** + * Test accessible boundaries when page is zoomed + */ +addAccessibleTask( + ` +

Tilimilitryamdiya

+

para 2

+ +`, + runTests, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/bounds/browser_test_zoom.js b/accessible/tests/browser/bounds/browser_test_zoom.js new file mode 100644 index 0000000000..6600a17130 --- /dev/null +++ b/accessible/tests/browser/bounds/browser_test_zoom.js @@ -0,0 +1,69 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ + +async function testContentBounds(browser, acc) { + let [ + expectedX, + expectedY, + expectedWidth, + expectedHeight, + ] = await getContentBoundsForDOMElm(browser, getAccessibleDOMNodeID(acc)); + + let contentDPR = await getContentDPR(browser); + let [x, y, width, height] = getBounds(acc, contentDPR); + let prettyAccName = prettyName(acc); + is(x, expectedX, "Wrong x coordinate of " + prettyAccName); + is(y, expectedY, "Wrong y coordinate of " + prettyAccName); + is(width, expectedWidth, "Wrong width of " + prettyAccName); + ok(height >= expectedHeight, "Wrong height of " + prettyAccName); +} + +async function runTests(browser, accDoc) { + let p1 = findAccessibleChildByID(accDoc, "p1"); + let p2 = findAccessibleChildByID(accDoc, "p2"); + let imgmap = findAccessibleChildByID(accDoc, "imgmap"); + if (!imgmap.childCount) { + // An image map may not be available even after the doc and image load + // is complete. We don't recieve any DOM events for this change either, + // so we need to wait for a REORDER. + await waitForEvent(EVENT_REORDER, "imgmap"); + } + let area = imgmap.firstChild; + + await testContentBounds(browser, p1); + await testContentBounds(browser, p2); + await testContentBounds(browser, area); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + Layout.zoomDocument(content.document, 2.0); + }); + + await testContentBounds(browser, p1); + await testContentBounds(browser, p2); + await testContentBounds(browser, area); +} + +/** + * Test accessible boundaries when page is zoomed + */ +addAccessibleTask( + ` +

para 1

para 2

+ + mozilla.org + +`, + runTests, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/bounds/browser_test_zoom_text.js b/accessible/tests/browser/bounds/browser_test_zoom_text.js new file mode 100644 index 0000000000..8abbff025d --- /dev/null +++ b/accessible/tests/browser/bounds/browser_test_zoom_text.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ + +async function runTests(browser, accDoc) { + async function testTextNode(id) { + let hyperTextNode = findAccessibleChildByID(accDoc, id); + let textNode = hyperTextNode.firstChild; + + let contentDPR = await getContentDPR(browser); + let [x, y, width, height] = getBounds(textNode, contentDPR); + testTextBounds( + hyperTextNode, + 0, + -1, + [x, y, width, height], + COORDTYPE_SCREEN_RELATIVE + ); + } + + async function testEmptyInputNode(id) { + let inputNode = findAccessibleChildByID(accDoc, id); + + let [x, y, width, height] = getBounds(inputNode); + testTextBounds( + inputNode, + 0, + -1, + [x, y, width, height], + COORDTYPE_SCREEN_RELATIVE + ); + testTextBounds( + inputNode, + 0, + 0, + [x, y, width, height], + COORDTYPE_SCREEN_RELATIVE + ); + } + + await testTextNode("p1"); + await testTextNode("p2"); + await testEmptyInputNode("i1"); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + Layout.zoomDocument(content.document, 2.0); + }); + + await testTextNode("p1"); + + await invokeContentTask(browser, [], () => { + const { Layout } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm" + ); + Layout.zoomDocument(content.document, 1.0); + }); +} + +/** + * Test the text range boundary when page is zoomed + */ +addAccessibleTask( + ` +

Tilimilitryamdiya

+

ل

+
`, + runTests, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/bounds/head.js b/accessible/tests/browser/bounds/head.js new file mode 100644 index 0000000000..f4d20e636c --- /dev/null +++ b/accessible/tests/browser/bounds/head.js @@ -0,0 +1,21 @@ +/* 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/. */ + +"use strict"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "layout.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); diff --git a/accessible/tests/browser/browser.ini b/accessible/tests/browser/browser.ini new file mode 100644 index 0000000000..99acf3c0b0 --- /dev/null +++ b/accessible/tests/browser/browser.ini @@ -0,0 +1,34 @@ +[DEFAULT] +skip-if = a11y_checks || (os == 'win' && processor == 'aarch64') # 1534855 +support-files = + !/accessible/tests/mochitest/*.js + *.jsm + head.js + shared-head.js + +[browser_shutdown_acc_reference.js] +skip-if = (os == 'linux' && debug && bits == 64) #Bug 1421307 +[browser_shutdown_doc_acc_reference.js] +[browser_shutdown_multi_acc_reference_obj.js] +[browser_shutdown_multi_acc_reference_doc.js] +[browser_shutdown_multi_reference.js] +[browser_shutdown_parent_own_reference.js] +skip-if = !e10s || (verify && debug && (os == 'win')) # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_pref.js] +[browser_shutdown_proxy_acc_reference.js] +skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_proxy_doc_acc_reference.js] +skip-if = !e10s || (os == 'win') || (verify && debug) # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_multi_proxy_acc_reference_doc.js] +skip-if = !e10s || (os == 'win') || (verify && debug && (os == 'linux')) # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_multi_proxy_acc_reference_obj.js] +skip-if = !e10s || (os == 'win') || (verify && debug && (os == 'linux')) # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_remote_no_reference.js] +skip-if = !e10s || (verify && debug && (os == 'win')) # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_remote_only.js] +skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_remote_own_reference.js] +skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content. +[browser_shutdown_scope_lifecycle.js] +[browser_shutdown_start_restart.js] +skip-if = (verify && debug) diff --git a/accessible/tests/browser/browser_shutdown_acc_reference.js b/accessible/tests/browser/browser_shutdown_acc_reference.js new file mode 100644 index 0000000000..68c07ba2b6 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_acc_reference.js @@ -0,0 +1,64 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Create a11y service. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + await a11yInit; + ok(accService, "Service initialized"); + + // Accessible object reference will live longer than the scope of this + // function. + let acc = await new Promise(resolve => { + let intervalId = setInterval(() => { + let tabAcc = accService.getAccessibleFor(gBrowser.selectedTab); + if (tabAcc) { + clearInterval(intervalId); + resolve(tabAcc); + } + }, 10); + }); + ok(acc, "Accessible object is created"); + + let canShutdown = false; + // This promise will resolve only if canShutdown flag is set to true. If + // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut + // down, the promise will reject. + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible object. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a reference to an accessible object. + acc = null; + ok(!acc, "Accessible object is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_doc_acc_reference.js b/accessible/tests/browser/browser_shutdown_doc_acc_reference.js new file mode 100644 index 0000000000..baf2b898e5 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_doc_acc_reference.js @@ -0,0 +1,56 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Create a11y service. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + await a11yInit; + ok(accService, "Service initialized"); + + // Accessible document reference will live longer than the scope of this + // function. + let docAcc = accService.getAccessibleFor(document); + ok(docAcc, "Accessible document is created"); + + let canShutdown = false; + // This promise will resolve only if canShutdown flag is set to true. If + // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut + // down, the promise will reject. + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible document. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a reference to an accessible document. + docAcc = null; + ok(!docAcc, "Accessible document is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js b/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js new file mode 100644 index 0000000000..b67b2f46f7 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_multi_acc_reference_doc.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Create a11y service. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + await a11yInit; + ok(accService, "Service initialized"); + + let docAcc = accService.getAccessibleFor(document); + ok(docAcc, "Accessible document is created"); + + // Accessible object reference will live longer than the scope of this + // function. + let acc = await new Promise(resolve => { + let intervalId = setInterval(() => { + let tabAcc = accService.getAccessibleFor(gBrowser.selectedTab); + if (tabAcc) { + clearInterval(intervalId); + resolve(tabAcc); + } + }, 10); + }); + ok(acc, "Accessible object is created"); + + let canShutdown = false; + // This promise will resolve only if canShutdown flag is set to true. If + // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut + // down, the promise will reject. + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + + // Force garbage collection that should not trigger shutdown because there are + // references to accessible objects. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Remove a reference to an accessible object. + acc = null; + ok(!acc, "Accessible object is removed"); + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible document. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a reference to an accessible document. + docAcc = null; + ok(!docAcc, "Accessible document is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js b/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js new file mode 100644 index 0000000000..18160a8db7 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_multi_acc_reference_obj.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Create a11y service. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + await a11yInit; + ok(accService, "Service initialized"); + + let docAcc = accService.getAccessibleFor(document); + ok(docAcc, "Accessible document is created"); + + // Accessible object reference will live longer than the scope of this + // function. + let acc = await new Promise(resolve => { + let intervalId = setInterval(() => { + let tabAcc = accService.getAccessibleFor(gBrowser.selectedTab); + if (tabAcc) { + clearInterval(intervalId); + resolve(tabAcc); + } + }, 10); + }); + ok(acc, "Accessible object is created"); + + let canShutdown = false; + // This promise will resolve only if canShutdown flag is set to true. If + // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut + // down, the promise will reject. + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + + // Force garbage collection that should not trigger shutdown because there are + // references to accessible objects. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Remove a reference to an accessible document. + docAcc = null; + ok(!docAcc, "Accessible document is removed"); + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible object. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a reference to an accessible object. + acc = null; + ok(!acc, "Accessible object is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js new file mode 100644 index 0000000000..8763327bae --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_doc.js @@ -0,0 +1,90 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + let docLoaded = waitForEvent( + Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE, + "body" + ); + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized"); + await a11yInit; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + +
+ `, + }, + async function(browser) { + let docLoadedEvent = await docLoaded; + let docAcc = docLoadedEvent.accessibleDocument; + ok(docAcc, "Accessible document proxy is created"); + // Remove unnecessary dangling references + docLoaded = null; + docLoadedEvent = null; + forceGC(); + + let acc = docAcc.getChildAt(0); + ok(acc, "Accessible proxy is created"); + + let canShutdown = false; + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + // Force garbage collection that should not trigger shutdown because there + // is a reference to an accessible proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Remove a reference to an accessible proxy. + acc = null; + ok(!acc, "Accessible proxy is removed"); + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible document proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a last reference to an accessible document proxy. + docAcc = null; + ok(!docAcc, "Accessible document proxy is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; + } + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); +}); diff --git a/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js new file mode 100644 index 0000000000..5134901355 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_multi_proxy_acc_reference_obj.js @@ -0,0 +1,90 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + let docLoaded = waitForEvent( + Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE, + "body" + ); + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized"); + await a11yInit; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + +
+ `, + }, + async function(browser) { + let docLoadedEvent = await docLoaded; + let docAcc = docLoadedEvent.accessibleDocument; + ok(docAcc, "Accessible document proxy is created"); + // Remove unnecessary dangling references + docLoaded = null; + docLoadedEvent = null; + forceGC(); + + let acc = docAcc.getChildAt(0); + ok(acc, "Accessible proxy is created"); + + let canShutdown = false; + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + // Force garbage collection that should not trigger shutdown because there + // is a reference to an accessible proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Remove a reference to an accessible document proxy. + docAcc = null; + ok(!docAcc, "Accessible document proxy is removed"); + // Force garbage collection that should not trigger shutdown because there is + // a reference to an accessible proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a last reference to an accessible proxy. + acc = null; + ok(!acc, "Accessible proxy is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; + } + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); +}); diff --git a/accessible/tests/browser/browser_shutdown_multi_reference.js b/accessible/tests/browser/browser_shutdown_multi_reference.js new file mode 100644 index 0000000000..cd0bc0d103 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_multi_reference.js @@ -0,0 +1,58 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + info("Creating a service"); + // Create a11y service. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService1 = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + await a11yInit; + ok(accService1, "Service initialized"); + + // Add another reference to a11y service. This will not trigger + // 'a11y-init-or-shutdown' event + let accService2 = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService2, "Service initialized"); + + info("Removing all service references"); + let canShutdown = false; + // This promise will resolve only if canShutdown flag is set to true. If + // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut + // down, the promise will reject. + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + // Remove first a11y service reference. + accService1 = null; + ok(!accService1, "Service is removed"); + // Force garbage collection that should not trigger shutdown because there is + // another reference. + forceGC(); + + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove last a11y service reference. + accService2 = null; + ok(!accService2, "Service is removed"); + // Force garbage collection that should trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_parent_own_reference.js b/accessible/tests/browser/browser_shutdown_parent_own_reference.js new file mode 100644 index 0000000000..86115fdaad --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_parent_own_reference.js @@ -0,0 +1,104 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + info( + "Creating a service in parent and waiting for service to be created " + + "in content" + ); + await loadContentScripts(browser, "Common.jsm"); + // Create a11y service in the main process. This will trigger creating of + // the a11y service in parent as well. + const [parentA11yInitObserver, parentA11yInit] = initAccService(); + const [contentA11yInitObserver, contentA11yInit] = initAccService( + browser + ); + + await Promise.all([parentA11yInitObserver, contentA11yInitObserver]); + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized in parent"); + await Promise.all([parentA11yInit, contentA11yInit]); + + info( + "Adding additional reference to accessibility service in content " + + "process" + ); + // Add a new reference to the a11y service inside the content process. + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.accService; + }); + + info( + "Trying to shut down a service in content and making sure it stays " + + "alive as it was started by parent" + ); + let contentCanShutdown = false; + // This promise will resolve only if contentCanShutdown flag is set to true. + // If 'a11y-init-or-shutdown' event with '0' flag (in content) comes before + // it can be shut down, the promise will reject. + const [ + contentA11yShutdownObserver, + contentA11yShutdownPromise, + ] = shutdownAccService(browser); + await contentA11yShutdownObserver; + const contentA11yShutdown = new Promise((resolve, reject) => + contentA11yShutdownPromise.then(flag => + contentCanShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + // Remove a11y service reference in content and force garbage collection. + // This should not trigger shutdown since a11y was originally initialized by + // the main process. + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.clearAccService(); + }); + + // Have some breathing room between a11y service shutdowns. + await TestUtils.waitForTick(); + + info("Removing a service in parent"); + // Now allow a11y service to shutdown in content. + contentCanShutdown = true; + // Remove the a11y service reference in the main process. + const [ + parentA11yShutdownObserver, + parentA11yShutdown, + ] = shutdownAccService(); + await parentA11yShutdownObserver; + + accService = null; + ok(!accService, "Service is removed in parent"); + // Force garbage collection that should trigger shutdown in both parent and + // content. + forceGC(); + await Promise.all([parentA11yShutdown, contentA11yShutdown]); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); + } + ); +}); diff --git a/accessible/tests/browser/browser_shutdown_pref.js b/accessible/tests/browser/browser_shutdown_pref.js new file mode 100644 index 0000000000..74cef28b03 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_pref.js @@ -0,0 +1,72 @@ +/* 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/. */ + +"use strict"; + +const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled"; + +add_task(async function testForceDisable() { + ok( + !Services.appinfo.accessibilityEnabled, + "Accessibility is disabled by default" + ); + + info("Reset force disabled preference"); + Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED); + + info("Enable accessibility service via XPCOM"); + let [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + await a11yInit; + ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled"); + + info("Force disable a11y service via preference"); + let [a11yShutdownObserver, a11yShutdown] = shutdownAccService(); + await a11yShutdownObserver; + + Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1); + await a11yShutdown; + ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled"); + + info("Attempt to get an instance of a11y service and call its method."); + accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + try { + accService.getAccesssibleFor(document); + ok(false, "getAccesssibleFor should've triggered an exception."); + } catch (e) { + ok( + true, + "getAccesssibleFor triggers an exception as a11y service is shutdown." + ); + } + ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled"); + + info("Reset force disabled preference"); + Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED); + + info("Create a11y service again"); + [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + await a11yInit; + ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled"); + + info("Remove all references to a11y service"); + [a11yShutdownObserver, a11yShutdown] = shutdownAccService(); + await a11yShutdownObserver; + + accService = null; + forceGC(); + await a11yShutdown; + ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled"); +}); diff --git a/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js b/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js new file mode 100644 index 0000000000..d6fa715cf3 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_proxy_acc_reference.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized"); + await a11yInit; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + let onShow = waitForEvent(Ci.nsIAccessibleEvent.EVENT_SHOW, "div"); + await invokeSetStyle(browser, "div", "visibility", "visible"); + let showEvent = await onShow; + let divAcc = showEvent.accessible; + ok(divAcc, "Accessible proxy is created"); + // Remove unnecessary dangling references + onShow = null; + showEvent = null; + forceGC(); + + let canShutdown = false; + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + // Force garbage collection that should not trigger shutdown because there + // is a reference to an accessible proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a last reference to an accessible proxy. + divAcc = null; + ok(!divAcc, "Accessible proxy is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; + } + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); +}); diff --git a/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js b/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js new file mode 100644 index 0000000000..1dc2344acb --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_proxy_doc_acc_reference.js @@ -0,0 +1,78 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + let docLoaded = waitForEvent( + Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE, + "body" + ); + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized"); + await a11yInit; + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + let docLoadedEvent = await docLoaded; + let docAcc = docLoadedEvent.accessibleDocument; + ok(docAcc, "Accessible document proxy is created"); + // Remove unnecessary dangling references + docLoaded = null; + docLoadedEvent = null; + forceGC(); + + let canShutdown = false; + const [a11yShutdownObserver, a11yShutdownPromise] = shutdownAccService(); + await a11yShutdownObserver; + const a11yShutdown = new Promise((resolve, reject) => + a11yShutdownPromise.then(flag => + canShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + accService = null; + ok(!accService, "Service is removed"); + // Force garbage collection that should not trigger shutdown because there + // is a reference to an accessible proxy. + forceGC(); + // Have some breathing room when removing a11y service references. + await TestUtils.waitForTick(); + + // Now allow a11y service to shutdown. + canShutdown = true; + // Remove a last reference to an accessible document proxy. + docAcc = null; + ok(!docAcc, "Accessible document proxy is removed"); + + // Force garbage collection that should now trigger shutdown. + forceGC(); + await a11yShutdown; + } + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); +}); diff --git a/accessible/tests/browser/browser_shutdown_remote_no_reference.js b/accessible/tests/browser/browser_shutdown_remote_no_reference.js new file mode 100644 index 0000000000..cce695579d --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_remote_no_reference.js @@ -0,0 +1,151 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + info( + "Creating a service in parent and waiting for service to be created " + + "in content" + ); + await loadContentScripts(browser, "Common.jsm"); + // Create a11y service in the main process. This will trigger creating of + // the a11y service in parent as well. + const [parentA11yInitObserver, parentA11yInit] = initAccService(); + const [contentA11yInitObserver, contentA11yInit] = initAccService( + browser + ); + let [ + parentConsumersChangedObserver, + parentConsumersChanged, + ] = accConsumersChanged(); + let [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + + await Promise.all([ + parentA11yInitObserver, + contentA11yInitObserver, + parentConsumersChangedObserver, + contentConsumersChangedObserver, + ]); + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized in parent"); + await Promise.all([parentA11yInit, contentA11yInit]); + await parentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: true, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers in parent are correct." + ) + ); + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: false, + MainProcess: true, + PlatformAPI: false, + }, + "Accessibility service consumers in content are correct." + ) + ); + + Assert.deepEqual( + JSON.parse(accService.getConsumers()), + { + XPCOM: true, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers in parent are correct." + ); + + info( + "Removing a service in parent and waiting for service to be shut " + + "down in content" + ); + // Remove a11y service reference in the main process. + const [ + parentA11yShutdownObserver, + parentA11yShutdown, + ] = shutdownAccService(); + const [ + contentA11yShutdownObserver, + contentA11yShutdown, + ] = shutdownAccService(browser); + [ + parentConsumersChangedObserver, + parentConsumersChanged, + ] = accConsumersChanged(); + [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + + await Promise.all([ + parentA11yShutdownObserver, + contentA11yShutdownObserver, + parentConsumersChangedObserver, + contentConsumersChangedObserver, + ]); + + accService = null; + ok(!accService, "Service is removed in parent"); + // Force garbage collection that should trigger shutdown in both main and + // content process. + forceGC(); + await Promise.all([parentA11yShutdown, contentA11yShutdown]); + await parentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: false, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers are correct." + ) + ); + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: false, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers are correct." + ) + ); + } + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); +}); diff --git a/accessible/tests/browser/browser_shutdown_remote_only.js b/accessible/tests/browser/browser_shutdown_remote_only.js new file mode 100644 index 0000000000..8798170c05 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_remote_only.js @@ -0,0 +1,56 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + info("Creating a service in content"); + await loadContentScripts(browser, "Common.jsm"); + // Create a11y service in the content process. + const [a11yInitObserver, a11yInit] = initAccService(browser); + await a11yInitObserver; + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.accService; + }); + await a11yInit; + ok( + true, + "Accessibility service is started in content process correctly." + ); + + info("Removing a service in content"); + // Remove a11y service reference from the content process. + const [a11yShutdownObserver, a11yShutdown] = shutdownAccService(browser); + await a11yShutdownObserver; + // Force garbage collection that should trigger shutdown. + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.clearAccService(); + }); + await a11yShutdown; + ok( + true, + "Accessibility service is shutdown in content process correctly." + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); + } + ); +}); diff --git a/accessible/tests/browser/browser_shutdown_remote_own_reference.js b/accessible/tests/browser/browser_shutdown_remote_own_reference.js new file mode 100644 index 0000000000..2d88e5ed85 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_remote_own_reference.js @@ -0,0 +1,191 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Making sure that the e10s is enabled on Windows for testing. + await setE10sPrefs(); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: `data:text/html, + + + + Accessibility Test + + + `, + }, + async function(browser) { + info( + "Creating a service in parent and waiting for service to be created " + + "in content" + ); + await loadContentScripts(browser, "Common.jsm"); + // Create a11y service in the main process. This will trigger creating of + // the a11y service in parent as well. + const [parentA11yInitObserver, parentA11yInit] = initAccService(); + const [contentA11yInitObserver, contentA11yInit] = initAccService( + browser + ); + let [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + + await Promise.all([ + parentA11yInitObserver, + contentA11yInitObserver, + contentConsumersChangedObserver, + ]); + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized in parent"); + await Promise.all([parentA11yInit, contentA11yInit]); + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: false, + MainProcess: true, + PlatformAPI: false, + }, + "Accessibility service consumers in content are correct." + ) + ); + + info( + "Adding additional reference to accessibility service in content " + + "process" + ); + [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + await contentConsumersChangedObserver; + // Add a new reference to the a11y service inside the content process. + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.accService; + }); + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: true, + MainProcess: true, + PlatformAPI: false, + }, + "Accessibility service consumers in content are correct." + ) + ); + + const contentConsumers = await SpecialPowers.spawn(browser, [], () => + content.CommonUtils.accService.getConsumers() + ); + Assert.deepEqual( + JSON.parse(contentConsumers), + { + XPCOM: true, + MainProcess: true, + PlatformAPI: false, + }, + "Accessibility service consumers in parent are correct." + ); + + info( + "Shutting down a service in parent and making sure the one in " + + "content stays alive" + ); + let contentCanShutdown = false; + const [ + parentA11yShutdownObserver, + parentA11yShutdown, + ] = shutdownAccService(); + [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + // This promise will resolve only if contentCanShutdown flag is set to true. + // If 'a11y-init-or-shutdown' event with '0' flag (in content) comes before + // it can be shut down, the promise will reject. + const [ + contentA11yShutdownObserver, + contentA11yShutdownPromise, + ] = shutdownAccService(browser); + const contentA11yShutdown = new Promise((resolve, reject) => + contentA11yShutdownPromise.then(flag => + contentCanShutdown + ? resolve() + : reject("Accessible service was shut down incorrectly") + ) + ); + + await Promise.all([ + parentA11yShutdownObserver, + contentA11yShutdownObserver, + contentConsumersChangedObserver, + ]); + // Remove a11y service reference in the main process and force garbage + // collection. This should not trigger shutdown in content since a11y + // service is used by XPCOM. + accService = null; + ok(!accService, "Service is removed in parent"); + // Force garbage collection that should not trigger shutdown because there + // is a reference in a content process. + forceGC(); + await SpecialPowers.spawn(browser, [], () => { + SpecialPowers.Cu.forceGC(); + }); + await parentA11yShutdown; + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: true, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers in content are correct." + ) + ); + + // Have some breathing room between a11y service shutdowns. + await TestUtils.waitForTick(); + + info("Removing a service in content"); + // Now allow a11y service to shutdown in content. + contentCanShutdown = true; + [ + contentConsumersChangedObserver, + contentConsumersChanged, + ] = accConsumersChanged(browser); + await contentConsumersChangedObserver; + // Remove last reference to a11y service in content and force garbage + // collection that should trigger shutdown. + await SpecialPowers.spawn(browser, [], () => { + content.CommonUtils.clearAccService(); + }); + await contentA11yShutdown; + await contentConsumersChanged.then(data => + Assert.deepEqual( + data, + { + XPCOM: false, + MainProcess: false, + PlatformAPI: false, + }, + "Accessibility service consumers in content are correct." + ) + ); + + // Unsetting e10s related preferences. + await unsetE10sPrefs(); + } + ); +}); diff --git a/accessible/tests/browser/browser_shutdown_scope_lifecycle.js b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js new file mode 100644 index 0000000000..b4dad44de8 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js @@ -0,0 +1,28 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + // Create a11y service inside of the function scope. Its reference should be + // released once the anonimous function is called. + const [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + const a11yInitThenShutdown = a11yInit.then(async () => { + const [a11yShutdownObserver, a11yShutdown] = shutdownAccService(); + await a11yShutdownObserver; + return a11yShutdown; + }); + + (function() { + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + ok(accService, "Service initialized"); + })(); + + // Force garbage collection that should trigger shutdown. + forceGC(); + await a11yInitThenShutdown; +}); diff --git a/accessible/tests/browser/browser_shutdown_start_restart.js b/accessible/tests/browser/browser_shutdown_start_restart.js new file mode 100644 index 0000000000..bac7a61da7 --- /dev/null +++ b/accessible/tests/browser/browser_shutdown_start_restart.js @@ -0,0 +1,51 @@ +/* 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/. */ + +"use strict"; + +add_task(async function() { + info("Creating a service"); + // Create a11y service. + let [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + let accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + await a11yInit; + ok(accService, "Service initialized"); + + info("Removing a service"); + // Remove the only reference to an a11y service. + let [a11yShutdownObserver, a11yShutdown] = shutdownAccService(); + await a11yShutdownObserver; + + accService = null; + ok(!accService, "Service is removed"); + // Force garbage collection that should trigger shutdown. + forceGC(); + await a11yShutdown; + + info("Recreating a service"); + // Re-create a11y service. + [a11yInitObserver, a11yInit] = initAccService(); + await a11yInitObserver; + + accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + await a11yInit; + ok(accService, "Service initialized again"); + + info("Removing a service again"); + // Remove the only reference to an a11y service again. + [a11yShutdownObserver, a11yShutdown] = shutdownAccService(); + await a11yShutdownObserver; + + accService = null; + ok(!accService, "Service is removed again"); + // Force garbage collection that should trigger shutdown. + forceGC(); + await a11yShutdown; +}); diff --git a/accessible/tests/browser/e10s/browser.ini b/accessible/tests/browser/e10s/browser.ini new file mode 100644 index 0000000000..a582b9b8bb --- /dev/null +++ b/accessible/tests/browser/e10s/browser.ini @@ -0,0 +1,60 @@ +[DEFAULT] +support-files = + head.js + doc_treeupdate_ariadialog.html + doc_treeupdate_ariaowns.html + doc_treeupdate_imagemap.html + doc_treeupdate_removal.xhtml + doc_treeupdate_visibility.html + doc_treeupdate_whitespace.html + !/accessible/tests/browser/shared-head.js + !/accessible/tests/browser/*.jsm + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png + +# Caching tests +[browser_caching_attributes.js] +[browser_caching_description.js] +[browser_caching_name.js] +skip-if = (os == "linux" && bits == 64) || (debug && os == "mac") || (debug && os == "win") #Bug 1388256 +[browser_caching_relations.js] +[browser_caching_states.js] +[browser_caching_value.js] +[browser_caching_uniqueid.js] + +# Events tests +[browser_events_announcement.js] +skip-if = e10s && os == 'win' # Bug 1288839 +[browser_events_caretmove.js] +[browser_events_hide.js] +[browser_events_show.js] +[browser_events_statechange.js] +[browser_events_textchange.js] +[browser_events_vcchange.js] + +# Text tests +[browser_text_paragraph_boundary.js] + +# Tree update tests +[browser_treeupdate_ariadialog.js] +[browser_treeupdate_ariaowns.js] +[browser_treeupdate_canvas.js] +skip-if = (os == 'win' && os_version == '10.0' && bits == 64 && !debug) #Bug 1462638 - Disabled on Win10 opt/pgo for frequent failures +[browser_treeupdate_cssoverflow.js] +[browser_treeupdate_doc.js] +skip-if = e10s && os == 'win' # Bug 1288839 +[browser_treeupdate_gencontent.js] +[browser_treeupdate_hidden.js] +[browser_treeupdate_imagemap.js] +[browser_treeupdate_list.js] +[browser_treeupdate_list_editabledoc.js] +[browser_treeupdate_listener.js] +[browser_treeupdate_optgroup.js] +[browser_treeupdate_removal.js] +[browser_treeupdate_select_dropdown.js] +[browser_treeupdate_table.js] +[browser_treeupdate_textleaf.js] +[browser_treeupdate_visibility.js] +[browser_treeupdate_whitespace.js] +skip-if = true # Failing due to incorrect index of test container children on document load. diff --git a/accessible/tests/browser/e10s/browser_caching_attributes.js b/accessible/tests/browser/e10s/browser_caching_attributes.js new file mode 100644 index 0000000000..b8e157b068 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_attributes.js @@ -0,0 +1,134 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/attributes.js */ +loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); + +/** + * Default textbox accessible attributes. + */ +const defaultAttributes = { + "margin-top": "0px", + "margin-right": "0px", + "margin-bottom": "0px", + "margin-left": "0px", + "text-align": "start", + "text-indent": "0px", + id: "textbox", + tag: "input", + display: "inline-block", +}; + +/** + * Test data has the format of: + * { + * desc {String} description for better logging + * expected {Object} expected attributes for given accessibles + * unexpected {Object} unexpected attributes for given accessibles + * + * action {?AsyncFunction} an optional action that awaits a change in + * attributes + * attrs {?Array} an optional list of attributes to update + * waitFor {?Number} an optional event to wait for + * } + */ +const attributesTests = [ + { + desc: "Initiall accessible attributes", + expected: defaultAttributes, + unexpected: { + "line-number": "1", + "explicit-name": "true", + "container-live": "polite", + live: "polite", + }, + }, + { + desc: "@line-number attribute is present when textbox is focused", + async action(browser) { + await invokeFocus(browser, "textbox"); + }, + waitFor: EVENT_FOCUS, + expected: Object.assign({}, defaultAttributes, { "line-number": "1" }), + unexpected: { + "explicit-name": "true", + "container-live": "polite", + live: "polite", + }, + }, + { + desc: "@aria-live sets container-live and live attributes", + attrs: [ + { + attr: "aria-live", + value: "polite", + }, + ], + expected: Object.assign({}, defaultAttributes, { + "line-number": "1", + "container-live": "polite", + live: "polite", + }), + unexpected: { + "explicit-name": "true", + }, + }, + { + desc: "@title attribute sets explicit-name attribute to true", + attrs: [ + { + attr: "title", + value: "textbox", + }, + ], + expected: Object.assign({}, defaultAttributes, { + "line-number": "1", + "explicit-name": "true", + "container-live": "polite", + live: "polite", + }), + unexpected: {}, + }, +]; + +/** + * Test caching of accessible object attributes + */ +addAccessibleTask( + ` + `, + async function(browser, accDoc) { + let textbox = findAccessibleChildByID(accDoc, "textbox"); + for (let { + desc, + action, + attrs, + expected, + waitFor, + unexpected, + } of attributesTests) { + info(desc); + let onUpdate; + + if (waitFor) { + onUpdate = waitForEvent(waitFor, "textbox"); + } + + if (action) { + await action(browser); + } else if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, "textbox", attr, value); + } + } + + await onUpdate; + testAttrs(textbox, expected); + testAbsentAttrs(textbox, unexpected); + } + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_description.js b/accessible/tests/browser/e10s/browser_caching_description.js new file mode 100644 index 0000000000..5b9e70ce07 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_description.js @@ -0,0 +1,214 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/name.js */ +loadScripts({ name: "name.js", dir: MOCHITESTS_DIR }); + +/** + * Test data has the format of: + * { + * desc {String} description for better logging + * expected {String} expected description value for a given accessible + * attrs {?Array} an optional list of attributes to update + * waitFor {?Array} an optional list of accessible events to wait for when + * attributes are updated + * } + */ +const tests = [ + { + desc: "No description when there are no @alt, @title and @aria-describedby", + expected: "", + }, + { + desc: "Description from @aria-describedby attribute", + attrs: [ + { + attr: "aria-describedby", + value: "description", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "aria description", + }, + { + desc: + "No description from @aria-describedby since it is the same as the " + + "@alt attribute which is used as the name", + attrs: [ + { + attr: "alt", + value: "aria description", + }, + ], + waitFor: [[EVENT_REORDER, matchContentDoc]], + expected: "", + }, + { + desc: + "Description from @aria-describedby attribute when @alt and " + + "@aria-describedby are not the same", + attrs: [ + { + attr: "aria-describedby", + value: "description2", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "another description", + }, + { + desc: + "Description from @aria-describedby attribute when @title (used for " + + "name) and @aria-describedby are not the same", + attrs: [ + { + attr: "alt", + }, + { + attr: "title", + value: "title", + }, + ], + waitFor: [[EVENT_REORDER, matchContentDoc]], + expected: "another description", + }, + { + desc: + "No description from @aria-describedby since it is the same as the " + + "@title attribute which is used as the name", + attrs: [ + { + attr: "title", + value: "another description", + }, + ], + waitFor: [[EVENT_NAME_CHANGE, "image"]], + expected: "", + }, + { + desc: "No description with only @title attribute which is used as the name", + attrs: [ + { + attr: "aria-describedby", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "", + }, + { + desc: + "Description from @title attribute when @alt and @atitle are not the " + + "same", + attrs: [ + { + attr: "alt", + value: "aria description", + }, + ], + waitFor: [[EVENT_REORDER, matchContentDoc]], + expected: "another description", + }, + { + desc: + "No description from @title since it is the same as the @alt " + + "attribute which is used as the name", + attrs: [ + { + attr: "alt", + value: "another description", + }, + ], + waitFor: [[EVENT_NAME_CHANGE, "image"]], + expected: "", + }, + { + desc: + "No description from @aria-describedby since it is the same as the " + + "@alt (used for name) and @title attributes", + attrs: [ + { + attr: "aria-describedby", + value: "description2", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "", + }, + { + desc: + "Description from @aria-describedby attribute when it is different " + + "from @alt (used for name) and @title attributes", + attrs: [ + { + attr: "aria-describedby", + value: "description", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "aria description", + }, + { + desc: + "No description from @aria-describedby since it is the same as the " + + "@alt attribute (used for name) but different from title", + attrs: [ + { + attr: "alt", + value: "aria description", + }, + ], + waitFor: [[EVENT_NAME_CHANGE, "image"]], + expected: "", + }, + { + desc: + "Description from @aria-describedby attribute when @alt (used for " + + "name) and @aria-describedby are not the same but @title and " + + "aria-describedby are", + attrs: [ + { + attr: "aria-describedby", + value: "description2", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "another description", + }, +]; + +/** + * Test caching of accessible object description + */ +addAccessibleTask( + ` +

aria description

+

another description

+ `, + async function(browser, accDoc) { + let imgAcc = findAccessibleChildByID(accDoc, "image"); + + for (let { desc, waitFor, attrs, expected } of tests) { + info(desc); + let onUpdate; + if (waitFor) { + onUpdate = waitForOrderedEvents(waitFor); + } + if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, "image", attr, value); + } + } + await onUpdate; + // When attribute change (alt) triggers reorder event, accessible will + // become defunct. + if (isDefunct(imgAcc)) { + imgAcc = findAccessibleChildByID(accDoc, "image"); + } + testDescr(imgAcc, expected); + } + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_name.js b/accessible/tests/browser/e10s/browser_caching_name.js new file mode 100644 index 0000000000..cac05a04de --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_name.js @@ -0,0 +1,539 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/name.js */ +loadScripts({ name: "name.js", dir: MOCHITESTS_DIR }); + +/** + * Rules for name tests that are inspired by + * accessible/tests/mochitest/name/markuprules.xul + * + * Each element in the list of rules represents a name calculation rule for a + * particular test case. + * + * The rules have the following format: + * { attr } - calculated from attribute + * { elm } - calculated from another element + * { fromsubtree } - calculated from element's subtree + * + * + * Options include: + * * waitFor - changes in the subtree will result in an accessible event + * being fired, the test must only continue after the event + * is receieved. + */ +const ARIARule = [ + { attr: "aria-labelledby" }, + { attr: "aria-label", waitFor: EVENT_NAME_CHANGE }, +]; +const HTMLControlHeadRule = [...ARIARule, { elm: "label", isSibling: true }]; +const rules = { + CSSContent: [{ elm: "style", isSibling: true }, { fromsubtree: true }], + HTMLARIAGridCell: [...ARIARule, { fromsubtree: true }, { attr: "title" }], + HTMLControl: [ + ...HTMLControlHeadRule, + { fromsubtree: true }, + { attr: "title" }, + ], + HTMLElm: [...ARIARule, { attr: "title" }], + HTMLImg: [ + ...ARIARule, + { attr: "alt", waitFor: EVENT_NAME_CHANGE }, + { attr: "title" }, + ], + HTMLImgEmptyAlt: [...ARIARule, { attr: "title" }, { attr: "alt" }], + HTMLInputButton: [ + ...HTMLControlHeadRule, + { attr: "value" }, + { attr: "title" }, + ], + HTMLInputImage: [ + ...HTMLControlHeadRule, + { attr: "alt", waitFor: EVENT_NAME_CHANGE }, + { attr: "value" }, + { attr: "title" }, + ], + HTMLInputImageNoValidSrc: [ + ...HTMLControlHeadRule, + { attr: "alt", waitFor: EVENT_NAME_CHANGE }, + { attr: "value" }, + ], + HTMLInputReset: [ + ...HTMLControlHeadRule, + { attr: "value", waitFor: EVENT_TEXT_INSERTED }, + ], + HTMLInputSubmit: [ + ...HTMLControlHeadRule, + { attr: "value", waitFor: EVENT_TEXT_INSERTED }, + ], + HTMLLink: [...ARIARule, { fromsubtree: true }, { attr: "title" }], + HTMLLinkImage: [...ARIARule, { elm: "img" }, { attr: "title" }], + HTMLOption: [ + ...ARIARule, + { attr: "label" }, + { fromsubtree: true }, + { attr: "title" }, + ], + HTMLTable: [ + ...ARIARule, + { elm: "caption" }, + { attr: "summary" }, + { attr: "title" }, + ], +}; + +const markupTests = [ + { + id: "btn", + ruleset: "HTMLControl", + markup: ` + test2 + test3 + + `, + expected: ["test2 test3", "test1", "test4", "press me", "test5"], + }, + { + id: "btn", + ruleset: "HTMLInputButton", + markup: ` + test2 + test3 + + `, + expected: [ + "test2 test3", + "test1", + "test4", + "name from value", + "name from title", + ], + }, + { + id: "btn-submit", + ruleset: "HTMLInputSubmit", + markup: ` + test2 + test3 + + `, + expected: ["test2 test3", "test1", "test4", "name from value"], + }, + { + id: "btn-reset", + ruleset: "HTMLInputReset", + markup: ` + test2 + test3 + + `, + expected: ["test2 test3", "test1", "test4", "name from value"], + }, + { + id: "btn-image", + ruleset: "HTMLInputImage", + markup: ` + test2 + test3 + + `, + expected: [ + "test2 test3", + "test1", + "test4", + "name from alt", + "name from value", + "name from title", + ], + }, + { + id: "btn-image", + ruleset: "HTMLInputImageNoValidSrc", + markup: ` + test2 + test3 + + `, + expected: [ + "test2 test3", + "test1", + "test4", + "name from alt", + "name from value", + ], + }, + { + id: "opt", + ruleset: "HTMLOption", + markup: ` + test2 + test3 + `, + expected: ["test2 test3", "test1", "test4", "option1", "test5"], + }, + { + id: "img", + ruleset: "HTMLImg", + markup: ` + test2 + test3 + Mozilla logo`, + expected: [ + "test2 test3", + "Logo of Mozilla", + "Mozilla logo", + "This is a logo", + ], + }, + { + id: "imgemptyalt", + ruleset: "HTMLImgEmptyAlt", + markup: ` + test2 + test3 + `, + expected: ["test2 test3", "Logo of Mozilla", "This is a logo", ""], + }, + { + id: "tc", + ruleset: "HTMLElm", + markup: ` + test2 + test3 + + + + + +
+

This is a paragraph

+ This is a link +
    +
  • This is a list
  • +
+
`, + expected: ["test2 test3", "test1", "test5"], + }, + { + id: "gc", + ruleset: "HTMLARIAGridCell", + markup: ` + test2 + test3 + + + + + +
+

This is a paragraph

+ This is a link +
    +
  • Listitem1
  • +
  • Listitem2
  • +
+
`, + expected: [ + "test2 test3", + "test1", + "This is a paragraph This is a link \u2022 Listitem1 \u2022 Listitem2", + "This is a paragraph This is a link This is a list", + ], + }, + { + id: "t", + ruleset: "HTMLTable", + markup: ` + lby_tst6_1 + lby_tst6_2 + + + + + + + +
caption_tst6
cell1cell2
`, + expected: [ + "lby_tst6_1 lby_tst6_2", + "arialabel_tst6", + "caption_tst6", + "summary_tst6", + "title_tst6", + ], + }, + { + id: "btn", + ruleset: "CSSContent", + markup: ` +
+ + +
`, + expected: ["do not press me", "press me"], + }, + { + // TODO: uncomment when Bug-1256382 is resoved. + // id: 'li', + // ruleset: 'CSSContent', + // markup: ` + // + // `, + // expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`] + // }, { + id: "a", + ruleset: "HTMLLink", + markup: ` + test2 + test3 + test5`, + expected: ["test2 test3", "test1", "test5", "test4"], + }, + { + id: "a-img", + ruleset: "HTMLLinkImage", + markup: ` + test2 + test3 + test5`, + expected: ["test2 test3", "test1", "test5", "test4"], + }, +]; + +/** + * Test accessible name that is calculated from an attribute, remove the + * attribute before proceeding to the next name test. If attribute removal + * results in a reorder or text inserted event - wait for it. If accessible + * becomes defunct, update its reference using the one that is attached to one + * of the above events. + * @param {Object} browser current "tabbrowser" element + * @param {Object} target { acc, parent, id } structure that contains an + * accessible, its parent and its content element + * id. + * @param {Object} rule current attr rule for name calculation + * @param {[type]} expected expected name value + */ +async function testAttrRule(browser, target, rule, expected) { + let { id, parent, acc } = target; + let { waitFor, attr } = rule; + + testName(acc, expected); + + if (waitFor) { + let [event] = await contentSpawnMutation( + browser, + { + expected: [[waitFor, waitFor === EVENT_REORDER ? parent : id]], + }, + (contentId, contentAttr) => + content.document.getElementById(contentId).removeAttribute(contentAttr), + [id, attr] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); + } else { + await invokeSetAttribute(browser, id, attr); + } +} + +/** + * Test accessible name that is calculated from an element name, remove the + * element before proceeding to the next name test. If element removal results + * in a reorder event - wait for it. If accessible becomes defunct, update its + * reference using the one that is attached to a possible reorder event. + * @param {Object} browser current "tabbrowser" element + * @param {Object} target { acc, parent, id } structure that contains an + * accessible, its parent and its content element + * id. + * @param {Object} rule current elm rule for name calculation + * @param {[type]} expected expected name value + */ +async function testElmRule(browser, target, rule, expected) { + let { id, parent, acc } = target; + let { isSibling, elm } = rule; + + testName(acc, expected); + let [event] = await contentSpawnMutation( + browser, + { + expected: [[EVENT_REORDER, isSibling ? parent : id]], + }, + contentElm => content.document.querySelector(`${contentElm}`).remove(), + [elm] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); +} + +/** + * Test accessible name that is calculated from its subtree, remove the subtree + * and wait for a reorder event before proceeding to the next name test. If + * accessible becomes defunct, update its reference using the one that is + * attached to a reorder event. + * @param {Object} browser current "tabbrowser" element + * @param {Object} target { acc, parent, id } structure that contains an + * accessible, its parent and its content element + * id. + * @param {Object} rule current subtree rule for name calculation + * @param {[type]} expected expected name value + */ +async function testSubtreeRule(browser, target, rule, expected) { + let { id, acc } = target; + + testName(acc, expected); + let [event] = await contentSpawnMutation( + browser, + { + expected: [[EVENT_REORDER, id]], + }, + contentId => { + let elm = content.document.getElementById(contentId); + while (elm.firstChild) { + elm.firstChild.remove(); + } + }, + [id] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); +} + +/** + * Iterate over a list of rules and test accessible names for each one of the + * rules. + * @param {Object} browser current "tabbrowser" element + * @param {Object} target { acc, parent, id } structure that contains an + * accessible, its parent and its content element + * id. + * @param {Array} ruleset A list of rules to test a target with + * @param {Array} expected A list of expected name value for each rule + */ +async function testNameRule(browser, target, ruleset, expected) { + for (let i = 0; i < ruleset.length; ++i) { + let rule = ruleset[i]; + let testFn; + if (rule.attr) { + testFn = testAttrRule; + } else if (rule.elm) { + testFn = testElmRule; + } else if (rule.fromsubtree) { + testFn = testSubtreeRule; + } + await testFn(browser, target, rule, expected[i]); + } +} + +markupTests.forEach(({ id, ruleset, markup, expected }) => + addAccessibleTask( + markup, + async function(browser, accDoc) { + const observer = { + observe(subject, topic, data) { + const event = subject.QueryInterface(nsIAccessibleEvent); + console.log(eventToString(event)); + }, + }; + Services.obs.addObserver(observer, "accessible-event"); + // Find a target accessible from an accessible subtree. + let acc = findAccessibleChildByID(accDoc, id); + // Find target's parent accessible from an accessible subtree. + let parent = getAccessibleDOMNodeID(acc.parent); + let target = { id, parent, acc }; + await testNameRule(browser, target, rules[ruleset], expected); + Services.obs.removeObserver(observer, "accessible-event"); + }, + { iframe: true, remoteIframe: true } + ) +); diff --git a/accessible/tests/browser/e10s/browser_caching_relations.js b/accessible/tests/browser/e10s/browser_caching_relations.js new file mode 100644 index 0000000000..38d3a0c63b --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_relations.js @@ -0,0 +1,96 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/relations.js */ +loadScripts({ name: "relations.js", dir: MOCHITESTS_DIR }); + +/** + * A test specification that has the following format: + * [ + * attr relevant aria attribute + * hostRelation corresponding host relation type + * dependantRelation corresponding dependant relation type + * ] + */ +const attrRelationsSpec = [ + ["aria-labelledby", RELATION_LABELLED_BY, RELATION_LABEL_FOR], + ["aria-describedby", RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR], + ["aria-controls", RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY], + ["aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM], +]; + +async function testRelated( + browser, + accDoc, + attr, + hostRelation, + dependantRelation +) { + let host = findAccessibleChildByID(accDoc, "host"); + let dependant1 = findAccessibleChildByID(accDoc, "dependant1"); + let dependant2 = findAccessibleChildByID(accDoc, "dependant2"); + + /** + * Test data has the format of: + * { + * desc {String} description for better logging + * attrs {?Array} an optional list of attributes to update + * expected {Array} expected relation values for dependant1, dependant2 + * and host respectively. + * } + */ + const tests = [ + { + desc: "No attribute", + expected: [null, null, null], + }, + { + desc: "Set attribute", + attrs: [{ key: attr, value: "dependant1" }], + expected: [host, null, dependant1], + }, + { + desc: "Change attribute", + attrs: [{ key: attr, value: "dependant2" }], + expected: [null, host, dependant2], + }, + { + desc: "Remove attribute", + attrs: [{ key: attr }], + expected: [null, null, null], + }, + ]; + + for (let { desc, attrs, expected } of tests) { + info(desc); + + if (attrs) { + for (let { key, value } of attrs) { + await invokeSetAttribute(browser, "host", key, value); + } + } + + testRelation(dependant1, dependantRelation, expected[0]); + testRelation(dependant2, dependantRelation, expected[1]); + testRelation(host, hostRelation, expected[2]); + } +} + +/** + * Test caching of relations between accessible objects. + */ +addAccessibleTask( + ` +
label
+
label2
+ `, + async function(browser, accDoc) { + for (let spec of attrRelationsSpec) { + await testRelated(browser, accDoc, ...spec); + } + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_states.js b/accessible/tests/browser/e10s/browser_caching_states.js new file mode 100644 index 0000000000..e6dbc33d65 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_states.js @@ -0,0 +1,154 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +/** + * Test data has the format of: + * { + * desc {String} description for better logging + * expected {Array} expected states for a given accessible that have the + * following format: + * [ + * expected state, + * expected extra state, + * absent state, + * absent extra state + * ] + * attrs {?Array} an optional list of attributes to update + * } + */ + +// State caching tests for attribute changes +const attributeTests = [ + { + desc: + "Checkbox with @checked attribute set to true should have checked " + + "state", + attrs: [ + { + attr: "checked", + value: "true", + }, + ], + expected: [STATE_CHECKED, 0], + }, + { + desc: "Checkbox with no @checked attribute should not have checked state", + attrs: [ + { + attr: "checked", + }, + ], + expected: [0, 0, STATE_CHECKED], + }, +]; + +// State caching tests for ARIA changes +const ariaTests = [ + { + desc: "File input has busy state when @aria-busy attribute is set to true", + attrs: [ + { + attr: "aria-busy", + value: "true", + }, + ], + expected: [STATE_BUSY, 0, STATE_REQUIRED | STATE_INVALID], + }, + { + desc: + "File input has required state when @aria-required attribute is set " + + "to true", + attrs: [ + { + attr: "aria-required", + value: "true", + }, + ], + expected: [STATE_REQUIRED, 0, STATE_INVALID], + }, + { + desc: + "File input has invalid state when @aria-invalid attribute is set to " + + "true", + attrs: [ + { + attr: "aria-invalid", + value: "true", + }, + ], + expected: [STATE_INVALID, 0], + }, +]; + +// Extra state caching tests +const extraStateTests = [ + { + desc: + "Input has no extra enabled state when aria and native disabled " + + "attributes are set at once", + attrs: [ + { + attr: "aria-disabled", + value: "true", + }, + { + attr: "disabled", + value: "true", + }, + ], + expected: [0, 0, 0, EXT_STATE_ENABLED], + }, + { + desc: + "Input has an extra enabled state when aria and native disabled " + + "attributes are unset at once", + attrs: [ + { + attr: "aria-disabled", + }, + { + attr: "disabled", + }, + ], + expected: [0, EXT_STATE_ENABLED], + }, +]; + +async function runStateTests(browser, accDoc, id, tests) { + let acc = findAccessibleChildByID(accDoc, id); + for (let { desc, attrs, expected } of tests) { + info(desc); + let onUpdate = waitForEvent(EVENT_STATE_CHANGE, id); + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, id, attr, value); + } + await onUpdate; + testStates(acc, ...expected); + } +} + +/** + * Test caching of accessible object states + */ +addAccessibleTask( + ` + + + `, + async function(browser, accDoc) { + await runStateTests(browser, accDoc, "checkbox", attributeTests); + await runStateTests(browser, accDoc, "file", ariaTests); + await runStateTests(browser, accDoc, "text", extraStateTests); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_uniqueid.js b/accessible/tests/browser/e10s/browser_caching_uniqueid.js new file mode 100644 index 0000000000..287f896c36 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_uniqueid.js @@ -0,0 +1,30 @@ +/* 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/. */ + +"use strict"; + +/** + * Test UniqueID property. + */ +addAccessibleTask( + '
', + async function(browser, accDoc) { + const div = findAccessibleChildByID(accDoc, "div"); + const accUniqueID = await invokeContentTask(browser, [], () => { + const accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + return accService.getAccessibleFor(content.document.getElementById("div")) + .uniqueID; + }); + + is( + accUniqueID, + div.uniqueID, + "Both proxy and the accessible return correct unique ID." + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_value.js b/accessible/tests/browser/e10s/browser_caching_value.js new file mode 100644 index 0000000000..c727fc5252 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_value.js @@ -0,0 +1,195 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/value.js */ +loadScripts({ name: "value.js", dir: MOCHITESTS_DIR }); + +/** + * Test data has the format of: + * { + * desc {String} description for better logging + * id {String} given accessible DOMNode ID + * expected {String} expected value for a given accessible + * action {?AsyncFunction} an optional action that awaits a value change + * attrs {?Array} an optional list of attributes to update + * waitFor {?Number} an optional value change event to wait for + * } + */ +const valueTests = [ + { + desc: "Initially value is set to 1st element of select", + id: "select", + expected: "1st", + }, + { + desc: "Value should update to 3rd when 3 is pressed", + id: "select", + async action(browser) { + await invokeFocus(browser, "select"); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("3", {}, content); + }); + }, + waitFor: EVENT_TEXT_VALUE_CHANGE, + expected: "3rd", + }, + { + desc: "Initially value is set to @aria-valuenow for slider", + id: "slider", + expected: ["5", 5, 0, 7, 0], + }, + { + desc: "Value should change when @aria-valuenow is updated", + id: "slider", + attrs: [ + { + attr: "aria-valuenow", + value: "6", + }, + ], + waitFor: EVENT_VALUE_CHANGE, + expected: ["6", 6, 0, 7, 0], + }, + { + desc: "Value should change when @aria-valuetext is set", + id: "slider", + attrs: [ + { + attr: "aria-valuetext", + value: "plain", + }, + ], + waitFor: EVENT_TEXT_VALUE_CHANGE, + expected: ["plain", 6, 0, 7, 0], + }, + { + desc: "Value should change when @aria-valuetext is updated", + id: "slider", + attrs: [ + { + attr: "aria-valuetext", + value: "hey!", + }, + ], + waitFor: EVENT_TEXT_VALUE_CHANGE, + expected: ["hey!", 6, 0, 7, 0], + }, + { + desc: + "Value should change to @aria-valuetext when @aria-valuenow is removed", + id: "slider", + attrs: [ + { + attr: "aria-valuenow", + }, + ], + expected: ["hey!", 3.5, 0, 7, 0], + }, + { + desc: "Initially value is not set for combobox", + id: "combobox", + expected: "", + }, + { + desc: "Value should change when @value attribute is updated", + id: "combobox", + attrs: [ + { + attr: "value", + value: "hello", + }, + ], + waitFor: EVENT_TEXT_VALUE_CHANGE, + expected: "hello", + }, + { + desc: "Initially value corresponds to @value attribute for progress", + id: "progress", + expected: "22%", + }, + { + desc: "Value should change when @value attribute is updated", + id: "progress", + attrs: [ + { + attr: "value", + value: "50", + }, + ], + waitFor: EVENT_VALUE_CHANGE, + expected: "50%", + }, + { + desc: "Initially value corresponds to @value attribute for range", + id: "range", + expected: "6", + }, + { + desc: "Value should change when slider is moved", + id: "range", + async action(browser) { + await invokeFocus(browser, "range"); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_LEFT", {}, content); + }); + }, + waitFor: EVENT_VALUE_CHANGE, + expected: "5", + }, +]; + +/** + * Test caching of accessible object values + */ +addAccessibleTask( + ` +
slider
+ + + + `, + async function(browser, accDoc) { + for (let { desc, id, action, attrs, expected, waitFor } of valueTests) { + info(desc); + let acc = findAccessibleChildByID(accDoc, id); + let onUpdate; + + if (waitFor) { + onUpdate = waitForEvent(waitFor, id); + } + + if (action) { + await action(browser); + } else if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, id, attr, value); + } + } + + await onUpdate; + if (Array.isArray(expected)) { + acc.QueryInterface(nsIAccessibleValue); + testValue(acc, ...expected); + } else { + is(acc.value, expected, `Correct value for ${prettyName(acc)}`); + } + } + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_announcement.js b/accessible/tests/browser/e10s/browser_events_announcement.js new file mode 100644 index 0000000000..2de6d4b005 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_announcement.js @@ -0,0 +1,30 @@ +/* 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/. */ + +"use strict"; + +addAccessibleTask( + `

abc

`, + async function(browser, accDoc) { + let acc = findAccessibleChildByID(accDoc, "p"); + let onAnnounce = waitForEvent(EVENT_ANNOUNCEMENT, acc); + acc.announce("please", nsIAccessibleAnnouncementEvent.POLITE); + let evt = await onAnnounce; + evt.QueryInterface(nsIAccessibleAnnouncementEvent); + is(evt.announcement, "please", "announcement matches."); + is(evt.priority, nsIAccessibleAnnouncementEvent.POLITE, "priority matches"); + + onAnnounce = waitForEvent(EVENT_ANNOUNCEMENT, acc); + acc.announce("do it", nsIAccessibleAnnouncementEvent.ASSERTIVE); + evt = await onAnnounce; + evt.QueryInterface(nsIAccessibleAnnouncementEvent); + is(evt.announcement, "do it", "announcement matches."); + is( + evt.priority, + nsIAccessibleAnnouncementEvent.ASSERTIVE, + "priority matches" + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_caretmove.js b/accessible/tests/browser/e10s/browser_events_caretmove.js new file mode 100644 index 0000000000..a39d16e710 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_caretmove.js @@ -0,0 +1,22 @@ +/* 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/. */ + +"use strict"; + +/** + * Test caret move event and its interface: + * - caretOffset + */ +addAccessibleTask( + '', + async function(browser) { + let onCaretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, "textbox"); + await invokeFocus(browser, "textbox"); + let event = await onCaretMoved; + + let caretMovedEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent); + is(caretMovedEvent.caretOffset, 5, "Correct caret offset."); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_hide.js b/accessible/tests/browser/e10s/browser_events_hide.js new file mode 100644 index 0000000000..d46921d051 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_hide.js @@ -0,0 +1,44 @@ +/* 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/. */ + +"use strict"; + +/** + * Test hide event and its interface: + * - targetParent + * - targetNextSibling + * - targetPrevSibling + */ +addAccessibleTask( + ` +
+ +
+ +
`, + async function(browser, accDoc) { + let acc = findAccessibleChildByID(accDoc, "to-hide"); + let onHide = waitForEvent(EVENT_HIDE, acc); + await invokeSetStyle(browser, "to-hide", "visibility", "hidden"); + let event = await onHide; + let hideEvent = event.QueryInterface(Ci.nsIAccessibleHideEvent); + + is( + getAccessibleDOMNodeID(hideEvent.targetParent), + "parent", + "Correct target parent." + ); + is( + getAccessibleDOMNodeID(hideEvent.targetNextSibling), + "next", + "Correct target next sibling." + ); + is( + getAccessibleDOMNodeID(hideEvent.targetPrevSibling), + "previous", + "Correct target previous sibling." + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_show.js b/accessible/tests/browser/e10s/browser_events_show.js new file mode 100644 index 0000000000..d464d8fb9d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_show.js @@ -0,0 +1,22 @@ +/* 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/. */ + +"use strict"; + +/** + * Test show event + */ +addAccessibleTask( + '', + async function(browser) { + let onShow = waitForEvent(EVENT_SHOW, "div"); + await invokeSetStyle(browser, "div", "visibility", "visible"); + let showEvent = await onShow; + ok( + showEvent.accessibleDocument instanceof nsIAccessibleDocument, + "Accessible document not present." + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_statechange.js b/accessible/tests/browser/e10s/browser_events_statechange.js new file mode 100644 index 0000000000..a027a974e4 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_statechange.js @@ -0,0 +1,71 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +function checkStateChangeEvent(event, state, isExtraState, isEnabled) { + let scEvent = event.QueryInterface(nsIAccessibleStateChangeEvent); + is(scEvent.state, state, "Correct state of the statechange event."); + is( + scEvent.isExtraState, + isExtraState, + "Correct extra state bit of the statechange event." + ); + is(scEvent.isEnabled, isEnabled, "Correct state of statechange event state"); +} + +// Insert mock source into the iframe to be able to verify the right document +// body id. +let iframeSrc = `data:text/html, + + + + Inner Iframe + + + `; + +/** + * Test state change event and its interface: + * - state + * - isExtraState + * - isEnabled + */ +addAccessibleTask( + ` + + `, + async function(browser) { + // Test state change + let onStateChange = waitForEvent(EVENT_STATE_CHANGE, "checkbox"); + // Set checked for a checkbox. + await invokeContentTask(browser, [], () => { + content.document.getElementById("checkbox").checked = true; + }); + let event = await onStateChange; + + checkStateChangeEvent(event, STATE_CHECKED, false, true); + testStates(event.accessible, STATE_CHECKED, 0); + + // Test extra state + onStateChange = waitForEvent(EVENT_STATE_CHANGE, "iframe"); + // Set design mode on. + await invokeContentTask(browser, [], () => { + content.document.getElementById("iframe").contentDocument.designMode = + "on"; + }); + event = await onStateChange; + + checkStateChangeEvent(event, EXT_STATE_EDITABLE, true, true); + testStates(event.accessible, 0, EXT_STATE_EDITABLE); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_textchange.js b/accessible/tests/browser/e10s/browser_events_textchange.js new file mode 100644 index 0000000000..1e822dc65a --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_textchange.js @@ -0,0 +1,114 @@ +/* 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/. */ + +"use strict"; + +function checkTextChangeEvent( + event, + id, + text, + start, + end, + isInserted, + isFromUserInput +) { + let tcEvent = event.QueryInterface(nsIAccessibleTextChangeEvent); + is(tcEvent.start, start, `Correct start offset for ${prettyName(id)}`); + is(tcEvent.length, end - start, `Correct length for ${prettyName(id)}`); + is( + tcEvent.isInserted, + isInserted, + `Correct isInserted flag for ${prettyName(id)}` + ); + is(tcEvent.modifiedText, text, `Correct text for ${prettyName(id)}`); + is( + tcEvent.isFromUserInput, + isFromUserInput, + `Correct value of isFromUserInput for ${prettyName(id)}` + ); + ok( + tcEvent.accessibleDocument instanceof nsIAccessibleDocument, + "Accessible document not present." + ); +} + +async function changeText(browser, id, value, events) { + let onEvents = waitForOrderedEvents( + events.map(({ isInserted }) => { + let eventType = isInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED; + return [eventType, id]; + }) + ); + // Change text in the subtree. + await invokeContentTask(browser, [id, value], (contentId, contentValue) => { + content.document.getElementById( + contentId + ).firstChild.textContent = contentValue; + }); + let resolvedEvents = await onEvents; + + events.forEach(({ isInserted, str, offset }, idx) => + checkTextChangeEvent( + resolvedEvents[idx], + id, + str, + offset, + offset + str.length, + isInserted, + false + ) + ); +} + +async function removeTextFromInput(browser, id, value, start, end) { + let onTextRemoved = waitForEvent(EVENT_TEXT_REMOVED, id); + // Select text and delete it. + await invokeContentTask( + browser, + [id, start, end], + (contentId, contentStart, contentEnd) => { + let el = content.document.getElementById(contentId); + el.focus(); + el.setSelectionRange(contentStart, contentEnd); + } + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.sendChar("VK_DELETE", content); + }); + + let event = await onTextRemoved; + checkTextChangeEvent(event, id, value, start, end, false, true); +} + +/** + * Test text change event and its interface: + * - start + * - length + * - isInserted + * - modifiedText + * - isFromUserInput + */ +addAccessibleTask( + ` +

abc

+ `, + async function(browser) { + let events = [ + { isInserted: false, str: "abc", offset: 0 }, + { isInserted: true, str: "def", offset: 0 }, + ]; + await changeText(browser, "p", "def", events); + + events = [{ isInserted: true, str: "DEF", offset: 2 }]; + await changeText(browser, "p", "deDEFf", events); + + // Test isFromUserInput property. + await removeTextFromInput(browser, "input", "n", 1, 2); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_vcchange.js b/accessible/tests/browser/e10s/browser_events_vcchange.js new file mode 100644 index 0000000000..bf649fe045 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_vcchange.js @@ -0,0 +1,87 @@ +/* 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/. */ + +"use strict"; + +addAccessibleTask( + ` +

abc

+ `, + async function(browser) { + let onVCChanged = waitForEvent( + EVENT_VIRTUALCURSOR_CHANGED, + matchContentDoc + ); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.position = CommonUtils.getAccessible( + "p1", + null, + null, + null, + content.document + ); + }); + let vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + is(vccEvent.newAccessible.id, "p1", "New position is correct"); + is(vccEvent.newStartOffset, -1, "New start offset is correct"); + is(vccEvent.newEndOffset, -1, "New end offset is correct"); + ok(!vccEvent.isFromUserInput, "not user initiated"); + + onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.moveNextByText(Ci.nsIAccessiblePivot.CHAR_BOUNDARY); + }); + vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + is(vccEvent.newAccessible.id, vccEvent.oldAccessible.id, "Same position"); + is(vccEvent.newStartOffset, 0, "New start offset is correct"); + is(vccEvent.newEndOffset, 1, "New end offset is correct"); + ok(vccEvent.isFromUserInput, "user initiated"); + + onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.position = CommonUtils.getAccessible( + "input1", + null, + null, + null, + content.document + ); + }); + vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + isnot(vccEvent.oldAccessible, vccEvent.newAccessible, "positions differ"); + is(vccEvent.oldAccessible.id, "p1", "Old position is correct"); + is(vccEvent.newAccessible.id, "input1", "New position is correct"); + is(vccEvent.newStartOffset, -1, "New start offset is correct"); + is(vccEvent.newEndOffset, -1, "New end offset is correct"); + ok(!vccEvent.isFromUserInput, "not user initiated"); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js b/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js new file mode 100644 index 0000000000..04e64520e8 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js @@ -0,0 +1,22 @@ +/* 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/. */ + +"use strict"; + +// Test that we don't crash the parent process when querying the paragraph +// boundary on an Accessible which has remote ProxyAccessible descendants. +addAccessibleTask( + `test`, + async function testParagraphBoundaryWithRemoteDescendants(browser, accDoc) { + const root = getRootAccessible(document).QueryInterface( + Ci.nsIAccessibleText + ); + let start = {}; + let end = {}; + // The offsets will change as the Firefox UI changes. We don't really care + // what they are, just that we don't crash. + root.getTextAtOffset(0, nsIAccessibleText.BOUNDARY_PARAGRAPH, start, end); + ok(true, "Getting paragraph boundary succeeded"); + } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js new file mode 100644 index 0000000000..8b4a575d75 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js @@ -0,0 +1,45 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +// Test ARIA Dialog +addAccessibleTask( + "e10s/doc_treeupdate_ariadialog.html", + async function(browser, accDoc) { + testAccessibleTree(accDoc, { + role: ROLE_DOCUMENT, + children: [], + }); + + // Make dialog visible and update its inner content. + let onShow = waitForEvent(EVENT_SHOW, "dialog"); + await invokeContentTask(browser, [], () => { + content.document.getElementById("dialog").style.display = "block"; + }); + await onShow; + + testAccessibleTree(accDoc, { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_DIALOG, + children: [ + { + role: ROLE_PUSHBUTTON, + children: [{ role: ROLE_TEXT_LEAF }], + }, + { + role: ROLE_ENTRY, + }, + ], + }, + ], + }); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js new file mode 100644 index 0000000000..dfd6401c48 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js @@ -0,0 +1,294 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function testContainer1(browser, accDoc) { + const id = "t1_container"; + const docID = getAccessibleDOMNodeID(accDoc); + const acc = findAccessibleChildByID(accDoc, id); + + /* ================= Initial tree test ==================================== */ + // children are swapped by ARIA owns + let tree = { + SECTION: [{ CHECKBUTTON: [{ SECTION: [] }] }, { PUSHBUTTON: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================ Change ARIA owns ====================================== */ + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv"); + await onReorder; + + // children are swapped again, button and subdiv are appended to + // the children. + tree = { + SECTION: [ + { CHECKBUTTON: [] }, // checkbox, native order + { PUSHBUTTON: [] }, // button, rearranged by ARIA own + { SECTION: [] }, // subdiv from the subtree, ARIA owned + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Remove ARIA owns ====================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, id, "aria-owns"); + await onReorder; + + // children follow the DOM order + tree = { + SECTION: [{ PUSHBUTTON: [] }, { CHECKBUTTON: [{ SECTION: [] }] }], + }; + testAccessibleTree(acc, tree); + + /* ================ Set ARIA owns ========================================= */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv"); + await onReorder; + + // children are swapped again, button and subdiv are appended to + // the children. + tree = { + SECTION: [ + { CHECKBUTTON: [] }, // checkbox + { PUSHBUTTON: [] }, // button, rearranged by ARIA own + { SECTION: [] }, // subdiv from the subtree, ARIA owned + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Add ID to ARIA owns =================================== */ + onReorder = waitForEvent(EVENT_REORDER, docID); + await invokeSetAttribute( + browser, + id, + "aria-owns", + "t1_button t1_subdiv t1_group" + ); + await onReorder; + + // children are swapped again, button and subdiv are appended to + // the children. + tree = { + SECTION: [ + { CHECKBUTTON: [] }, // t1_checkbox + { PUSHBUTTON: [] }, // button, t1_button + { SECTION: [] }, // subdiv from the subtree, t1_subdiv + { GROUPING: [] }, // group from outside, t1_group + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Append element ======================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let div = content.document.createElement("div"); + div.setAttribute("id", "t1_child3"); + div.setAttribute("role", "radio"); + content.document.getElementById(contentId).appendChild(div); + }); + await onReorder; + + // children are invalidated, they includes aria-owns swapped kids and + // newly inserted child. + tree = { + SECTION: [ + { CHECKBUTTON: [] }, // existing explicit, t1_checkbox + { RADIOBUTTON: [] }, // new explicit, t1_child3 + { PUSHBUTTON: [] }, // ARIA owned, t1_button + { SECTION: [] }, // ARIA owned, t1_subdiv + { GROUPING: [] }, // ARIA owned, t1_group + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Remove element ======================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + content.document.getElementById("t1_span").remove(); + }); + await onReorder; + + // subdiv should go away + tree = { + SECTION: [ + { CHECKBUTTON: [] }, // explicit, t1_checkbox + { RADIOBUTTON: [] }, // explicit, t1_child3 + { PUSHBUTTON: [] }, // ARIA owned, t1_button + { GROUPING: [] }, // ARIA owned, t1_group + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Remove ID ============================================= */ + onReorder = waitForEvent(EVENT_REORDER, docID); + await invokeSetAttribute(browser, "t1_group", "id"); + await onReorder; + + tree = { + SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] }, + { PUSHBUTTON: [] }, // ARIA owned, t1_button + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Set ID ================================================ */ + onReorder = waitForEvent(EVENT_REORDER, docID); + await invokeSetAttribute(browser, "t1_grouptmp", "id", "t1_group"); + await onReorder; + + tree = { + SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] }, + { PUSHBUTTON: [] }, // ARIA owned, t1_button + { GROUPING: [] }, // ARIA owned, t1_group, previously t1_grouptmp + ], + }; + testAccessibleTree(acc, tree); +} + +async function removeContainer(browser, accDoc) { + const id = "t2_container1"; + const acc = findAccessibleChildByID(accDoc, id); + + let tree = { + SECTION: [ + { CHECKBUTTON: [] }, // ARIA owned, 't2_owned' + ], + }; + testAccessibleTree(acc, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + content.document + .getElementById("t2_container2") + .removeChild(content.document.getElementById("t2_container3")); + }); + await onReorder; + + tree = { + SECTION: [], + }; + testAccessibleTree(acc, tree); +} + +async function stealAndRecacheChildren(browser, accDoc) { + const id1 = "t3_container1"; + const id2 = "t3_container2"; + const acc1 = findAccessibleChildByID(accDoc, id1); + const acc2 = findAccessibleChildByID(accDoc, id2); + + /* ================ Attempt to steal from other ARIA owns ================= */ + let onReorder = waitForEvent(EVENT_REORDER, id2); + await invokeSetAttribute(browser, id2, "aria-owns", "t3_child"); + await invokeContentTask(browser, [id2], id => { + let div = content.document.createElement("div"); + div.setAttribute("role", "radio"); + content.document.getElementById(id).appendChild(div); + }); + await onReorder; + + let tree = { + SECTION: [ + { CHECKBUTTON: [] }, // ARIA owned + ], + }; + testAccessibleTree(acc1, tree); + + tree = { + SECTION: [{ RADIOBUTTON: [] }], + }; + testAccessibleTree(acc2, tree); +} + +async function showHiddenElement(browser, accDoc) { + const id = "t4_container1"; + const acc = findAccessibleChildByID(accDoc, id); + + let tree = { + SECTION: [{ RADIOBUTTON: [] }], + }; + testAccessibleTree(acc, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetStyle(browser, "t4_child1", "display", "block"); + await onReorder; + + tree = { + SECTION: [{ CHECKBUTTON: [] }, { RADIOBUTTON: [] }], + }; + testAccessibleTree(acc, tree); +} + +async function rearrangeARIAOwns(browser, accDoc) { + const id = "t5_container"; + const acc = findAccessibleChildByID(accDoc, id); + const tests = [ + { + val: "t5_checkbox t5_radio t5_button", + roleList: ["CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON"], + }, + { + val: "t5_radio t5_button t5_checkbox", + roleList: ["RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON"], + }, + ]; + + for (let { val, roleList } of tests) { + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, id, "aria-owns", val); + await onReorder; + + let tree = { SECTION: [] }; + for (let role of roleList) { + let ch = {}; + ch[role] = []; + tree.SECTION.push(ch); + } + testAccessibleTree(acc, tree); + } +} + +async function removeNotARIAOwnedEl(browser, accDoc) { + const id = "t6_container"; + const acc = findAccessibleChildByID(accDoc, id); + + let tree = { + SECTION: [{ TEXT_LEAF: [] }, { GROUPING: [] }], + }; + testAccessibleTree(acc, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + content.document + .getElementById(contentId) + .removeChild(content.document.getElementById("t6_span")); + }); + await onReorder; + + tree = { + SECTION: [{ GROUPING: [] }], + }; + testAccessibleTree(acc, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_ariaowns.html", + async function(browser, accDoc) { + await testContainer1(browser, accDoc); + await removeContainer(browser, accDoc); + await stealAndRecacheChildren(browser, accDoc); + await showHiddenElement(browser, accDoc); + await rearrangeARIAOwns(browser, accDoc); + await removeNotARIAOwnedEl(browser, accDoc); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_canvas.js b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js new file mode 100644 index 0000000000..5fcd1eb773 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js @@ -0,0 +1,28 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + + + `, + async function(browser, accDoc) { + let canvas = findAccessibleChildByID(accDoc, "canvas"); + let dialog = findAccessibleChildByID(accDoc, "dialog"); + + testAccessibleTree(canvas, { CANVAS: [] }); + + let onShow = waitForEvent(EVENT_SHOW, "dialog"); + await invokeSetStyle(browser, "dialog", "display", "block"); + await onShow; + + testAccessibleTree(dialog, { DIALOG: [] }); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js new file mode 100644 index 0000000000..629f9fb89f --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js @@ -0,0 +1,60 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` +
`, + async function(browser, accDoc) { + const id1 = "container"; + const container = findAccessibleChildByID(accDoc, id1); + + /* ================= Change scroll range ================================== */ + let tree = { + SECTION: [ + { + // container + SECTION: [ + { + // scroll area + ENTRY: [], // child content + }, + ], + }, + ], + }; + testAccessibleTree(container, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id1); + await invokeContentTask(browser, [id1], id => { + let doc = content.document; + doc.getElementById("scrollarea").style.width = "20px"; + doc.getElementById(id).appendChild(doc.createElement("input")); + }); + await onReorder; + + tree = { + SECTION: [ + { + // container + SECTION: [ + { + // scroll area + ENTRY: [], // child content + }, + ], + }, + { + ENTRY: [], // inserted input + }, + ], + }; + testAccessibleTree(container, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_doc.js b/accessible/tests/browser/e10s/browser_treeupdate_doc.js new file mode 100644 index 0000000000..98f399695c --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_doc.js @@ -0,0 +1,320 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +const iframeSrc = `data:text/html, + + + + Inner Iframe + + + `; + +addAccessibleTask( + ` + `, + async function(browser, accDoc) { + // ID of the iframe that is being tested + const id = "inner-iframe"; + + let iframe = findAccessibleChildByID(accDoc, id); + + /* ================= Initial tree check =================================== */ + let tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Write iframe document ================================ */ + let reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let docNode = content.document.getElementById("iframe").contentDocument; + let newHTMLNode = docNode.createElement("html"); + let newBodyNode = docNode.createElement("body"); + let newTextNode = docNode.createTextNode("New Wave"); + newBodyNode.id = contentId; + newBodyNode.appendChild(newTextNode); + newHTMLNode.appendChild(newBodyNode); + docNode.replaceChild(newHTMLNode, docNode.documentElement); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Wave", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Replace iframe HTML element ========================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let docNode = content.document.getElementById("iframe").contentDocument; + // We can't use open/write/close outside of iframe document because of + // security error. + let script = docNode.createElement("script"); + script.textContent = ` + document.open(); + document.write('hello'); + document.close();`; + docNode.body.appendChild(script); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Replace iframe body ================================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let docNode = content.document.getElementById("iframe").contentDocument; + let newBodyNode = docNode.createElement("body"); + let newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.id = contentId; + newBodyNode.appendChild(newTextNode); + newBodyNode.setAttribute("role", "application"); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }); + await reorderEventPromise; + + tree = { + role: ROLE_APPLICATION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Open iframe document ================================= */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + // Open document. + let docNode = content.document.getElementById("iframe").contentDocument; + let script = docNode.createElement("script"); + script.textContent = ` + function closeMe() { + document.write('Works?'); + document.close(); + } + window.closeMe = closeMe; + document.open(); + document.write('');`; + docNode.body.appendChild(script); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Close iframe document ================================ */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + // Write and close document. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.write("Works?"); + docNode.close(); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Works?", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Remove HTML from iframe document ===================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, iframe); + await invokeContentTask(browser, [], () => { + // Remove HTML element. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.firstChild.remove(); + }); + let event = await reorderEventPromise; + + ok( + event.accessible instanceof nsIAccessibleDocument, + "Reorder should happen on the document" + ); + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Insert HTML to iframe document ======================= */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + // Insert HTML element. + let docNode = content.document.getElementById("iframe").contentDocument; + let html = docNode.createElement("html"); + let body = docNode.createElement("body"); + let text = docNode.createTextNode("Haha"); + body.appendChild(text); + body.id = contentId; + html.appendChild(body); + docNode.appendChild(html); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Haha", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Remove body from iframe document ===================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, iframe); + await invokeContentTask(browser, [], () => { + // Remove body element. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.documentElement.removeChild(docNode.body); + }); + event = await reorderEventPromise; + + ok( + event.accessible instanceof nsIAccessibleDocument, + "Reorder should happen on the document" + ); + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================ Insert element under document element while body missed */ + reorderEventPromise = waitForEvent(EVENT_REORDER, iframe); + await invokeContentTask(browser, [], () => { + let docNode = content.document.getElementById("iframe").contentDocument; + let inputNode = (content.window.inputNode = docNode.createElement( + "input" + )); + docNode.documentElement.appendChild(inputNode); + }); + event = await reorderEventPromise; + + ok( + event.accessible instanceof nsIAccessibleDocument, + "Reorder should happen on the document" + ); + tree = { + DOCUMENT: [{ ENTRY: [] }], + }; + testAccessibleTree(iframe, tree); + + reorderEventPromise = waitForEvent(EVENT_REORDER, iframe); + await invokeContentTask(browser, [], () => { + let docEl = content.document.getElementById("iframe").contentDocument + .documentElement; + // Remove aftermath of this test before next test starts. + docEl.firstChild.remove(); + }); + // Make sure reorder event was fired and that the input was removed. + await reorderEventPromise; + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Insert body to iframe document ======================= */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + // Write and close document. + let docNode = content.document.getElementById("iframe").contentDocument; + // Insert body element. + let body = docNode.createElement("body"); + let text = docNode.createTextNode("Yo ho ho i butylka roma!"); + body.appendChild(text); + body.id = contentId; + docNode.documentElement.appendChild(body); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Yo ho ho i butylka roma!", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Change source ======================================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, "iframe"); + await invokeSetAttribute( + browser, + "iframe", + "src", + `data:text/html,` + ); + event = await reorderEventPromise; + + tree = { + INTERNAL_FRAME: [{ DOCUMENT: [{ ENTRY: [] }] }], + }; + testAccessibleTree(event.accessible, tree); + iframe = findAccessibleChildByID(event.accessible, id); + + /* ================= Replace iframe body on ARIA role body ================ */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let docNode = content.document.getElementById("iframe").contentDocument; + let newBodyNode = docNode.createElement("body"); + let newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.appendChild(newTextNode); + newBodyNode.setAttribute("role", "application"); + newBodyNode.id = contentId; + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }); + await reorderEventPromise; + + tree = { + role: ROLE_APPLICATION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js new file mode 100644 index 0000000000..ca1150f9dd --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js @@ -0,0 +1,94 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + +
+
text
`, + async function(browser, accDoc) { + const id1 = "container1"; + const id2 = "container2"; + let container1 = findAccessibleChildByID(accDoc, id1); + let container2 = findAccessibleChildByID(accDoc, id2); + + let tree = { + SECTION: [], // container + }; + testAccessibleTree(container1, tree); + + tree = { + SECTION: [ + { + // container2 + SECTION: [ + { + // container2 child + TEXT_LEAF: [], // primary text + }, + ], + }, + ], + }; + testAccessibleTree(container2, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id1); + // Create and add an element with CSS generated content to container1 + await invokeContentTask(browser, [id1], id => { + let node = content.document.createElement("div"); + node.textContent = "text"; + node.setAttribute("class", "gentext"); + content.document.getElementById(id).appendChild(node); + }); + await onReorder; + + tree = { + SECTION: [ + // container + { + SECTION: [ + // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ], + }, + ], + }; + testAccessibleTree(container1, tree); + + onReorder = waitForEvent(EVENT_REORDER, "container2_child"); + // Add CSS generated content to an element in container2's subtree + await invokeSetAttribute(browser, "container2_child", "class", "gentext"); + await onReorder; + + tree = { + SECTION: [ + // container2 + { + SECTION: [ + // container2 child + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ], + }, + ], + }; + testAccessibleTree(container2, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_hidden.js b/accessible/tests/browser/e10s/browser_treeupdate_hidden.js new file mode 100644 index 0000000000..725999db36 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_hidden.js @@ -0,0 +1,32 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function setHidden(browser, value) { + let onReorder = waitForEvent(EVENT_REORDER, "container"); + await invokeSetAttribute(browser, "child", "hidden", value); + await onReorder; +} + +addAccessibleTask( + '
', + async function(browser, accDoc) { + let container = findAccessibleChildByID(accDoc, "container"); + + testAccessibleTree(container, { SECTION: [{ ENTRY: [] }] }); + + // Set @hidden attribute + await setHidden(browser, "true"); + testAccessibleTree(container, { SECTION: [] }); + + // Remove @hidden attribute + await setHidden(browser); + testAccessibleTree(container, { SECTION: [{ ENTRY: [] }] }); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js new file mode 100644 index 0000000000..e206b83276 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js @@ -0,0 +1,187 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function testImageMap(browser, accDoc) { + const id = "imgmap"; + const acc = findAccessibleChildByID(accDoc, id); + + /* ================= Initial tree test ==================================== */ + let tree = { + IMAGE_MAP: [{ role: ROLE_LINK, name: "b", children: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================= Insert area ========================================== */ + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + let areaElm = content.document.createElement("area"); + let mapNode = content.document.getElementById("map"); + areaElm.setAttribute( + "href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#a" + ); + areaElm.setAttribute("coords", "0,0,13,14"); + areaElm.setAttribute("alt", "a"); + areaElm.setAttribute("shape", "rect"); + mapNode.insertBefore(areaElm, mapNode.firstChild); + }); + await onReorder; + + tree = { + IMAGE_MAP: [ + { role: ROLE_LINK, name: "a", children: [] }, + { role: ROLE_LINK, name: "b", children: [] }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Append area ========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + let areaElm = content.document.createElement("area"); + let mapNode = content.document.getElementById("map"); + areaElm.setAttribute( + "href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#c" + ); + areaElm.setAttribute("coords", "34,0,47,14"); + areaElm.setAttribute("alt", "c"); + areaElm.setAttribute("shape", "rect"); + mapNode.appendChild(areaElm); + }); + await onReorder; + + tree = { + IMAGE_MAP: [ + { role: ROLE_LINK, name: "a", children: [] }, + { role: ROLE_LINK, name: "b", children: [] }, + { role: ROLE_LINK, name: "c", children: [] }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Remove area ========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + let mapNode = content.document.getElementById("map"); + mapNode.removeChild(mapNode.firstElementChild); + }); + await onReorder; + + tree = { + IMAGE_MAP: [ + { role: ROLE_LINK, name: "b", children: [] }, + { role: ROLE_LINK, name: "c", children: [] }, + ], + }; + testAccessibleTree(acc, tree); +} + +async function testContainer(browser) { + const id = "container"; + /* ================= Remove name on map =================================== */ + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, "map", "name"); + let event = await onReorder; + const acc = event.accessible; + + let tree = { + SECTION: [{ GRAPHIC: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================= Restore name on map ================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, "map", "name", "atoz_map"); + // XXX: force repainting of the image (see bug 745788 for details). + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeMouse( + content.document.getElementById("imgmap"), + 10, + 10, + { type: "mousemove" }, + content + ); + }); + await onReorder; + + tree = { + SECTION: [ + { + IMAGE_MAP: [{ LINK: [] }, { LINK: [] }], + }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Remove map =========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + let mapNode = content.document.getElementById("map"); + mapNode.remove(); + }); + await onReorder; + + tree = { + SECTION: [{ GRAPHIC: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================= Insert map =========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + let map = content.document.createElement("map"); + let area = content.document.createElement("area"); + + map.setAttribute("name", "atoz_map"); + map.setAttribute("id", "map"); + + area.setAttribute("href", "http://www.bbc.co.uk/radio4/atoz/index.shtml#b"); + area.setAttribute("coords", "17,0,30,14"); + area.setAttribute("alt", "b"); + area.setAttribute("shape", "rect"); + + map.appendChild(area); + content.document.getElementById(contentId).appendChild(map); + }); + await onReorder; + + tree = { + SECTION: [ + { + IMAGE_MAP: [{ LINK: [] }], + }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Hide image map ======================================= */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetStyle(browser, "imgmap", "display", "none"); + await onReorder; + + tree = { + SECTION: [], + }; + testAccessibleTree(acc, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_imagemap.html", + async function(browser, accDoc) { + await waitForImageMap(browser, accDoc); + await testImageMap(browser, accDoc); + await testContainer(browser); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_list.js b/accessible/tests/browser/e10s/browser_treeupdate_list.js new file mode 100644 index 0000000000..d14b983c10 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_list.js @@ -0,0 +1,52 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function setDisplayAndWaitForReorder(browser, value) { + let onReorder = waitForEvent(EVENT_REORDER, "ul"); + await invokeSetStyle(browser, "li", "display", value); + return onReorder; +} + +addAccessibleTask( + ` +
    +
  • item1
  • +
`, + async function(browser, accDoc) { + let li = findAccessibleChildByID(accDoc, "li"); + let bullet = li.firstChild; + let accTree = { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_LISTITEM_MARKER, + children: [], + }, + { + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + testAccessibleTree(li, accTree); + + await setDisplayAndWaitForReorder(browser, "none"); + + ok(isDefunct(li), "Check that li is defunct."); + ok(isDefunct(bullet), "Check that bullet is defunct."); + + let event = await setDisplayAndWaitForReorder(browser, "list-item"); + + testAccessibleTree( + findAccessibleChildByID(event.accessible, "li"), + accTree + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js new file mode 100644 index 0000000000..9c672f3c7c --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js @@ -0,0 +1,48 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '
    ', + async function(browser, accDoc) { + let list = findAccessibleChildByID(accDoc, "list"); + + testAccessibleTree(list, { + role: ROLE_LIST, + children: [], + }); + + await invokeSetAttribute( + browser, + currentContentDoc(), + "contentEditable", + "true" + ); + let onReorder = waitForEvent(EVENT_REORDER, "list"); + await invokeContentTask(browser, [], () => { + let li = content.document.createElement("li"); + li.textContent = "item"; + content.document.getElementById("list").appendChild(li); + }); + await onReorder; + + testAccessibleTree(list, { + role: ROLE_LIST, + children: [ + { + role: ROLE_LISTITEM, + children: [ + { role: ROLE_LISTITEM_MARKER, name: "1. ", children: [] }, + { role: ROLE_TEXT_LEAF, children: [] }, + ], + }, + ], + }); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_listener.js b/accessible/tests/browser/e10s/browser_treeupdate_listener.js new file mode 100644 index 0000000000..35baf28667 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_listener.js @@ -0,0 +1,38 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '', + async function(browser, accDoc) { + is( + findAccessibleChildByID(accDoc, "parent"), + null, + "Check that parent is not accessible." + ); + is( + findAccessibleChildByID(accDoc, "child"), + null, + "Check that child is not accessible." + ); + + let onReorder = waitForEvent(EVENT_REORDER, matchContentDoc); + // Add an event listener to parent. + await invokeContentTask(browser, [], () => { + content.window.dummyListener = () => {}; + content.document + .getElementById("parent") + .addEventListener("click", content.window.dummyListener); + }); + await onReorder; + + let tree = { TEXT: [] }; + testAccessibleTree(findAccessibleChildByID(accDoc, "parent"), tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js new file mode 100644 index 0000000000..15fed8112d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js @@ -0,0 +1,103 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '', + async function(browser, accDoc) { + let select = findAccessibleChildByID(accDoc, "select"); + + let onEvent = waitForEvent(EVENT_REORDER, "select"); + // Create a combobox with grouping and 2 standalone options + await invokeContentTask(browser, [], () => { + let doc = content.document; + let contentSelect = doc.getElementById("select"); + let optGroup = doc.createElement("optgroup"); + + for (let i = 0; i < 2; i++) { + let opt = doc.createElement("option"); + opt.value = i; + opt.text = "Option: Value " + i; + optGroup.appendChild(opt); + } + contentSelect.add(optGroup, null); + + for (let i = 0; i < 2; i++) { + let opt = doc.createElement("option"); + contentSelect.add(opt, null); + } + contentSelect.firstChild.firstChild.id = "option1Node"; + }); + let event = await onEvent; + let option1Node = findAccessibleChildByID(event.accessible, "option1Node"); + + let tree = { + COMBOBOX: [ + { + COMBOBOX_LIST: [ + { + GROUPING: [ + { COMBOBOX_OPTION: [{ TEXT_LEAF: [] }] }, + { COMBOBOX_OPTION: [{ TEXT_LEAF: [] }] }, + ], + }, + { + COMBOBOX_OPTION: [], + }, + { + COMBOBOX_OPTION: [], + }, + ], + }, + ], + }; + testAccessibleTree(select, tree); + ok(!isDefunct(option1Node), "option shouldn't be defunct"); + + onEvent = waitForEvent(EVENT_REORDER, "select"); + // Remove grouping from combobox + await invokeContentTask(browser, [], () => { + let contentSelect = content.document.getElementById("select"); + contentSelect.firstChild.remove(); + }); + await onEvent; + + tree = { + COMBOBOX: [ + { + COMBOBOX_LIST: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }], + }, + ], + }; + testAccessibleTree(select, tree); + ok( + isDefunct(option1Node), + "removed option shouldn't be accessible anymore!" + ); + + onEvent = waitForEvent(EVENT_REORDER, "select"); + // Remove all options from combobox + await invokeContentTask(browser, [], () => { + let contentSelect = content.document.getElementById("select"); + while (contentSelect.length) { + contentSelect.remove(0); + } + }); + await onEvent; + + tree = { + COMBOBOX: [ + { + COMBOBOX_LIST: [], + }, + ], + }; + testAccessibleTree(select, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_removal.js b/accessible/tests/browser/e10s/browser_treeupdate_removal.js new file mode 100644 index 0000000000..eb791525b3 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_removal.js @@ -0,0 +1,58 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "e10s/doc_treeupdate_removal.xhtml", + async function(browser, accDoc) { + ok( + isAccessible(findAccessibleChildByID(accDoc, "the_table")), + "table should be accessible" + ); + + // Move the_table element into hidden subtree. + let onReorder = waitForEvent(EVENT_REORDER, matchContentDoc); + await invokeContentTask(browser, [], () => { + content.document + .getElementById("the_displaynone") + .appendChild(content.document.getElementById("the_table")); + }); + await onReorder; + + ok( + !isAccessible(findAccessibleChildByID(accDoc, "the_table")), + "table in display none tree shouldn't be accessible" + ); + ok( + !isAccessible(findAccessibleChildByID(accDoc, "the_row")), + "row shouldn't be accessible" + ); + + // Remove the_row element (since it did not have accessible, no event needed). + await invokeContentTask(browser, [], () => { + content.document.body.removeChild( + content.document.getElementById("the_row") + ); + }); + + // make sure no accessibles have stuck around. + ok( + !isAccessible(findAccessibleChildByID(accDoc, "the_row")), + "row shouldn't be accessible" + ); + ok( + !isAccessible(findAccessibleChildByID(accDoc, "the_table")), + "table shouldn't be accessible" + ); + ok( + !isAccessible(findAccessibleChildByID(accDoc, "the_displayNone")), + "display none things shouldn't be accessible" + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js new file mode 100644 index 0000000000..add454ceac --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js @@ -0,0 +1,77 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +const snippet = ` + +`; + +addAccessibleTask( + snippet, + async function(browser, accDoc) { + await invokeFocus(browser, "select"); + // Expand the select. A dropdown item should get focus. + // Note that the dropdown is rendered in the parent process. + let focused = waitForEvent( + EVENT_FOCUS, + event => event.accessible.role == ROLE_COMBOBOX_OPTION, + "Dropdown item focused after select expanded" + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_DOWN", { altKey: true }, content); + }); + let event = await focused; + let dropdown = event.accessible.parent; + + let selectedOptionChildren = []; + if (MAC) { + // Checkmark is part of the Mac menu styling. + selectedOptionChildren = [{ STATICTEXT: [] }]; + } + let tree = { + COMBOBOX_LIST: [ + { COMBOBOX_OPTION: selectedOptionChildren }, + { GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] }, + { GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] }, + { COMBOBOX_OPTION: [] }, + ], + }; + testAccessibleTree(dropdown, tree); + + // Collapse the select. Focus should return to the select. + focused = waitForEvent( + EVENT_FOCUS, + "select", + "select focused after collapsed" + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_ESCAPE", {}, content); + }); + await focused; + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_table.js b/accessible/tests/browser/e10s/browser_treeupdate_table.js new file mode 100644 index 0000000000..5c2903225a --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_table.js @@ -0,0 +1,48 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + + + + + +
    cell1cell2
    `, + async function(browser, accDoc) { + let table = findAccessibleChildByID(accDoc, "table"); + + let tree = { + TABLE: [ + { ROW: [{ CELL: [{ TEXT_LEAF: [] }] }, { CELL: [{ TEXT_LEAF: [] }] }] }, + ], + }; + testAccessibleTree(table, tree); + + let onReorder = waitForEvent(EVENT_REORDER, "table"); + await invokeContentTask(browser, [], () => { + // append a caption, it should appear as a first element in the + // accessible tree. + let doc = content.document; + let caption = doc.createElement("caption"); + caption.textContent = "table caption"; + doc.getElementById("table").appendChild(caption); + }); + await onReorder; + + tree = { + TABLE: [ + { CAPTION: [{ TEXT_LEAF: [] }] }, + { ROW: [{ CELL: [{ TEXT_LEAF: [] }] }, { CELL: [{ TEXT_LEAF: [] }] }] }, + ], + }; + testAccessibleTree(table, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js new file mode 100644 index 0000000000..6f89105b86 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js @@ -0,0 +1,38 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function removeTextData(browser, accessible, id, role) { + let tree = { + role, + children: [{ role: ROLE_TEXT_LEAF, name: "text" }], + }; + testAccessibleTree(accessible, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + content.document.getElementById(contentId).firstChild.textContent = ""; + }); + await onReorder; + + tree = { role, children: [] }; + testAccessibleTree(accessible, tree); +} + +addAccessibleTask( + ` +

    text

    +
    text
    `, + async function(browser, accDoc) { + let p = findAccessibleChildByID(accDoc, "p"); + let pre = findAccessibleChildByID(accDoc, "pre"); + await removeTextData(browser, p, "p", ROLE_PARAGRAPH); + await removeTextData(browser, pre, "pre", ROLE_TEXT_CONTAINER); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_visibility.js b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js new file mode 100644 index 0000000000..4583056586 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js @@ -0,0 +1,342 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function testTreeOnHide(browser, accDoc, containerID, id, before, after) { + let acc = findAccessibleChildByID(accDoc, containerID); + testAccessibleTree(acc, before); + + let onReorder = waitForEvent(EVENT_REORDER, containerID); + await invokeSetStyle(browser, id, "visibility", "hidden"); + await onReorder; + + testAccessibleTree(acc, after); +} + +async function test3(browser, accessible) { + let tree = { + SECTION: [ + // container + { + SECTION: [ + // parent + { + SECTION: [ + // child + { TEXT_LEAF: [] }, + ], + }, + ], + }, + { + SECTION: [ + // parent2 + { + SECTION: [ + // child2 + { TEXT_LEAF: [] }, + ], + }, + ], + }, + ], + }; + testAccessibleTree(accessible, tree); + + let onReorder = waitForEvent(EVENT_REORDER, "t3_container"); + await invokeContentTask(browser, [], () => { + let doc = content.document; + doc.getElementById("t3_container").style.color = "red"; + doc.getElementById("t3_parent").style.visibility = "hidden"; + doc.getElementById("t3_parent2").style.visibility = "hidden"; + }); + await onReorder; + + tree = { + SECTION: [ + // container + { + SECTION: [ + // child + { TEXT_LEAF: [] }, + ], + }, + { + SECTION: [ + // child2 + { TEXT_LEAF: [] }, + ], + }, + ], + }; + testAccessibleTree(accessible, tree); +} + +async function test4(browser, accessible) { + let tree = { + SECTION: [{ TABLE: [{ ROW: [{ CELL: [] }] }] }], + }; + testAccessibleTree(accessible, tree); + + let onReorder = waitForEvent(EVENT_REORDER, "t4_parent"); + await invokeContentTask(browser, [], () => { + let doc = content.document; + doc.getElementById("t4_container").style.color = "red"; + doc.getElementById("t4_child").style.visibility = "visible"; + }); + await onReorder; + + tree = { + SECTION: [ + { + TABLE: [ + { + ROW: [ + { + CELL: [ + { + SECTION: [ + { + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + testAccessibleTree(accessible, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_visibility.html", + async function(browser, accDoc) { + let t3Container = findAccessibleChildByID(accDoc, "t3_container"); + let t4Container = findAccessibleChildByID(accDoc, "t4_container"); + + await testTreeOnHide( + browser, + accDoc, + "t1_container", + "t1_parent", + { + SECTION: [ + { + SECTION: [ + { + SECTION: [{ TEXT_LEAF: [] }], + }, + ], + }, + ], + }, + { + SECTION: [ + { + SECTION: [{ TEXT_LEAF: [] }], + }, + ], + } + ); + + await testTreeOnHide( + browser, + accDoc, + "t2_container", + "t2_grandparent", + { + SECTION: [ + { + // container + SECTION: [ + { + // grand parent + SECTION: [ + { + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + { + SECTION: [ + { + // child2 + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + SECTION: [ + { + // container + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + { + SECTION: [ + { + // child2 + TEXT_LEAF: [], + }, + ], + }, + ], + } + ); + + await test3(browser, t3Container); + await test4(browser, t4Container); + + await testTreeOnHide( + browser, + accDoc, + "t5_container", + "t5_subcontainer", + { + SECTION: [ + { + // container + SECTION: [ + { + // subcontainer + TABLE: [ + { + ROW: [ + { + CELL: [ + { + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + SECTION: [ + { + // container + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + ], + } + ); + + await testTreeOnHide( + browser, + accDoc, + "t6_container", + "t6_subcontainer", + { + SECTION: [ + { + // container + SECTION: [ + { + // subcontainer + TABLE: [ + { + ROW: [ + { + CELL: [ + { + TABLE: [ + { + // nested table + ROW: [ + { + CELL: [ + { + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + SECTION: [ + { + // child2 + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + { + SECTION: [ + { + // container + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + { + SECTION: [ + { + // child2 + TEXT_LEAF: [], + }, + ], + }, + ], + } + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js new file mode 100644 index 0000000000..7f7e4ba97d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js @@ -0,0 +1,68 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "e10s/doc_treeupdate_whitespace.html", + async function(browser, accDoc) { + let container1 = findAccessibleChildByID(accDoc, "container1"); + let container2Parent = findAccessibleChildByID(accDoc, "container2-parent"); + + let tree = { + SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + ], + }; + testAccessibleTree(container1, tree); + + let onReorder = waitForEvent(EVENT_REORDER, "container1"); + // Remove img1 from container1 + await invokeContentTask(browser, [], () => { + let doc = content.document; + doc.getElementById("container1").removeChild(doc.getElementById("img1")); + }); + await onReorder; + + tree = { + SECTION: [{ GRAPHIC: [] }, { TEXT_LEAF: [] }, { GRAPHIC: [] }], + }; + testAccessibleTree(container1, tree); + + tree = { + SECTION: [{ LINK: [] }, { LINK: [{ GRAPHIC: [] }] }], + }; + testAccessibleTree(container2Parent, tree); + + onReorder = waitForEvent(EVENT_REORDER, "container2-parent"); + // Append an img with valid src to container2 + await invokeContentTask(browser, [], () => { + let doc = content.document; + let img = doc.createElement("img"); + img.setAttribute( + "src", + "http://example.com/a11y/accessible/tests/mochitest/moz.png" + ); + doc.getElementById("container2").appendChild(img); + }); + await onReorder; + + tree = { + SECTION: [ + { LINK: [{ GRAPHIC: [] }] }, + { TEXT_LEAF: [] }, + { LINK: [{ GRAPHIC: [] }] }, + ], + }; + testAccessibleTree(container2Parent, tree); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html b/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html new file mode 100644 index 0000000000..9d08854b9a --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html @@ -0,0 +1,23 @@ + + + + Tree Update ARIA Dialog Test + + + + + diff --git a/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html b/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html new file mode 100644 index 0000000000..38b5c333a1 --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html @@ -0,0 +1,44 @@ + + + + Tree Update ARIA Owns Test + + +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + +
    + +
    + +
    +
    + + +
    + +
    +
    + + +
    + +
    + hey +
    +
    + + diff --git a/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html b/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html new file mode 100644 index 0000000000..4dd230fc28 --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html @@ -0,0 +1,21 @@ + + + + Tree Update Imagemap Test + + + + b + + +
    +
    + + diff --git a/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml b/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml new file mode 100644 index 0000000000..9c59fb9d11 --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml @@ -0,0 +1,11 @@ + + + + Tree Update Removal Test + + + +
    + + + diff --git a/accessible/tests/browser/e10s/doc_treeupdate_visibility.html b/accessible/tests/browser/e10s/doc_treeupdate_visibility.html new file mode 100644 index 0000000000..00213b2b70 --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_visibility.html @@ -0,0 +1,78 @@ + + + + Tree Update Visibility Test + + + +
    +
    +
    text
    +
    +
    + + +
    +
    +
    +
    text
    +
    text
    +
    +
    +
    + + +
    +
    +
    text
    +
    +
    +
    text
    +
    +
    + + +
    + + + + +
    + +
    +
    + + +
    +
    + + + + +
    +
    text
    +
    +
    +
    + + +
    +
    + + + + +
    + + + + +
    +
    text
    +
    +
    +
    text
    +
    +
    + + diff --git a/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html b/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html new file mode 100644 index 0000000000..f17dbbd60e --- /dev/null +++ b/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html @@ -0,0 +1,10 @@ + + + + Whitespace text accessible creation/destruction + + +
    +
    + + diff --git a/accessible/tests/browser/e10s/head.js b/accessible/tests/browser/e10s/head.js new file mode 100644 index 0000000000..672aa46171 --- /dev/null +++ b/accessible/tests/browser/e10s/head.js @@ -0,0 +1,19 @@ +/* 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/. */ + +"use strict"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); diff --git a/accessible/tests/browser/events/browser.ini b/accessible/tests/browser/events/browser.ini new file mode 100644 index 0000000000..fe1d15917d --- /dev/null +++ b/accessible/tests/browser/events/browser.ini @@ -0,0 +1,17 @@ +[DEFAULT] +support-files = + head.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/mochitest/*.js + !/accessible/tests/browser/*.jsm + +[browser_test_docload.js] +skip-if = e10s +[browser_test_scrolling.js] +[browser_test_textcaret.js] +[browser_test_focus_browserui.js] +skip-if = sessionHistoryInParent +[browser_test_focus_dialog.js] +[browser_test_focus_urlbar.js] +[browser_test_A11yUtils_announce.js] +[browser_test_selection_urlbar.js] diff --git a/accessible/tests/browser/events/browser_test_A11yUtils_announce.js b/accessible/tests/browser/events/browser_test_A11yUtils_announce.js new file mode 100644 index 0000000000..b2848f35c2 --- /dev/null +++ b/accessible/tests/browser/events/browser_test_A11yUtils_announce.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +// Check that the browser A11yUtils.announce() function works correctly. +// Note that this does not use mozilla::a11y::Accessible::Announce and a11y +// announcement events, as these aren't yet supported on desktop. +async function runTests() { + const alert = document.getElementById("a11y-announcement"); + let alerted = waitForEvent(EVENT_ALERT, alert); + A11yUtils.announce({ raw: "first" }); + let event = await alerted; + const alertAcc = event.accessible; + is(alertAcc.role, ROLE_ALERT); + ok(!alertAcc.name); + is(alertAcc.childCount, 1); + is(alertAcc.firstChild.name, "first"); + + alerted = waitForEvent(EVENT_ALERT, alertAcc); + A11yUtils.announce({ raw: "second" }); + event = await alerted; + ok(!alertAcc.name); + is(alertAcc.childCount, 1); + is(alertAcc.firstChild.name, "second"); + + info("Testing Fluent message"); + // We need a simple Fluent message here without arguments or attributes. + const fluentId = "search-one-offs-with-title"; + const fluentMessage = await document.l10n.formatValue(fluentId); + alerted = waitForEvent(EVENT_ALERT, alertAcc); + A11yUtils.announce({ id: fluentId }); + event = await alerted; + ok(!alertAcc.name); + is(alertAcc.childCount, 1); + is(alertAcc.firstChild.name, fluentMessage); + + info("Ensuring Fluent message is cancelled if announce is re-entered"); + alerted = waitForEvent(EVENT_ALERT, alertAcc); + // This call runs async. + let asyncAnnounce = A11yUtils.announce({ id: fluentId }); + // Before the async call finishes, call announce again. + A11yUtils.announce({ raw: "third" }); + // Wait for the async call to complete. + await asyncAnnounce; + event = await alerted; + ok(!alertAcc.name); + is(alertAcc.childCount, 1); + // The async call should have been cancelled. If it wasn't, we would get + // fluentMessage here instead of "third". + is(alertAcc.firstChild.name, "third"); +} + +addAccessibleTask(``, runTests); diff --git a/accessible/tests/browser/events/browser_test_docload.js b/accessible/tests/browser/events/browser_test_docload.js new file mode 100644 index 0000000000..53722a582a --- /dev/null +++ b/accessible/tests/browser/events/browser_test_docload.js @@ -0,0 +1,120 @@ +/* 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/. */ + +"use strict"; + +function busyChecker(isBusy) { + return function(event) { + let scEvent; + try { + scEvent = event.QueryInterface(nsIAccessibleStateChangeEvent); + } catch (e) { + return false; + } + + return scEvent.state == STATE_BUSY && scEvent.isEnabled == isBusy; + }; +} + +function inIframeChecker(iframeId) { + return function(event) { + return getAccessibleDOMNodeID(event.accessibleDocument.parent) == iframeId; + }; +} + +function urlChecker(url) { + return function(event) { + info(`${event.accessibleDocument.URL} == ${url}`); + return event.accessibleDocument.URL == url; + }; +} + +async function runTests(browser, accDoc) { + let onLoadEvents = waitForEvents({ + expected: [ + [EVENT_REORDER, getAccessible(browser)], + [EVENT_DOCUMENT_LOAD_COMPLETE, "body2"], + [EVENT_STATE_CHANGE, busyChecker(false)], + ], + unexpected: [ + [EVENT_DOCUMENT_LOAD_COMPLETE, inIframeChecker("iframe1")], + [EVENT_STATE_CHANGE, inIframeChecker("iframe1")], + ], + }); + + BrowserTestUtils.loadURI( + browser, + `data:text/html;charset=utf-8, + + + ` + ); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:about")], + [EVENT_STATE_CHANGE, busyChecker(false)], + [EVENT_REORDER, getAccessible(browser)], + ]); + + BrowserTestUtils.loadURI(browser, "about:about"); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_RELOAD, evt => evt.isFromUserInput], + [EVENT_REORDER, getAccessible(browser)], + [EVENT_STATE_CHANGE, busyChecker(false)], + ]); + + EventUtils.synthesizeKey("VK_F5", {}, browser.ownerGlobal); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:mozilla")], + [EVENT_STATE_CHANGE, busyChecker(false)], + [EVENT_REORDER, getAccessible(browser)], + ]); + + BrowserTestUtils.loadURI(browser, "about:mozilla"); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_RELOAD, evt => !evt.isFromUserInput], + [EVENT_REORDER, getAccessible(browser)], + [EVENT_STATE_CHANGE, busyChecker(false)], + ]); + + browser.reload(); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("http://www.wronguri.wronguri/")], + [EVENT_STATE_CHANGE, busyChecker(false)], + [EVENT_REORDER, getAccessible(browser)], + ]); + + BrowserTestUtils.loadURI(browser, "http://www.wronguri.wronguri/"); + + await onLoadEvents; + + onLoadEvents = waitForEvents([ + [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("https://nocert.example.com/")], + [EVENT_STATE_CHANGE, busyChecker(false)], + [EVENT_REORDER, getAccessible(browser)], + ]); + + BrowserTestUtils.loadURI(browser, "https://nocert.example.com:443/"); + + await onLoadEvents; +} + +/** + * Test caching of accessible object states + */ +addAccessibleTask("", runTests); diff --git a/accessible/tests/browser/events/browser_test_focus_browserui.js b/accessible/tests/browser/events/browser_test_focus_browserui.js new file mode 100644 index 0000000000..98fc735269 --- /dev/null +++ b/accessible/tests/browser/events/browser_test_focus_browserui.js @@ -0,0 +1,52 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/states.js */ +/* import-globals-from ../../mochitest/role.js */ +loadScripts( + { name: "states.js", dir: MOCHITESTS_DIR }, + { name: "role.js", dir: MOCHITESTS_DIR } +); + +async function runTests(browser, accDoc) { + let onFocus = waitForEvent(EVENT_FOCUS, "input"); + EventUtils.synthesizeKey("VK_TAB", {}, browser.ownerGlobal); + let evt = await onFocus; + testStates(evt.accessible, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "buttonInputDoc"); + let url = snippetToURL(``, { + contentDocBodyAttrs: { id: "buttonInputDoc" }, + }); + browser.loadURI(url, { + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + evt = await onFocus; + testStates(evt.accessible, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "input"); + browser.goBack(); + evt = await onFocus; + testStates(evt.accessible, STATE_FOCUSED); + + onFocus = waitForEvent( + EVENT_FOCUS, + event => event.accessible.DOMNode == gURLBar.inputField + ); + EventUtils.synthesizeKey("t", { accelKey: true }, browser.ownerGlobal); + evt = await onFocus; + testStates(evt.accessible, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "input"); + EventUtils.synthesizeKey("w", { accelKey: true }, browser.ownerGlobal); + evt = await onFocus; + testStates(evt.accessible, STATE_FOCUSED); +} + +/** + * Accessibility loading document events test. + */ +addAccessibleTask(``, runTests); diff --git a/accessible/tests/browser/events/browser_test_focus_dialog.js b/accessible/tests/browser/events/browser_test_focus_dialog.js new file mode 100644 index 0000000000..71485a678d --- /dev/null +++ b/accessible/tests/browser/events/browser_test_focus_dialog.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/states.js */ +/* import-globals-from ../../mochitest/role.js */ +loadScripts( + { name: "states.js", dir: MOCHITESTS_DIR }, + { name: "role.js", dir: MOCHITESTS_DIR } +); + +async function runTests(browser, accDoc) { + let onFocus = waitForEvent(EVENT_FOCUS, "button"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("button").focus(); + }); + let button = (await onFocus).accessible; + testStates(button, STATE_FOCUSED); + + // Bug 1377942 - The target of the focus event changes under different + // circumstances. + // In e10s the focus event is the new window, in non-e10s it's the doc. + onFocus = waitForEvent(EVENT_FOCUS, () => true); + let newWin = await BrowserTestUtils.openNewBrowserWindow(); + // button should be blurred + await onFocus; + testStates(button, 0, 0, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "button"); + await BrowserTestUtils.closeWindow(newWin); + testStates((await onFocus).accessible, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "body2"); + await SpecialPowers.spawn(browser, [], () => { + content.document + .getElementById("editabledoc") + .contentWindow.document.body.focus(); + }); + testStates((await onFocus).accessible, STATE_FOCUSED); + + onFocus = waitForEvent(EVENT_FOCUS, "body2"); + newWin = await BrowserTestUtils.openNewBrowserWindow(); + await BrowserTestUtils.closeWindow(newWin); + testStates((await onFocus).accessible, STATE_FOCUSED); + + let onShow = waitForEvent(EVENT_SHOW, "alertdialog"); + onFocus = waitForEvent(EVENT_FOCUS, "alertdialog"); + await SpecialPowers.spawn(browser, [], () => { + let alertDialog = content.document.getElementById("alertdialog"); + alertDialog.style.display = "block"; + alertDialog.focus(); + }); + await onShow; + testStates((await onFocus).accessible, STATE_FOCUSED); +} + +/** + * Accessible dialog focus testing + */ +addAccessibleTask( + ` + + + `, + runTests +); diff --git a/accessible/tests/browser/events/browser_test_focus_urlbar.js b/accessible/tests/browser/events/browser_test_focus_urlbar.js new file mode 100644 index 0000000000..874a0e239b --- /dev/null +++ b/accessible/tests/browser/events/browser_test_focus_urlbar.js @@ -0,0 +1,410 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../../mochitest/states.js */ +/* import-globals-from ../../mochitest/role.js */ +loadScripts( + { name: "states.js", dir: MOCHITESTS_DIR }, + { name: "role.js", dir: MOCHITESTS_DIR } +); + +XPCOMUtils.defineLazyModuleGetters(this, { + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm", + PlacesUtils: "resource://gre/modules/PlacesUtils.jsm", + UrlbarProvider: "resource:///modules/UrlbarUtils.jsm", + UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm", + UrlbarResult: "resource:///modules/UrlbarResult.jsm", + UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm", + UrlbarUtils: "resource:///modules/UrlbarUtils.jsm", +}); + +function isEventForAutocompleteItem(event) { + return event.accessible.role == ROLE_COMBOBOX_OPTION; +} + +function isEventForButton(event) { + return event.accessible.role == ROLE_PUSHBUTTON; +} + +function isEventForOneOffEngine(event) { + let parent = event.accessible.parent; + return ( + event.accessible.role == ROLE_PUSHBUTTON && + parent && + parent.role == ROLE_GROUPING && + parent.name + ); +} + +function isEventForMenuPopup(event) { + return event.accessible.role == ROLE_MENUPOPUP; +} + +function isEventForMenuItem(event) { + return event.accessible.role == ROLE_MENUITEM; +} + +function isEventForTipButton(event) { + let parent = event.accessible.parent; + return ( + event.accessible.role == ROLE_PUSHBUTTON && + parent && + parent.role == ROLE_GROUPING && + parent.name + ); +} + +/** + * A test provider. + */ +class TipTestProvider extends UrlbarProvider { + constructor(matches) { + super(); + this._matches = matches; + } + get name() { + return "TipTestProvider"; + } + get type() { + return UrlbarUtils.PROVIDER_TYPE.PROFILE; + } + isActive(context) { + return true; + } + isRestricting(context) { + return true; + } + async startQuery(context, addCallback) { + this._context = context; + for (const match of this._matches) { + addCallback(this, match); + } + } +} + +// Check that the URL bar manages accessibility focus appropriately. +async function runTests() { + registerCleanupFunction(async function() { + await UrlbarTestUtils.promisePopupClose(window); + await PlacesUtils.history.clear(); + }); + + await PlacesTestUtils.addVisits([ + "http://example1.com/blah", + "http://example2.com/blah", + "http://example1.com/", + "http://example2.com/", + ]); + + // Ensure initial state. + await UrlbarTestUtils.promisePopupClose(window); + + let focused = waitForEvent( + EVENT_FOCUS, + event => event.accessible.role == ROLE_ENTRY + ); + gURLBar.focus(); + let event = await focused; + let textBox = event.accessible; + // Ensure the URL bar is ready for a new URL to be typed. + // Sometimes, when this test runs, the existing text isn't selected when the + // URL bar is focused. Pressing escape twice ensures that the popup is + // closed and that the existing text is selected. + EventUtils.synthesizeKey("KEY_Escape"); + EventUtils.synthesizeKey("KEY_Escape"); + + info("Ensuring no focus change when first text is typed"); + await UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + waitForFocus, + value: "example", + fireInputEvent: true, + }); + // Wait a tick for a11y events to fire. + await TestUtils.waitForTick(); + testStates(textBox, STATE_FOCUSED); + + info("Ensuring no focus change on backspace"); + EventUtils.synthesizeKey("KEY_Backspace"); + await UrlbarTestUtils.promiseSearchComplete(window); + // Wait a tick for a11y events to fire. + await TestUtils.waitForTick(); + testStates(textBox, STATE_FOCUSED); + + info("Ensuring no focus change on text selection and delete"); + EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true }); + EventUtils.synthesizeKey("KEY_Delete"); + await UrlbarTestUtils.promiseSearchComplete(window); + // Wait a tick for a11y events to fire. + await TestUtils.waitForTick(); + testStates(textBox, STATE_FOCUSED); + + info("Ensuring autocomplete focus on down arrow (1)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring focus of another autocomplete item on down arrow"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring previous arrow selection state doesn't get stale on input"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.sendString("z"); + await focused; + EventUtils.synthesizeKey("KEY_Backspace"); + await UrlbarTestUtils.promiseSearchComplete(window); + testStates(textBox, STATE_FOCUSED); + + info("Ensuring focus of another autocomplete item on down arrow"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + if (AppConstants.platform == "macosx") { + info("Ensuring focus of another autocomplete item on ctrl-n"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("n", { ctrlKey: true }); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring focus of another autocomplete item on ctrl-p"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("p", { ctrlKey: true }); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + } + + info("Ensuring focus of another autocomplete item on up arrow"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowUp"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring text box focus on left arrow"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + await focused; + testStates(textBox, STATE_FOCUSED); + + gURLBar.view.close(); + // On Mac, down arrow when not at the end of the field moves to the end. + // Move back to the end so the next press of down arrow opens the popup. + EventUtils.synthesizeKey("KEY_ArrowRight"); + + info("Ensuring autocomplete focus on down arrow (2)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring autocomplete focus on arrow up for search settings button"); + focused = waitForEvent(EVENT_FOCUS, isEventForButton); + EventUtils.synthesizeKey("KEY_ArrowUp"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring text box focus when text is typed"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.sendString("z"); + await focused; + testStates(textBox, STATE_FOCUSED); + EventUtils.synthesizeKey("KEY_Backspace"); + await UrlbarTestUtils.promiseSearchComplete(window); + + info("Ensuring autocomplete focus on down arrow (3)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring text box focus on backspace"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.synthesizeKey("KEY_Backspace"); + await focused; + testStates(textBox, STATE_FOCUSED); + await UrlbarTestUtils.promiseSearchComplete(window); + + info("Ensuring autocomplete focus on arrow down (4)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + // Arrow down to the last result. + const resultCount = UrlbarTestUtils.getResultCount(window); + while (UrlbarTestUtils.getSelectedRowIndex(window) != resultCount - 1) { + EventUtils.synthesizeKey("KEY_ArrowDown"); + } + + info("Ensuring one-off search button focus on arrow down"); + focused = waitForEvent(EVENT_FOCUS, isEventForOneOffEngine); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring autocomplete focus on arrow up"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowUp"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring text box focus on text selection"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true }); + await focused; + testStates(textBox, STATE_FOCUSED); + + if (AppConstants.platform == "macosx") { + // On Mac, ctrl-n after arrow left/right does not re-open the popup. + // Type some text so the next press of ctrl-n opens the popup. + EventUtils.sendString("ple"); + + info("Ensuring autocomplete focus on ctrl-n"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("n", { ctrlKey: true }); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + } + + info( + "Ensuring context menu gets menu event on launch, item focus on down, and address bar focus on escape." + ); + let menuEvent = waitForEvent( + nsIAccessibleEvent.EVENT_MENUPOPUP_START, + isEventForMenuPopup + ); + await EventUtils.sendMouseEvent( + { type: "contextmenu" }, + gURLBar.querySelector("moz-input-box") + ); + await menuEvent; + + focused = waitForEvent(EVENT_FOCUS, isEventForMenuItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + focused = waitForEvent(EVENT_FOCUS, textBox); + let closed = waitForEvent( + nsIAccessibleEvent.EVENT_MENUPOPUP_END, + isEventForMenuPopup + ); + EventUtils.synthesizeKey("KEY_Escape"); + await closed; + await focused; + testStates(textBox, STATE_FOCUSED); +} + +// We test TIP results in their own test so the spoofed results don't interfere +// with the main test. +async function runTipTests() { + let matches = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/a" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.TIP, + UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, + { + icon: "", + text: "This is a test intervention.", + buttonText: "Done", + type: "test", + helpUrl: "about:blank", + buttonUrl: "about:mozilla", + } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/b" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/c" } + ), + ]; + + // Ensure the tip appears in the expected position. + matches[1].suggestedIndex = 2; + + let provider = new TipTestProvider(matches); + UrlbarProvidersManager.registerProvider(provider); + + registerCleanupFunction(async function() { + UrlbarProvidersManager.unregisterProvider(provider); + }); + + let focused = waitForEvent( + EVENT_FOCUS, + event => event.accessible.role == ROLE_ENTRY + ); + gURLBar.focus(); + let event = await focused; + let textBox = event.accessible; + + EventUtils.synthesizeKey("KEY_Escape"); + EventUtils.synthesizeKey("KEY_Escape"); + + info("Ensuring no focus change when first text is typed"); + await UrlbarTestUtils.promiseAutocompleteResultPopup({ + window, + waitForFocus, + value: "example", + fireInputEvent: true, + }); + // Wait a tick for a11y events to fire. + await TestUtils.waitForTick(); + testStates(textBox, STATE_FOCUSED); + + info("Ensuring autocomplete focus on down arrow (1)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring the tip button is focused on down arrow"); + info("Also ensuring that the tip button is a part of a labelled group"); + focused = waitForEvent(EVENT_FOCUS, isEventForTipButton); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring the help button is focused on down arrow"); + info("Also ensuring that the help button is a part of a labelled group"); + focused = waitForEvent(EVENT_FOCUS, isEventForTipButton); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring autocomplete focus on down arrow (2)"); + focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem); + EventUtils.synthesizeKey("KEY_ArrowDown"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring the help button is focused on up arrow"); + focused = waitForEvent(EVENT_FOCUS, isEventForTipButton); + EventUtils.synthesizeKey("KEY_ArrowUp"); + event = await focused; + testStates(event.accessible, STATE_FOCUSED); + + info("Ensuring text box focus on left arrow, and not back to the tip button"); + focused = waitForEvent(EVENT_FOCUS, textBox); + EventUtils.synthesizeKey("KEY_ArrowLeft"); + await focused; + testStates(textBox, STATE_FOCUSED); +} + +addAccessibleTask(``, runTests); +addAccessibleTask(``, runTipTests); diff --git a/accessible/tests/browser/events/browser_test_scrolling.js b/accessible/tests/browser/events/browser_test_scrolling.js new file mode 100644 index 0000000000..d9a4a0a73f --- /dev/null +++ b/accessible/tests/browser/events/browser_test_scrolling.js @@ -0,0 +1,96 @@ +/* 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/. */ + +"use strict"; + +addAccessibleTask( + ` +
    one
    +
    two
    +
    +
    three
    +
    `, + async function(browser, accDoc) { + let onScrolling = waitForEvents([ + [EVENT_SCROLLING, accDoc], + [EVENT_SCROLLING_END, accDoc], + ]); + await SpecialPowers.spawn(browser, [], () => { + content.location.hash = "#two"; + }); + let [scrollEvent1, scrollEndEvent1] = await onScrolling; + scrollEvent1.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEvent1.maxScrollY >= scrollEvent1.scrollY, + "scrollY is within max" + ); + scrollEndEvent1.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEndEvent1.maxScrollY >= scrollEndEvent1.scrollY, + "scrollY is within max" + ); + + onScrolling = waitForEvents([ + [EVENT_SCROLLING, accDoc], + [EVENT_SCROLLING_END, accDoc], + ]); + await SpecialPowers.spawn(browser, [], () => { + content.location.hash = "#three"; + }); + let [scrollEvent2, scrollEndEvent2] = await onScrolling; + scrollEvent2.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEvent2.scrollY > scrollEvent1.scrollY, + `${scrollEvent2.scrollY} > ${scrollEvent1.scrollY}` + ); + scrollEndEvent2.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEndEvent2.maxScrollY >= scrollEndEvent2.scrollY, + "scrollY is within max" + ); + + onScrolling = waitForEvents([ + [EVENT_SCROLLING, accDoc], + [EVENT_SCROLLING_END, accDoc], + ]); + await SpecialPowers.spawn(browser, [], () => { + content.scrollTo(10, 0); + }); + let [scrollEvent3, scrollEndEvent3] = await onScrolling; + scrollEvent3.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEvent3.maxScrollX >= scrollEvent3.scrollX, + "scrollX is within max" + ); + scrollEndEvent3.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEndEvent3.maxScrollX >= scrollEndEvent3.scrollX, + "scrollY is within max" + ); + ok( + scrollEvent3.scrollX > scrollEvent2.scrollX, + `${scrollEvent3.scrollX} > ${scrollEvent2.scrollX}` + ); + + // non-doc scrolling + onScrolling = waitForEvents([ + [EVENT_SCROLLING, "three"], + [EVENT_SCROLLING_END, "three"], + ]); + await SpecialPowers.spawn(browser, [], () => { + content.document.querySelector("#three").scrollTo(0, 10); + }); + let [scrollEvent4, scrollEndEvent4] = await onScrolling; + scrollEvent4.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEvent4.maxScrollY >= scrollEvent4.scrollY, + "scrollY is within max" + ); + scrollEndEvent4.QueryInterface(nsIAccessibleScrollingEvent); + ok( + scrollEndEvent4.maxScrollY >= scrollEndEvent4.scrollY, + "scrollY is within max" + ); + } +); diff --git a/accessible/tests/browser/events/browser_test_selection_urlbar.js b/accessible/tests/browser/events/browser_test_selection_urlbar.js new file mode 100644 index 0000000000..0a773ce896 --- /dev/null +++ b/accessible/tests/browser/events/browser_test_selection_urlbar.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +XPCOMUtils.defineLazyModuleGetters(this, { + BrowserTestUtils: "resource://testing-common/BrowserTestUtils.jsm", + PlacesTestUtils: "resource://testing-common/PlacesTestUtils.jsm", + UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm", +}); + +// Check that the URL bar manages accessibility +// selection notifications appropriately on startup (new window). +async function runTests() { + let focused = waitForEvent( + EVENT_FOCUS, + event => event.accessible.role == ROLE_ENTRY + ); + info("Creating new window"); + let newWin = await BrowserTestUtils.openNewBrowserWindow(); + await PlacesTestUtils.addVisits("http://addons.mozilla.org"); + + registerCleanupFunction(async function() { + await BrowserTestUtils.closeWindow(newWin); + await PlacesUtils.history.clear(); + }); + info("Focusing window"); + newWin.focus(); + await focused; + + // Ensure the URL bar is ready for a new URL to be typed. + // Sometimes, when this test runs, the existing text isn't selected when the + // URL bar is focused. Pressing escape twice ensures that the popup is + // closed and that the existing text is selected. + EventUtils.synthesizeKey("KEY_Escape", {}, newWin); + EventUtils.synthesizeKey("KEY_Escape", {}, newWin); + let caretMoved = waitForEvent( + EVENT_TEXT_CARET_MOVED, + event => event.accessible.role == ROLE_ENTRY + ); + + info("Autofilling after typing `a` in new window URL bar."); + EventUtils.synthesizeKey("a", {}, newWin); + await UrlbarTestUtils.promiseSearchComplete(newWin); + Assert.equal( + newWin.gURLBar.inputField.value, + "addons.mozilla.org/", + "autofilled value as expected" + ); + + info("Ensuring caret moved on text selection"); + await caretMoved; +} + +addAccessibleTask(``, runTests); diff --git a/accessible/tests/browser/events/browser_test_textcaret.js b/accessible/tests/browser/events/browser_test_textcaret.js new file mode 100644 index 0000000000..d4e0f11a0f --- /dev/null +++ b/accessible/tests/browser/events/browser_test_textcaret.js @@ -0,0 +1,58 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Caret move events checker. + */ +function caretMoveChecker(target, caretOffset) { + return function(event) { + let cmEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent); + return ( + cmEvent.accessible == getAccessible(target) && + cmEvent.caretOffset == caretOffset + ); + }; +} + +async function checkURLBarCaretEvents() { + const kURL = "about:mozilla"; + let newWin = await BrowserTestUtils.openNewBrowserWindow(); + BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, kURL); + newWin.gBrowser.selectedBrowser.focus(); + + await waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, event => { + try { + return event.accessible.QueryInterface(nsIAccessibleDocument).URL == kURL; + } catch (e) { + return false; + } + }); + info("Loaded " + kURL); + + let urlbarInputEl = newWin.gURLBar.inputField; + let urlbarInput = getAccessible(urlbarInputEl, [nsIAccessibleText]); + + let onCaretMove = waitForEvents([ + [EVENT_TEXT_CARET_MOVED, caretMoveChecker(urlbarInput, kURL.length)], + [EVENT_FOCUS, urlbarInput], + ]); + + urlbarInput.caretOffset = -1; + await onCaretMove; + ok(true, "Caret move in URL bar #1"); + + onCaretMove = waitForEvent( + EVENT_TEXT_CARET_MOVED, + caretMoveChecker(urlbarInput, 0) + ); + + urlbarInput.caretOffset = 0; + await onCaretMove; + ok(true, "Caret move in URL bar #2"); + + await BrowserTestUtils.closeWindow(newWin); +} + +add_task(checkURLBarCaretEvents); diff --git a/accessible/tests/browser/events/head.js b/accessible/tests/browser/events/head.js new file mode 100644 index 0000000000..672aa46171 --- /dev/null +++ b/accessible/tests/browser/events/head.js @@ -0,0 +1,19 @@ +/* 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/. */ + +"use strict"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); diff --git a/accessible/tests/browser/fission/browser.ini b/accessible/tests/browser/fission/browser.ini new file mode 100644 index 0000000000..a3fc7aa839 --- /dev/null +++ b/accessible/tests/browser/fission/browser.ini @@ -0,0 +1,14 @@ +[DEFAULT] +support-files = + head.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/browser/*.jsm + !/accessible/tests/mochitest/*.js + +[browser_content_tree.js] +[browser_hidden_iframe.js] +[browser_nested_iframe.js] +[browser_reframe_root.js] +[browser_reframe_visibility.js] +[browser_src_change.js] +[browser_take_focus.js] diff --git a/accessible/tests/browser/fission/browser_content_tree.js b/accessible/tests/browser/fission/browser_content_tree.js new file mode 100644 index 0000000000..54df06c7f4 --- /dev/null +++ b/accessible/tests/browser/fission/browser_content_tree.js @@ -0,0 +1,75 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + + + + +
    cell1cell2
    +
      +
    • item1
    • +
    `, + async function(browser, iframeDocAcc, contentDocAcc) { + ok(iframeDocAcc, "IFRAME document accessible is present"); + (gIsRemoteIframe ? isnot : is)( + browser.browsingContext.currentWindowGlobal.osPid, + browser.browsingContext.children[0].currentWindowGlobal.osPid, + `Content and IFRAME documents are in ${ + gIsRemoteIframe ? "separate processes" : "same process" + }.` + ); + + const tree = { + DOCUMENT: [ + { + INTERNAL_FRAME: [ + { + DOCUMENT: [ + { + TABLE: [ + { + ROW: [ + { CELL: [{ TEXT_LEAF: [] }] }, + { CELL: [{ TEXT_LEAF: [] }] }, + ], + }, + ], + }, + { + LIST: [ + { + LISTITEM: [{ LISTITEM_MARKER: [] }, { TEXT_LEAF: [] }], + }, + ], + }, + ], + }, + ], + }, + ], + }; + testAccessibleTree(contentDocAcc, tree); + + const iframeAcc = contentDocAcc.getChildAt(0); + is( + iframeAcc.getChildAt(0), + iframeDocAcc, + "Document for the IFRAME matches IFRAME's first child." + ); + + is( + iframeDocAcc.parent, + iframeAcc, + "IFRAME document's parent matches the IFRAME." + ); + }, + { topLevel: false, iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/fission/browser_hidden_iframe.js b/accessible/tests/browser/fission/browser_hidden_iframe.js new file mode 100644 index 0000000000..94b3015424 --- /dev/null +++ b/accessible/tests/browser/fission/browser_hidden_iframe.js @@ -0,0 +1,81 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/states.js */ +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "states.js", dir: MOCHITESTS_DIR }); +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ``, + async function(browser, contentDocAcc) { + info( + "Check that the IFRAME and the IFRAME document are not accessible initially." + ); + let iframeAcc = findAccessibleChildByID(contentDocAcc, DEFAULT_IFRAME_ID); + let iframeDocAcc = findAccessibleChildByID( + contentDocAcc, + DEFAULT_IFRAME_DOC_BODY_ID + ); + ok(!iframeAcc, "IFRAME is hidden and should not be accessible"); + ok(!iframeDocAcc, "IFRAME document is hidden and should be accessible"); + + info( + "Show the IFRAME and check that it's now available in the accessibility tree." + ); + + const events = [[EVENT_REORDER, contentDocAcc]]; + // Until this event is fired, IFRAME accessible has no children attached. + events.push([ + EVENT_STATE_CHANGE, + event => { + const scEvent = event.QueryInterface(nsIAccessibleStateChangeEvent); + const id = getAccessibleDOMNodeID(event.accessible); + return ( + id === DEFAULT_IFRAME_DOC_BODY_ID && + scEvent.state === STATE_BUSY && + scEvent.isEnabled === false + ); + }, + ]); + + const onEvents = waitForEvents(events); + await SpecialPowers.spawn(browser, [DEFAULT_IFRAME_ID], contentId => { + content.document.getElementById(contentId).style.display = ""; + }); + await onEvents; + + iframeAcc = findAccessibleChildByID(contentDocAcc, DEFAULT_IFRAME_ID); + iframeDocAcc = findAccessibleChildByID( + contentDocAcc, + DEFAULT_IFRAME_DOC_BODY_ID + ); + + ok(!isDefunct(iframeAcc), "IFRAME should be accessible"); + is(iframeAcc.childCount, 1, "IFRAME accessible should have a single child"); + ok(iframeDocAcc, "IFRAME document exists"); + ok(!isDefunct(iframeDocAcc), "IFRAME document should be accessible"); + is( + iframeAcc.firstChild, + iframeDocAcc, + "An accessible for a IFRAME document is the child of the IFRAME accessible" + ); + is( + iframeDocAcc.parent, + iframeAcc, + "IFRAME document's parent matches the IFRAME." + ); + }, + { + topLevel: false, + iframe: true, + remoteIframe: true, + iframeAttrs: { + style: "display: none;", + }, + skipFissionDocLoad: true, + } +); diff --git a/accessible/tests/browser/fission/browser_nested_iframe.js b/accessible/tests/browser/fission/browser_nested_iframe.js new file mode 100644 index 0000000000..0e48d767f8 --- /dev/null +++ b/accessible/tests/browser/fission/browser_nested_iframe.js @@ -0,0 +1,153 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +const NESTED_IFRAME_DOC_BODY_ID = "nested-iframe-body"; +const NESTED_IFRAME_ID = "nested-iframe"; +const nestedURL = new URL(`http://example.com/document-builder.sjs`); +nestedURL.searchParams.append( + "html", + ` + + + Accessibility Nested Iframe Frame Test + + + + + + + +
    cell1cell2
    +
      +
    • item1
    • +
    + + ` +); + +function getOsPid(browsingContext) { + return browsingContext.currentWindowGlobal.osPid; +} + +addAccessibleTask( + ` + + `, + async (browser, accDoc) => { + const searchPred = { + AXSearchKey: "AXFrameSearchKey", + AXImmediateDescendantsOnly: 0, + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + }; + + const webArea = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + is( + webArea.getAttributeValue("AXRole"), + "AXWebArea", + "Got web area accessible" + ); + + const frameCount = webArea.getParameterizedAttributeValue( + "AXUIElementCountForSearchPredicate", + NSDictionary(searchPred) + ); + is(3, frameCount, "Found 3 frames"); + } +); + +/** + * Test rotor with static text + */ +addAccessibleTask( + ` +

    Hello I am a heading

    + This is some regular text.

    this is some paragraph text


    + This is a list:
      +
    • List item one
    • +
    • List item two
    • +
    + + This is a link + `, + async (browser, accDoc) => { + const searchPred = { + AXSearchKey: "AXStaticTextSearchKey", + AXImmediateDescendants: 0, + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + }; + + const webArea = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + is( + webArea.getAttributeValue("AXRole"), + "AXWebArea", + "Got web area accessible" + ); + + const textCount = webArea.getParameterizedAttributeValue( + "AXUIElementCountForSearchPredicate", + NSDictionary(searchPred) + ); + is(7, textCount, "Found 7 pieces of text"); + + const text = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + is( + "Hello I am a heading", + text[0].getAttributeValue("AXValue"), + "Found correct text node for heading" + ); + is( + "This is some regular text.", + text[1].getAttributeValue("AXValue"), + "Found correct text node" + ); + is( + "this is some paragraph text", + text[2].getAttributeValue("AXValue"), + "Found correct text node for paragraph" + ); + is( + "This is a list:", + text[3].getAttributeValue("AXValue"), + "Found correct text node for pre-list text node" + ); + is( + "List item one", + text[4].getAttributeValue("AXValue"), + "Found correct text node for list item one" + ); + is( + "List item two", + text[5].getAttributeValue("AXValue"), + "Found correct text node for list item two" + ); + is( + "This is a link", + text[6].getAttributeValue("AXValue"), + "Found correct text node for link" + ); + } +); + +/** + * Test search with non-webarea root + */ +addAccessibleTask( + ` +

    hello

    world

    +

    goodybe

    + `, + async (browser, accDoc) => { + let searchPred = { + AXSearchKey: "AXAnyTypeSearchKey", + AXImmediateDescendantsOnly: 1, + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + }; + + const searchRoot = getNativeInterface(accDoc, "searchroot"); + const resultCount = searchRoot.getParameterizedAttributeValue( + "AXUIElementCountForSearchPredicate", + NSDictionary(searchPred) + ); + is(resultCount, 2, "Found 2 items"); + + const p1 = getNativeInterface(accDoc, "p1"); + searchPred = { + AXSearchKey: "AXAnyTypeSearchKey", + AXImmediateDescendantsOnly: 1, + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + AXStartElement: p1, + }; + + let results = searchRoot.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + Assert.deepEqual( + results.map(r => r.getAttributeValue("AXDOMIdentifier")), + ["p2"], + "Result is next group sibling" + ); + + searchPred = { + AXSearchKey: "AXAnyTypeSearchKey", + AXImmediateDescendantsOnly: 1, + AXResultsLimit: -1, + AXDirection: "AXDirectionPrevious", + }; + + results = searchRoot.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + Assert.deepEqual( + results.map(r => r.getAttributeValue("AXDOMIdentifier")), + ["p2", "p1"], + "A reverse search should return groups in reverse" + ); + } +); + +/** + * Test search text + */ +addAccessibleTask( + ` +

    It's about the future, isn't it?

    +

    Okay, alright, Saturday is good, Saturday's good, I could spend a week in 1955.

    +
      +
    • I could hang out, you could show me around.
    • +
    • There's that word again, heavy.
    • +
    + `, + async (browser, f, accDoc) => { + let searchPred = { + AXSearchKey: "AXAnyTypeSearchKey", + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + AXSearchText: "could", + }; + + const webArea = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + is( + webArea.getAttributeValue("AXRole"), + "AXWebArea", + "Got web area accessible" + ); + + const textSearchCount = webArea.getParameterizedAttributeValue( + "AXUIElementCountForSearchPredicate", + NSDictionary(searchPred) + ); + is(textSearchCount, 2, "Found 2 matching items in text search"); + + const results = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + + info(results.map(r => r.getAttributeValue("AXMozDebugDescription"))); + + Assert.deepEqual( + results.map(r => r.getAttributeValue("AXValue")), + [ + "Okay, alright, Saturday is good, Saturday's good, I could spend a week in 1955.", + "I could hang out, you could show me around.", + ], + "Correct text search results" + ); + }, + { topLevel: false, iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/mac/browser_selectables.js b/accessible/tests/browser/mac/browser_selectables.js new file mode 100644 index 0000000000..29cd326bdd --- /dev/null +++ b/accessible/tests/browser/mac/browser_selectables.js @@ -0,0 +1,336 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +function getSelectedIds(selectable) { + return selectable + .getAttributeValue("AXSelectedChildren") + .map(c => c.getAttributeValue("AXDOMIdentifier")); +} + +/** + * Test aria tabs + */ +addAccessibleTask("mac/doc_aria_tabs.html", async (browser, accDoc) => { + let tablist = getNativeInterface(accDoc, "tablist"); + is( + tablist.getAttributeValue("AXRole"), + "AXTabGroup", + "Correct role for tablist" + ); + + let tabMacAccs = tablist.getAttributeValue("AXTabs"); + is(tabMacAccs.length, 3, "3 items in AXTabs"); + + let selectedTabs = tablist.getAttributeValue("AXSelectedChildren"); + is(selectedTabs.length, 1, "one selected tab"); + + let tab = selectedTabs[0]; + is(tab.getAttributeValue("AXRole"), "AXRadioButton", "Correct role for tab"); + is( + tab.getAttributeValue("AXSubrole"), + "AXTabButton", + "Correct subrole for tab" + ); + is(tab.getAttributeValue("AXTitle"), "First Tab", "Correct title for tab"); + + let tabToSelect = tabMacAccs[1]; + is( + tabToSelect.getAttributeValue("AXTitle"), + "Second Tab", + "Correct title for tab" + ); + + let actions = tabToSelect.actionNames; + ok(true, actions); + ok(actions.includes("AXPress"), "Has switch action"); + + let evt = waitForMacEvent("AXSelectedChildrenChanged"); + tabToSelect.performAction("AXPress"); + await evt; + + selectedTabs = tablist.getAttributeValue("AXSelectedChildren"); + is(selectedTabs.length, 1, "one selected tab"); + is( + selectedTabs[0].getAttributeValue("AXTitle"), + "Second Tab", + "Correct title for tab" + ); +}); + +addAccessibleTask('

    hello

    ', async (browser, accDoc) => { + let p = getNativeInterface(accDoc, "p"); + ok( + p.attributeNames.includes("AXSelected"), + "html element includes 'AXSelected' attribute" + ); + is(p.getAttributeValue("AXSelected"), 0, "AX selected is 'false'"); +}); + +addAccessibleTask( + ``, + async (browser, accDoc) => { + let select = getNativeInterface(accDoc, "select"); + let one = getNativeInterface(accDoc, "one"); + let two = getNativeInterface(accDoc, "two"); + let three = getNativeInterface(accDoc, "three"); + let four = getNativeInterface(accDoc, "four"); + + is( + select.getAttributeValue("AXTitle"), + "Choose a number", + "Select titled correctly" + ); + ok( + select.attributeNames.includes("AXOrientation"), + "Have orientation attribute" + ); + ok( + select.isAttributeSettable("AXSelectedChildren"), + "Select can have AXSelectedChildren set" + ); + + is(one.getAttributeValue("AXTitle"), "", "Option should not have a title"); + is( + one.getAttributeValue("AXValue"), + "One", + "Option should have correct value" + ); + is( + one.getAttributeValue("AXRole"), + "AXStaticText", + "Options should have AXStaticText role" + ); + ok(one.isAttributeSettable("AXSelected"), "Option can have AXSelected set"); + + is(select.getAttributeValue("AXSelectedChildren").length, 1); + one.setAttributeValue("AXSelected", false); + is(select.getAttributeValue("AXSelectedChildren").length, 0); + + three.setAttributeValue("AXSelected", true); + is(select.getAttributeValue("AXSelectedChildren").length, 1); + ok(getSelectedIds(select).includes("three"), "'three' is selected"); + + select.setAttributeValue("AXSelectedChildren", [one, two]); + Assert.deepEqual( + getSelectedIds(select), + ["one", "two"], + "one and two are selected" + ); + + select.setAttributeValue("AXSelectedChildren", [three, two, four]); + Assert.deepEqual( + getSelectedIds(select), + ["two", "three"], + "two and three are selected, four is disabled so it's not" + ); + + ok(!four.getAttributeValue("AXEnabled"), "Disabled option is disabled"); + } +); + +addAccessibleTask( + ``, + async (browser, accDoc) => { + let select = getNativeInterface(accDoc, "select"); + + is( + select.getAttributeValue("AXTitle"), + "Choose a thing", + "Select titled correctly" + ); + ok( + select.attributeNames.includes("AXOrientation"), + "Have orientation attribute" + ); + ok( + select.isAttributeSettable("AXSelectedChildren"), + "Select can have AXSelectedChildren set" + ); + let childValueSelectablePairs = select + .getAttributeValue("AXChildren") + .map(c => [ + c.getAttributeValue("AXValue"), + c.isAttributeSettable("AXSelected"), + c.getAttributeValue("AXEnabled"), + ]); + Assert.deepEqual( + childValueSelectablePairs, + [ + ["Fruits", false, false], + ["Banana", true, true], + ["Apple", true, true], + ["Orange", true, true], + ["Vegetables", false, false], + ["Lettuce", true, true], + ["Tomato", true, true], + ["Onion", true, true], + ["Spices", false, false], + ["Cumin", true, true], + ["Coriander", true, true], + ["Allspice", true, true], + ["Everything", true, true], + ], + "Options are selectable, group labels are not" + ); + + let allspice = getNativeInterface(accDoc, "allspice"); + is( + allspice.getAttributeValue("AXTitle"), + "", + "Option should not have a title" + ); + is( + allspice.getAttributeValue("AXValue"), + "Allspice", + "Option should have a value" + ); + is( + allspice.getAttributeValue("AXRole"), + "AXStaticText", + "Options should have AXStaticText role" + ); + ok( + allspice.isAttributeSettable("AXSelected"), + "Option can have AXSelected set" + ); + is( + allspice + .getAttributeValue("AXParent") + .getAttributeValue("AXDOMIdentifier"), + "select", + "Select is direct parent of nested option" + ); + + let groupLabel = select.getAttributeValue("AXChildren")[0]; + ok( + !groupLabel.isAttributeSettable("AXSelected"), + "Group label should not be selectable" + ); + is( + groupLabel.getAttributeValue("AXValue"), + "Fruits", + "Group label should have a value" + ); + is( + groupLabel.getAttributeValue("AXTitle"), + null, + "Group label should not have a title" + ); + is( + groupLabel.getAttributeValue("AXRole"), + "AXStaticText", + "Group label should have AXStaticText role" + ); + is( + groupLabel + .getAttributeValue("AXParent") + .getAttributeValue("AXDOMIdentifier"), + "select", + "Select is direct parent of group label" + ); + + Assert.deepEqual(getSelectedIds(select), ["banana", "lettuce", "allspice"]); + } +); + +addAccessibleTask( + `
    +
    One
    +
    Two
    +
    Three
    +
    Four
    +
    `, + async (browser, accDoc) => { + let select = getNativeInterface(accDoc, "select"); + let one = getNativeInterface(accDoc, "one"); + let two = getNativeInterface(accDoc, "two"); + let three = getNativeInterface(accDoc, "three"); + let four = getNativeInterface(accDoc, "four"); + + is( + select.getAttributeValue("AXTitle"), + "Choose a number", + "Select titled correctly" + ); + ok( + select.attributeNames.includes("AXOrientation"), + "Have orientation attribute" + ); + ok( + select.isAttributeSettable("AXSelectedChildren"), + "Select can have AXSelectedChildren set" + ); + + is(one.getAttributeValue("AXTitle"), "", "Option should not have a title"); + is( + one.getAttributeValue("AXValue"), + "One", + "Option should have correct value" + ); + is( + one.getAttributeValue("AXRole"), + "AXStaticText", + "Options should have AXStaticText role" + ); + ok(one.isAttributeSettable("AXSelected"), "Option can have AXSelected set"); + + is(select.getAttributeValue("AXSelectedChildren").length, 1); + let evt = waitForMacEvent("AXSelectedChildrenChanged"); + // Change selection from content. + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("one").removeAttribute("aria-selected"); + }); + await evt; + is(select.getAttributeValue("AXSelectedChildren").length, 0); + + three.setAttributeValue("AXSelected", true); + is(select.getAttributeValue("AXSelectedChildren").length, 1); + ok(getSelectedIds(select).includes("three"), "'three' is selected"); + + select.setAttributeValue("AXSelectedChildren", [one, two]); + Assert.deepEqual( + getSelectedIds(select), + ["one", "two"], + "one and two are selected" + ); + + select.setAttributeValue("AXSelectedChildren", [three, two, four]); + Assert.deepEqual( + getSelectedIds(select), + ["two", "three"], + "two and three are selected, four is disabled so it's not" + ); + } +); diff --git a/accessible/tests/browser/mac/browser_table.js b/accessible/tests/browser/mac/browser_table.js new file mode 100644 index 0000000000..34aeb87a17 --- /dev/null +++ b/accessible/tests/browser/mac/browser_table.js @@ -0,0 +1,281 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +/* import-globals-from ../../mochitest/attributes.js */ +loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); + +/** + * Helper function to test table consistency. + */ +function testTableConsistency(table, expectedRowCount, expectedColumnCount) { + is(table.getAttributeValue("AXRole"), "AXTable", "Correct role for table"); + + let tableChildren = table.getAttributeValue("AXChildren"); + // XXX: Should be expectedRowCount+ExpectedColumnCount+1 children, rows (incl headers) + cols + headers + // if we're trying to match Safari. + is( + tableChildren.length, + expectedRowCount + expectedColumnCount, + "Table has children = rows (4) + cols (3)" + ); + for (let i = 0; i < tableChildren.length; i++) { + let currChild = tableChildren[i]; + if (i < expectedRowCount) { + is( + currChild.getAttributeValue("AXRole"), + "AXRow", + "Correct role for row" + ); + } else { + is( + currChild.getAttributeValue("AXRole"), + "AXColumn", + "Correct role for col" + ); + is( + currChild.getAttributeValue("AXRoleDescription"), + "column", + "Correct role desc for col" + ); + } + } + + is( + table.getAttributeValue("AXColumnCount"), + expectedColumnCount, + "Table has correct column count." + ); + is( + table.getAttributeValue("AXRowCount"), + expectedRowCount, + "Table has correct row count." + ); + + let cols = table.getAttributeValue("AXColumns"); + is(cols.length, expectedColumnCount, "Table has col list of correct length"); + for (let i = 0; i < cols.length; i++) { + let currCol = cols[i]; + let currChildren = currCol.getAttributeValue("AXChildren"); + is( + currChildren.length, + expectedRowCount, + "Column has correct number of cells" + ); + for (let j = 0; j < currChildren.length; j++) { + let currChild = currChildren[j]; + is( + currChild.getAttributeValue("AXRole"), + "AXCell", + "Column child is cell" + ); + } + } + + let rows = table.getAttributeValue("AXRows"); + is(rows.length, expectedRowCount, "Table has row list of correct length"); + for (let i = 0; i < rows.length; i++) { + let currRow = rows[i]; + let currChildren = currRow.getAttributeValue("AXChildren"); + is( + currChildren.length, + expectedColumnCount, + "Row has correct number of cells" + ); + for (let j = 0; j < currChildren.length; j++) { + let currChild = currChildren[j]; + is(currChild.getAttributeValue("AXRole"), "AXCell", "Row child is cell"); + } + } +} + +/** + * Test table, columns, rows + */ +addAccessibleTask( + ` + + + + + + +
    CompanyContactCountry
    Alfreds FutterkisteMaria AndersGermany
    Centro comercial MoctezumaFrancisco ChangMexico
    Ernst HandelRoland MendelAustria
    `, + async (browser, accDoc) => { + let table = getNativeInterface(accDoc, "customers"); + testTableConsistency(table, 4, 3); + + const rowText = [ + "Madrigal Electromotive GmbH", + "Lydia Rodarte-Quayle", + "Germany", + ]; + let reorder = waitForEvent(EVENT_REORDER, "customers"); + await SpecialPowers.spawn(browser, [rowText], _rowText => { + let tr = content.document.createElement("tr"); + for (let t of _rowText) { + let td = content.document.createElement("td"); + td.textContent = t; + tr.appendChild(td); + } + content.document.getElementById("customers").appendChild(tr); + }); + await reorder; + + let cols = table.getAttributeValue("AXColumns"); + is(cols.length, 3, "Table has col list of correct length"); + for (let i = 0; i < cols.length; i++) { + let currCol = cols[i]; + let currChildren = currCol.getAttributeValue("AXChildren"); + is(currChildren.length, 5, "Column has correct number of cells"); + let lastCell = currChildren[currChildren.length - 1]; + let cellChildren = lastCell.getAttributeValue("AXChildren"); + is(cellChildren.length, 1, "Cell has a single text child"); + is( + cellChildren[0].getAttributeValue("AXRole"), + "AXStaticText", + "Correct role for cell child" + ); + is( + cellChildren[0].getAttributeValue("AXValue"), + rowText[i], + "Correct text for cell" + ); + } + + reorder = waitForEvent(EVENT_REORDER, "firstrow"); + await SpecialPowers.spawn(browser, [], () => { + let td = content.document.createElement("td"); + td.textContent = "Ticker"; + content.document.getElementById("firstrow").appendChild(td); + }); + await reorder; + + cols = table.getAttributeValue("AXColumns"); + is(cols.length, 4, "Table has col list of correct length"); + is( + cols[cols.length - 1].getAttributeValue("AXChildren").length, + 1, + "Last column has single child" + ); + } +); + +addAccessibleTask( + ` + + + + + + + + + + + + + +
    Header 1Header 2
    onetwothree
    fourfive
    `, + (browser, accDoc) => { + let table = getNativeInterface(accDoc, "table"); + + let getCellAt = (col, row) => + table.getParameterizedAttributeValue("AXCellForColumnAndRow", [col, row]); + + function testCell(cell, expectedId, expectedColRange, expectedRowRange) { + is( + cell.getAttributeValue("AXDOMIdentifier"), + expectedId, + "Correct DOM Identifier" + ); + Assert.deepEqual( + cell.getAttributeValue("AXColumnIndexRange"), + expectedColRange, + "Correct column range" + ); + Assert.deepEqual( + cell.getAttributeValue("AXRowIndexRange"), + expectedRowRange, + "Correct row range" + ); + } + + testCell(getCellAt(0, 0), "header1", [0, 2], [0, 1]); + testCell(getCellAt(1, 0), "header1", [0, 2], [0, 1]); + testCell(getCellAt(2, 0), "header2", [2, 1], [0, 1]); + + testCell(getCellAt(0, 1), "cell1", [0, 1], [1, 1]); + testCell(getCellAt(1, 1), "cell2", [1, 1], [1, 2]); + testCell(getCellAt(2, 1), "cell3", [2, 1], [1, 1]); + + testCell(getCellAt(0, 2), "cell4", [0, 1], [2, 1]); + testCell(getCellAt(1, 2), "cell2", [1, 1], [1, 2]); + testCell(getCellAt(2, 2), "cell5", [2, 1], [2, 1]); + + let colHeaders = table.getAttributeValue("AXColumnHeaderUIElements"); + Assert.deepEqual( + colHeaders.map(c => c.getAttributeValue("AXDOMIdentifier")), + ["header1", "header1", "header2"], + "Correct column headers" + ); + } +); + +addAccessibleTask( + ` + + + +
    Foo
    `, + (browser, accDoc) => { + // Make sure we guess this table to be a layout table. + testAttrs( + findAccessibleChildByID(accDoc, "table"), + { "layout-guess": "true" }, + true + ); + + let table = getNativeInterface(accDoc, "table"); + is( + table.getAttributeValue("AXRole"), + "AXGroup", + "Correct role (AXGroup) for layout table" + ); + + let children = table.getAttributeValue("AXChildren"); + is( + children.length, + 1, + "Layout table has single child (no additional columns)" + ); + } +); + +addAccessibleTask( + `
    + +
    +
    Cell 1
    +
    Cell 2
    +
    +
    + +
    + +
    Cell 3
    +
    Cell 4
    +
    +
    +
    +
    `, + async (browser, accDoc) => { + let table = getNativeInterface(accDoc, "table"); + testTableConsistency(table, 2, 2); + } +); diff --git a/accessible/tests/browser/mac/browser_text_basics.js b/accessible/tests/browser/mac/browser_text_basics.js new file mode 100644 index 0000000000..de66ebf639 --- /dev/null +++ b/accessible/tests/browser/mac/browser_text_basics.js @@ -0,0 +1,282 @@ +/* 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/. */ + +"use strict"; + +function testRangeAtMarker(macDoc, marker, attribute, expected, msg) { + let range = macDoc.getParameterizedAttributeValue(attribute, marker); + is(stringForRange(macDoc, range), expected, msg); +} + +function testUIElement( + macDoc, + marker, + msg, + expectedRole, + expectedValue, + expectedRange +) { + let elem = macDoc.getParameterizedAttributeValue( + "AXUIElementForTextMarker", + marker + ); + is( + elem.getAttributeValue("AXRole"), + expectedRole, + `${msg}: element role matches` + ); + is(elem.getAttributeValue("AXValue"), expectedValue, `${msg}: element value`); + let elemRange = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUIElement", + elem + ); + is( + stringForRange(macDoc, elemRange), + expectedRange, + `${msg}: element range matches element value` + ); +} + +function testStyleRun(macDoc, marker, msg, expectedStyleRun) { + testRangeAtMarker( + macDoc, + marker, + "AXStyleTextMarkerRangeForTextMarker", + expectedStyleRun, + `${msg}: style run matches` + ); +} + +function testParagraph(macDoc, marker, msg, expectedParagraph) { + testRangeAtMarker( + macDoc, + marker, + "AXParagraphTextMarkerRangeForTextMarker", + expectedParagraph, + `${msg}: paragraph matches` + ); +} + +function testWords(macDoc, marker, msg, expectedLeft, expectedRight) { + testRangeAtMarker( + macDoc, + marker, + "AXLeftWordTextMarkerRangeForTextMarker", + expectedLeft, + `${msg}: left word matches` + ); + + testRangeAtMarker( + macDoc, + marker, + "AXRightWordTextMarkerRangeForTextMarker", + expectedRight, + `${msg}: right word matches` + ); +} + +function testLines( + macDoc, + marker, + msg, + expectedLine, + expectedLeft, + expectedRight +) { + testRangeAtMarker( + macDoc, + marker, + "AXLineTextMarkerRangeForTextMarker", + expectedLine, + `${msg}: line matches` + ); + + testRangeAtMarker( + macDoc, + marker, + "AXLeftLineTextMarkerRangeForTextMarker", + expectedLeft, + `${msg}: left line matches` + ); + + testRangeAtMarker( + macDoc, + marker, + "AXRightLineTextMarkerRangeForTextMarker", + expectedRight, + `${msg}: right line matches` + ); +} + +// Tests consistency in text markers between: +// 1. "Linked list" forward navagation +// 2. Getting markers by index +// 3. "Linked list" reverse navagation +// For each iteration method check that the returned index is consistent +function testMarkerIntegrity(accDoc, expectedMarkerValues) { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let count = 0; + + // Iterate forward with "AXNextTextMarkerForTextMarker" + let marker = macDoc.getAttributeValue("AXStartTextMarker"); + while (marker) { + let index = macDoc.getParameterizedAttributeValue( + "AXIndexForTextMarker", + marker + ); + is( + index, + count, + `Correct index in "AXNextTextMarkerForTextMarker": ${count}` + ); + + testWords( + macDoc, + marker, + `At index ${count}`, + ...expectedMarkerValues[count].words + ); + testLines( + macDoc, + marker, + `At index ${count}`, + ...expectedMarkerValues[count].lines + ); + testUIElement( + macDoc, + marker, + `At index ${count}`, + ...expectedMarkerValues[count].element + ); + testParagraph( + macDoc, + marker, + `At index ${count}`, + expectedMarkerValues[count].paragraph + ); + testStyleRun( + macDoc, + marker, + `At index ${count}`, + expectedMarkerValues[count].style + ); + + let prevMarker = marker; + marker = macDoc.getParameterizedAttributeValue( + "AXNextTextMarkerForTextMarker", + marker + ); + + if (marker) { + let range = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUnorderedTextMarkers", + [prevMarker, marker] + ); + is( + macDoc.getParameterizedAttributeValue( + "AXLengthForTextMarkerRange", + range + ), + 1, + "marker moved one character" + ); + } + + count++; + } + + // Use "AXTextMarkerForIndex" to retrieve all text markers + for (let i = 0; i < count; i++) { + marker = macDoc.getParameterizedAttributeValue("AXTextMarkerForIndex", i); + let index = macDoc.getParameterizedAttributeValue( + "AXIndexForTextMarker", + marker + ); + is(index, i, `Correct index in "AXTextMarkerForIndex": ${i}`); + } + + ok( + !macDoc.getParameterizedAttributeValue( + "AXNextTextMarkerForTextMarker", + marker + ), + "Iterated through all markers" + ); + + // Iterate backward with "AXPreviousTextMarkerForTextMarker" + marker = macDoc.getAttributeValue("AXEndTextMarker"); + while (marker) { + count--; + let index = macDoc.getParameterizedAttributeValue( + "AXIndexForTextMarker", + marker + ); + is( + index, + count, + `Correct index in "AXPreviousTextMarkerForTextMarker": ${count}` + ); + marker = macDoc.getParameterizedAttributeValue( + "AXPreviousTextMarkerForTextMarker", + marker + ); + } + + is(count, 0, "Iterated backward through all text markers"); +} + +addAccessibleTask("mac/doc_textmarker_test.html", async (browser, accDoc) => { + const expectedMarkerValues = await SpecialPowers.spawn( + browser, + [], + async () => { + return content.wrappedJSObject.EXPECTED; + } + ); + + testMarkerIntegrity(accDoc, expectedMarkerValues); +}); + +// Test text marker lesser-than operator +addAccessibleTask( + `

    hello goodbye world

    `, + async (browser, accDoc) => { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let start = macDoc.getParameterizedAttributeValue( + "AXTextMarkerForIndex", + 1 + ); + let end = macDoc.getParameterizedAttributeValue("AXTextMarkerForIndex", 10); + + let range = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUnorderedTextMarkers", + [end, start] + ); + is(stringForRange(macDoc, range), "ello good"); + } +); + +addAccessibleTask( + `goodbye`, + async (browser, accDoc) => { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let input = getNativeInterface(accDoc, "input"); + + let range = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUIElement", + input + ); + + is(stringForRange(macDoc, range), "", "string value is correct"); + } +); diff --git a/accessible/tests/browser/mac/browser_text_input.js b/accessible/tests/browser/mac/browser_text_input.js new file mode 100644 index 0000000000..b5aa9511a1 --- /dev/null +++ b/accessible/tests/browser/mac/browser_text_input.js @@ -0,0 +1,412 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +function testValueChangedEventData( + macIface, + data, + expectedId, + expectedChangeValue, + expectedEditType, + expectedWordAtLeft +) { + is( + data.AXTextChangeElement.getAttributeValue("AXDOMIdentifier"), + expectedId, + "Correct AXTextChangeElement" + ); + is( + data.AXTextStateChangeType, + AXTextStateChangeTypeEdit, + "Correct AXTextStateChangeType" + ); + + let changeValues = data.AXTextChangeValues; + is(changeValues.length, 1, "One element in AXTextChangeValues"); + is( + changeValues[0].AXTextChangeValue, + expectedChangeValue, + "Correct AXTextChangeValue" + ); + is( + changeValues[0].AXTextEditType, + expectedEditType, + "Correct AXTextEditType" + ); + + let textMarker = changeValues[0].AXTextChangeValueStartMarker; + ok(textMarker, "There is a AXTextChangeValueStartMarker"); + let range = macIface.getParameterizedAttributeValue( + "AXLeftWordTextMarkerRangeForTextMarker", + textMarker + ); + let str = macIface.getParameterizedAttributeValue( + "AXStringForTextMarkerRange", + range, + "correct word before caret" + ); + is(str, expectedWordAtLeft); +} + +// Return true if the first given object a subset of the second +function isSubset(subset, superset) { + if (typeof subset != "object" || typeof superset != "object") { + return superset == subset; + } + + for (let [prop, val] of Object.entries(subset)) { + if (!isSubset(val, superset[prop])) { + return false; + } + } + + return true; +} + +function matchWebArea(expectedId, expectedInfo) { + return (iface, data) => { + if (!data) { + return false; + } + + let textChangeElemID = data.AXTextChangeElement.getAttributeValue( + "AXDOMIdentifier" + ); + + return ( + iface.getAttributeValue("AXRole") == "AXWebArea" && + textChangeElemID == expectedId && + isSubset(expectedInfo, data) + ); + }; +} + +function matchInput(expectedId, expectedInfo) { + return (iface, data) => { + if (!data) { + return false; + } + + return ( + iface.getAttributeValue("AXDOMIdentifier") == expectedId && + isSubset(expectedInfo, data) + ); + }; +} + +async function synthKeyAndTestSelectionChanged( + synthKey, + synthEvent, + expectedId, + expectedSelectionString, + expectedSelectionInfo +) { + let selectionChangedEvents = Promise.all([ + waitForMacEventWithInfo( + "AXSelectedTextChanged", + matchWebArea(expectedId, expectedSelectionInfo) + ), + waitForMacEventWithInfo( + "AXSelectedTextChanged", + matchInput(expectedId, expectedSelectionInfo) + ), + ]); + + EventUtils.synthesizeKey(synthKey, synthEvent); + let [webareaEvent, inputEvent] = await selectionChangedEvents; + is( + inputEvent.data.AXTextChangeElement.getAttributeValue("AXDOMIdentifier"), + expectedId, + "Correct AXTextChangeElement" + ); + + let rangeString = inputEvent.macIface.getParameterizedAttributeValue( + "AXStringForTextMarkerRange", + inputEvent.data.AXSelectedTextMarkerRange + ); + is( + rangeString, + expectedSelectionString, + `selection has correct value (${expectedSelectionString})` + ); + + is( + webareaEvent.macIface.getAttributeValue("AXDOMIdentifier"), + "body", + "Input event target is top-level WebArea" + ); + rangeString = webareaEvent.macIface.getParameterizedAttributeValue( + "AXStringForTextMarkerRange", + inputEvent.data.AXSelectedTextMarkerRange + ); + is( + rangeString, + expectedSelectionString, + `selection has correct value (${expectedSelectionString}) via top document` + ); +} + +async function synthKeyAndTestValueChanged( + synthKey, + synthEvent, + expectedId, + expectedTextSelectionId, + expectedChangeValue, + expectedEditType, + expectedWordAtLeft +) { + let valueChangedEvents = Promise.all([ + waitForMacEvent( + "AXSelectedTextChanged", + matchWebArea(expectedTextSelectionId, { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + }) + ), + waitForMacEvent( + "AXSelectedTextChanged", + matchInput(expectedTextSelectionId, { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + }) + ), + waitForMacEventWithInfo( + "AXValueChanged", + matchWebArea(expectedId, { + AXTextStateChangeType: AXTextStateChangeTypeEdit, + AXTextChangeValues: [ + { + AXTextChangeValue: expectedChangeValue, + AXTextEditType: expectedEditType, + }, + ], + }) + ), + waitForMacEventWithInfo( + "AXValueChanged", + matchInput(expectedId, { + AXTextStateChangeType: AXTextStateChangeTypeEdit, + AXTextChangeValues: [ + { + AXTextChangeValue: expectedChangeValue, + AXTextEditType: expectedEditType, + }, + ], + }) + ), + ]); + + EventUtils.synthesizeKey(synthKey, synthEvent); + let [, , webareaEvent, inputEvent] = await valueChangedEvents; + + testValueChangedEventData( + webareaEvent.macIface, + webareaEvent.data, + expectedId, + expectedChangeValue, + expectedEditType, + expectedWordAtLeft + ); + testValueChangedEventData( + inputEvent.macIface, + inputEvent.data, + expectedId, + expectedChangeValue, + expectedEditType, + expectedWordAtLeft + ); +} + +async function focusIntoInputAndType(accDoc, inputId, innerContainerId) { + let selectionId = innerContainerId ? innerContainerId : inputId; + let input = getNativeInterface(accDoc, inputId); + ok(!input.getAttributeValue("AXFocused"), "input is not focused"); + ok(input.isAttributeSettable("AXFocused"), "input is focusable"); + let events = Promise.all([ + waitForMacEvent( + "AXFocusedUIElementChanged", + iface => iface.getAttributeValue("AXDOMIdentifier") == inputId + ), + waitForMacEventWithInfo( + "AXSelectedTextChanged", + matchWebArea(selectionId, { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + }) + ), + waitForMacEventWithInfo( + "AXSelectedTextChanged", + matchInput(selectionId, { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + }) + ), + ]); + input.setAttributeValue("AXFocused", true); + await events; + + async function testTextInput( + synthKey, + expectedChangeValue, + expectedWordAtLeft + ) { + await synthKeyAndTestValueChanged( + synthKey, + null, + inputId, + selectionId, + expectedChangeValue, + AXTextEditTypeTyping, + expectedWordAtLeft + ); + } + + await testTextInput("h", "h", "h"); + await testTextInput("e", "e", "he"); + await testTextInput("l", "l", "hel"); + await testTextInput("l", "l", "hell"); + await testTextInput("o", "o", "hello"); + await testTextInput(" ", " ", "hello"); + // You would expect this to be useless but this is what VO + // consumes. I guess it concats the inserted text data to the + // word to the left of the marker. + await testTextInput("w", "w", " "); + await testTextInput("o", "o", "wo"); + await testTextInput("r", "r", "wor"); + await testTextInput("l", "l", "worl"); + await testTextInput("d", "d", "world"); + + async function testTextDelete(expectedChangeValue, expectedWordAtLeft) { + await synthKeyAndTestValueChanged( + "KEY_Backspace", + null, + inputId, + selectionId, + expectedChangeValue, + AXTextEditTypeDelete, + expectedWordAtLeft + ); + } + + await testTextDelete("d", "worl"); + await testTextDelete("l", "wor"); + + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + null, + selectionId, + "", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionMove, + AXTextSelectionDirection: AXTextSelectionDirectionPrevious, + AXTextSelectionGranularity: AXTextSelectionGranularityCharacter, + } + ); + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + { shiftKey: true }, + selectionId, + "o", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend, + AXTextSelectionDirection: AXTextSelectionDirectionPrevious, + AXTextSelectionGranularity: AXTextSelectionGranularityCharacter, + } + ); + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + { shiftKey: true }, + selectionId, + "wo", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend, + AXTextSelectionDirection: AXTextSelectionDirectionPrevious, + AXTextSelectionGranularity: AXTextSelectionGranularityCharacter, + } + ); + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + null, + selectionId, + "", + { AXTextStateChangeType: AXTextStateChangeTypeSelectionMove } + ); + await synthKeyAndTestSelectionChanged( + "KEY_Home", + { shiftKey: true }, + selectionId, + "hello ", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend, + AXTextSelectionDirection: AXTextSelectionDirectionPrevious, + AXTextSelectionGranularity: AXTextSelectionGranularityWord, + } + ); + await synthKeyAndTestSelectionChanged( + "KEY_ArrowLeft", + null, + selectionId, + "", + { AXTextStateChangeType: AXTextStateChangeTypeSelectionMove } + ); + await synthKeyAndTestSelectionChanged( + "KEY_ArrowRight", + { shiftKey: true, altKey: true }, + selectionId, + "hello", + { + AXTextStateChangeType: AXTextStateChangeTypeSelectionExtend, + AXTextSelectionDirection: AXTextSelectionDirectionNext, + AXTextSelectionGranularity: AXTextSelectionGranularityWord, + } + ); +} + +// Test text input +addAccessibleTask( + `link `, + async (browser, accDoc) => { + await focusIntoInputAndType(accDoc, "input"); + } +); + +// Test content editable +addAccessibleTask( + `

    `, + async (browser, accDoc) => { + const inner = getNativeInterface(accDoc, "inner"); + const editableAncestor = inner.getAttributeValue("AXEditableAncestor"); + is( + editableAncestor.getAttributeValue("AXDOMIdentifier"), + "input", + "Editable ancestor is input" + ); + await focusIntoInputAndType(accDoc, "input"); + } +); + +// Test text input in iframe +addAccessibleTask( + `link `, + async (browser, accDoc) => { + await focusIntoInputAndType(accDoc, "input"); + }, + { iframe: true } +); + +// Test input that gets role::EDITCOMBOBOX +addAccessibleTask(``, async (browser, accDoc) => { + const box = getNativeInterface(accDoc, "box"); + const editableAncestor = box.getAttributeValue("AXEditableAncestor"); + is( + editableAncestor.getAttributeValue("AXDOMIdentifier"), + "box", + "Editable ancestor is box itself" + ); + await focusIntoInputAndType(accDoc, "box"); +}); diff --git a/accessible/tests/browser/mac/browser_text_leaf.js b/accessible/tests/browser/mac/browser_text_leaf.js new file mode 100644 index 0000000000..c7c7a5c319 --- /dev/null +++ b/accessible/tests/browser/mac/browser_text_leaf.js @@ -0,0 +1,75 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +/** + * Test accessibles aren't created for linebreaks. + */ +addAccessibleTask(`hello
    world`, async (browser, accDoc) => { + let doc = accDoc.nativeInterface.QueryInterface(Ci.nsIAccessibleMacInterface); + let docChildren = doc.getAttributeValue("AXChildren"); + is(docChildren.length, 1, "The document contains a root group"); + + let rootGroup = docChildren[0]; + let children = rootGroup.getAttributeValue("AXChildren"); + is(docChildren.length, 1, "The root group contains 2 children"); + + // verify first child is correct + is( + children[0].getAttributeValue("AXRole"), + "AXStaticText", + "First child is a text node" + ); + is( + children[0].getAttributeValue("AXValue"), + "hello", + "First child is hello text" + ); + + // verify second child is correct + is( + children[1].getAttributeValue("AXRole"), + "AXStaticText", + "Second child is a text node" + ); + + is( + children[1].getAttributeValue("AXValue"), + "world ", + "Second child is world text" + ); + // we have a trailing space here due to bug 1577028 +}); + +addAccessibleTask( + `

    hello, this is a test

    `, + async (browser, accDoc) => { + let p = getNativeInterface(accDoc, "p"); + let textLeaf = p.getAttributeValue("AXChildren")[0]; + ok(textLeaf, "paragraph has a text leaf"); + + let str = textLeaf.getParameterizedAttributeValue( + "AXStringForRange", + NSRange(3, 6) + ); + + is(str, "lo, this ", "AXStringForRange matches."); + + let smallBounds = textLeaf.getParameterizedAttributeValue( + "AXBoundsForRange", + NSRange(3, 6) + ); + + let largeBounds = textLeaf.getParameterizedAttributeValue( + "AXBoundsForRange", + NSRange(3, 8) + ); + + ok(smallBounds.size[0] < largeBounds.size[0], "longer range is wider"); + } +); diff --git a/accessible/tests/browser/mac/browser_text_selection.js b/accessible/tests/browser/mac/browser_text_selection.js new file mode 100644 index 0000000000..a914adba8e --- /dev/null +++ b/accessible/tests/browser/mac/browser_text_selection.js @@ -0,0 +1,187 @@ +/* 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/. */ + +"use strict"; + +/** + * Test simple text selection + */ +addAccessibleTask(`

    Hello World

    `, async (browser, accDoc) => { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let startMarker = macDoc.getAttributeValue("AXStartTextMarker"); + let endMarker = macDoc.getAttributeValue("AXEndTextMarker"); + let range = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUnorderedTextMarkers", + [startMarker, endMarker] + ); + is(stringForRange(macDoc, range), "Hello World"); + + let evt = waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { + return ( + !info.AXTextStateSync && + info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend && + elem.getAttributeValue("AXRole") == "AXWebArea" + ); + }); + await SpecialPowers.spawn(browser, [], () => { + let p = content.document.getElementById("p"); + let r = new content.Range(); + r.setStart(p.firstChild, 1); + r.setEnd(p.firstChild, 8); + + let s = content.getSelection(); + s.addRange(r); + }); + await evt; + + range = macDoc.getAttributeValue("AXSelectedTextMarkerRange"); + is(stringForRange(macDoc, range), "ello Wo"); + + let firstWordRange = macDoc.getParameterizedAttributeValue( + "AXRightWordTextMarkerRangeForTextMarker", + startMarker + ); + is(stringForRange(macDoc, firstWordRange), "Hello"); + + evt = waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { + return ( + !info.AXTextStateSync && + info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend && + elem.getAttributeValue("AXRole") == "AXWebArea" + ); + }); + macDoc.setAttributeValue("AXSelectedTextMarkerRange", firstWordRange); + await evt; + range = macDoc.getAttributeValue("AXSelectedTextMarkerRange"); + is(stringForRange(macDoc, range), "Hello"); + + // Collapse selection + evt = waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { + return ( + info.AXTextStateSync && + info.AXTextStateChangeType == AXTextStateChangeTypeSelectionMove && + elem.getAttributeValue("AXRole") == "AXWebArea" + ); + }); + await SpecialPowers.spawn(browser, [], () => { + let s = content.getSelection(); + s.collapseToEnd(); + }); + await evt; +}); + +/** + * Test text selection events caused by focus change + */ +addAccessibleTask( + `

    + Hello World, + I love +

    `, + async (browser, accDoc) => { + // Set up an AXSelectedTextChanged listener here. It will get resolved + // on the first non-root event it encounters, so if we test its data at the end + // of this test it will show us the first text-selectable object that was focused, + // which is "link". + let selTextChanged = waitForMacEvent( + "AXSelectedTextChanged", + e => e.getAttributeValue("AXDOMIdentifier") != "body" + ); + + let focusChanged = waitForMacEvent("AXFocusedUIElementChanged"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("unselectable_link").focus(); + }); + let focusChangedTarget = await focusChanged; + is( + focusChangedTarget.getAttributeValue("AXDOMIdentifier"), + "unselectable_link", + "Correct event target" + ); + + focusChanged = waitForMacEvent("AXFocusedUIElementChanged"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("button").focus(); + }); + focusChangedTarget = await focusChanged; + is( + focusChangedTarget.getAttributeValue("AXDOMIdentifier"), + "button", + "Correct event target" + ); + + focusChanged = waitForMacEvent("AXFocusedUIElementChanged"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("link").focus(); + }); + focusChangedTarget = await focusChanged; + is( + focusChangedTarget.getAttributeValue("AXDOMIdentifier"), + "link", + "Correct event target" + ); + + let selTextChangedTarget = await selTextChanged; + is( + selTextChangedTarget.getAttributeValue("AXDOMIdentifier"), + "link", + "Correct event target" + ); + } +); + +/** + * Test text selection with focus change + */ +addAccessibleTask( + `

    Hello

    `, + async (browser, accDoc) => { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let evt = waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => { + return ( + !info.AXTextStateSync && + info.AXTextStateChangeType == AXTextStateChangeTypeSelectionExtend && + elem.getAttributeValue("AXRole") == "AXWebArea" + ); + }); + await SpecialPowers.spawn(browser, [], () => { + let p = content.document.getElementById("p"); + let r = new content.Range(); + r.setStart(p.firstChild, 1); + r.setEnd(p.firstChild, 3); + + let s = content.getSelection(); + s.addRange(r); + }); + await evt; + + let range = macDoc.getAttributeValue("AXSelectedTextMarkerRange"); + is(stringForRange(macDoc, range), "el"); + + let events = Promise.all([ + waitForMacEvent("AXFocusedUIElementChanged"), + waitForMacEventWithInfo("AXSelectedTextChanged"), + ]); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("input").focus(); + }); + let [, { data }] = await events; + ok( + data.AXTextSelectionChangedFocus, + "have AXTextSelectionChangedFocus in event info" + ); + ok(!data.AXTextStateSync, "no AXTextStateSync in editables"); + is( + data.AXTextSelectionDirection, + AXTextSelectionDirectionDiscontiguous, + "discontigous direction" + ); + } +); diff --git a/accessible/tests/browser/mac/browser_toggle_radio_check.js b/accessible/tests/browser/mac/browser_toggle_radio_check.js new file mode 100644 index 0000000000..17c292fd87 --- /dev/null +++ b/accessible/tests/browser/mac/browser_toggle_radio_check.js @@ -0,0 +1,140 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR } +); + +/** + * Test input[type=checkbox] + */ +addAccessibleTask( + ``, + async (browser, accDoc) => { + let checkbox = getNativeInterface(accDoc, "vehicle"); + is(checkbox.getAttributeValue("AXValue"), 0, "Correct initial value"); + + let actions = checkbox.actionNames; + ok(actions.includes("AXPress"), "Has press action"); + + let evt = waitForMacEvent("AXValueChanged", "vehicle"); + checkbox.performAction("AXPress"); + await evt; + is(checkbox.getAttributeValue("AXValue"), 1, "Correct checked value"); + + evt = waitForMacEvent("AXValueChanged", "vehicle"); + checkbox.performAction("AXPress"); + await evt; + is(checkbox.getAttributeValue("AXValue"), 0, "Correct checked value"); + } +); + +/** + * Test aria-pressed toggle buttons + */ +addAccessibleTask( + ``, + async (browser, accDoc) => { + // Set up a callback to change the toggle value + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("toggle").onclick = e => { + let curVal = e.target.getAttribute("aria-pressed"); + let nextVal = curVal == "false" ? "true" : "false"; + e.target.setAttribute("aria-pressed", nextVal); + }; + }); + + let toggle = getNativeInterface(accDoc, "toggle"); + is(toggle.getAttributeValue("AXValue"), 0, "Correct initial value"); + + let actions = toggle.actionNames; + ok(actions.includes("AXPress"), "Has press action"); + + let evt = waitForMacEvent("AXValueChanged", "toggle"); + toggle.performAction("AXPress"); + await evt; + is(toggle.getAttributeValue("AXValue"), 1, "Correct checked value"); + + evt = waitForMacEvent("AXValueChanged", "toggle"); + toggle.performAction("AXPress"); + await evt; + is(toggle.getAttributeValue("AXValue"), 0, "Correct checked value"); + } +); + +/** + * Test aria-checked with tri state + */ +addAccessibleTask( + ``, + async (browser, accDoc) => { + // Set up a callback to change the toggle value + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("checkbox").onclick = e => { + const states = ["false", "true", "mixed"]; + let currState = e.target.getAttribute("aria-checked"); + let nextState = states[(states.indexOf(currState) + 1) % states.length]; + e.target.setAttribute("aria-checked", nextState); + }; + }); + + let checkbox = getNativeInterface(accDoc, "checkbox"); + is(checkbox.getAttributeValue("AXValue"), 0, "Correct initial value"); + + let actions = checkbox.actionNames; + ok(actions.includes("AXPress"), "Has press action"); + + let evt = waitForMacEvent("AXValueChanged", "checkbox"); + checkbox.performAction("AXPress"); + await evt; + is(checkbox.getAttributeValue("AXValue"), 1, "Correct checked value"); + + evt = waitForMacEvent("AXValueChanged", "checkbox"); + checkbox.performAction("AXPress"); + await evt; + is(checkbox.getAttributeValue("AXValue"), 2, "Correct checked value"); + } +); + +/** + * Test input[type=radio] + */ +addAccessibleTask( + ` + + + `, + async (browser, accDoc) => { + let huey = getNativeInterface(accDoc, "huey"); + is(huey.getAttributeValue("AXValue"), 1, "Correct initial value for huey"); + + let dewey = getNativeInterface(accDoc, "dewey"); + is( + dewey.getAttributeValue("AXValue"), + 0, + "Correct initial value for dewey" + ); + + let actions = dewey.actionNames; + ok(actions.includes("AXPress"), "Has press action"); + + let evt = Promise.all([ + waitForMacEvent("AXValueChanged", "huey"), + waitForMacEvent("AXValueChanged", "dewey"), + ]); + dewey.performAction("AXPress"); + await evt; + is( + dewey.getAttributeValue("AXValue"), + 1, + "Correct checked value for dewey" + ); + is(huey.getAttributeValue("AXValue"), 0, "Correct checked value for huey"); + } +); diff --git a/accessible/tests/browser/mac/browser_webarea.js b/accessible/tests/browser/mac/browser_webarea.js new file mode 100644 index 0000000000..c1e68dac1b --- /dev/null +++ b/accessible/tests/browser/mac/browser_webarea.js @@ -0,0 +1,76 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +// Test web area role and AXLoadComplete event +addAccessibleTask(``, async (browser, accDoc) => { + let evt = waitForMacEvent("AXLoadComplete"); + await SpecialPowers.spawn(browser, [], () => { + content.location = "data:text/html,webarea test"; + }); + let doc = await evt; + + is( + doc.getAttributeValue("AXRole"), + "AXWebArea", + "document has AXWebArea role" + ); + is(doc.getAttributeValue("AXValue"), "", "document has no AXValue"); + is(doc.getAttributeValue("AXTitle"), null, "document has no AXTitle"); + is( + doc.getAttributeValue("AXDescription"), + "webarea test", + "test has correct label" + ); + + is(doc.getAttributeValue("AXLoaded"), 1, "document has finished loading"); +}); + +// Test iframe web area role and AXLayoutComplete event +addAccessibleTask(`webarea test`, async (browser, accDoc) => { + // If the iframe loads before the top level document finishes loading, we'll + // get both an AXLayoutComplete event for the iframe and an AXLoadComplete + // event for the document. Otherwise, if the iframe loads after the + // document, we'll get one AXLoadComplete event. + let eventPromise = Promise.race([ + waitForMacEvent("AXLayoutComplete"), + waitForMacEvent("AXLoadComplete"), + ]); + await SpecialPowers.spawn(browser, [], () => { + const iframe = content.document.createElement("iframe"); + iframe.src = "data:text/html,hello world"; + content.document.body.appendChild(iframe); + }); + let doc = await eventPromise; + + if (doc.getAttributeValue("AXTitle")) { + // iframe should have no title, so if we get a title here + // we've got the main document and need to get the iframe from + // the main doc + doc = doc.getAttributeValue("AXChildren")[0]; + } + + is( + doc.getAttributeValue("AXRole"), + "AXWebArea", + "iframe document has AXWebArea role" + ); + is(doc.getAttributeValue("AXValue"), "", "iframe document has no AXValue"); + is(doc.getAttributeValue("AXTitle"), null, "iframe document has no AXTitle"); + is( + doc.getAttributeValue("AXDescription"), + "data:text/html,hello world", + "test has correct label" + ); + + is( + doc.getAttributeValue("AXLoaded"), + 1, + "iframe document has finished loading" + ); +}); diff --git a/accessible/tests/browser/mac/doc_aria_tabs.html b/accessible/tests/browser/mac/doc_aria_tabs.html new file mode 100644 index 0000000000..9de6f8b81c --- /dev/null +++ b/accessible/tests/browser/mac/doc_aria_tabs.html @@ -0,0 +1,95 @@ + + + + + + + + ARIA: tab role - Example - code sample + + + +
    +
    + + + +
    +
    +

    Content for the first panel

    +
    + + +
    + diff --git a/accessible/tests/browser/mac/doc_menulist.xhtml b/accessible/tests/browser/mac/doc_menulist.xhtml new file mode 100644 index 0000000000..d6751bc8f4 --- /dev/null +++ b/accessible/tests/browser/mac/doc_menulist.xhtml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/accessible/tests/browser/mac/doc_rich_listbox.xhtml b/accessible/tests/browser/mac/doc_rich_listbox.xhtml new file mode 100644 index 0000000000..3acaf3bff8 --- /dev/null +++ b/accessible/tests/browser/mac/doc_rich_listbox.xhtml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/accessible/tests/browser/mac/doc_textmarker_test.html b/accessible/tests/browser/mac/doc_textmarker_test.html new file mode 100644 index 0000000000..8a73c95a35 --- /dev/null +++ b/accessible/tests/browser/mac/doc_textmarker_test.html @@ -0,0 +1,2424 @@ + + + + + + + +

    Bob Loblaw Lobs Law Bomb

    +

    I love all of my children equally

    +

    This is the best free scrapbooking class I have ever taken

    +
      +
    • Fried cheese with club sauce
    • +
    • Popcorn shrimp with club sauce
    • +
    • Chicken fingers with spicy club sauce
    • +
    +
    • Do not order the Skip's Scramble
    +

    These are my awards, Mother. From Army.

    +

    I , mom.

    + + + diff --git a/accessible/tests/browser/mac/doc_tree.xhtml b/accessible/tests/browser/mac/doc_tree.xhtml new file mode 100644 index 0000000000..d043fa8923 --- /dev/null +++ b/accessible/tests/browser/mac/doc_tree.xhtml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/accessible/tests/browser/mac/head.js b/accessible/tests/browser/mac/head.js new file mode 100644 index 0000000000..7f70bd4a4e --- /dev/null +++ b/accessible/tests/browser/mac/head.js @@ -0,0 +1,114 @@ +/* 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/. */ + +"use strict"; + +/* exported getNativeInterface, waitForMacEventWithInfo, waitForMacEvent, + NSRange, NSDictionary, stringForRange, AXTextStateChangeTypeEdit, + AXTextEditTypeDelete, AXTextEditTypeTyping, AXTextStateChangeTypeSelectionMove, + AXTextStateChangeTypeSelectionExtend, AXTextSelectionDirectionUnknown, + AXTextSelectionDirectionPrevious, AXTextSelectionDirectionNext, + AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, + AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord */ + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); + +// AXTextStateChangeType enum values +const AXTextStateChangeTypeEdit = 1; +const AXTextStateChangeTypeSelectionMove = 2; +const AXTextStateChangeTypeSelectionExtend = 3; + +// AXTextEditType enum values +const AXTextEditTypeDelete = 1; +const AXTextEditTypeTyping = 3; + +// AXTextSelectionDirection enum values +const AXTextSelectionDirectionUnknown = 0; +const AXTextSelectionDirectionPrevious = 3; +const AXTextSelectionDirectionNext = 4; +const AXTextSelectionDirectionDiscontiguous = 5; + +// AXTextSelectionGranularity enum values +const AXTextSelectionGranularityUnknown = 0; +const AXTextSelectionGranularityCharacter = 1; +const AXTextSelectionGranularityWord = 2; + +function getNativeInterface(accDoc, id) { + return findAccessibleChildByID(accDoc, id).nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); +} + +function waitForMacEventWithInfo(notificationType, filter) { + let filterFunc = (macIface, data) => { + if (!filter) { + return true; + } + + if (typeof filter == "function") { + return filter(macIface, data); + } + + return macIface.getAttributeValue("AXDOMIdentifier") == filter; + }; + + return new Promise(resolve => { + let eventObserver = { + observe(subject, topic, data) { + let macEvent = subject.QueryInterface(Ci.nsIAccessibleMacEvent); + if ( + data === notificationType && + filterFunc(macEvent.macIface, macEvent.data) + ) { + Services.obs.removeObserver(this, "accessible-mac-event"); + resolve(macEvent); + } + }, + }; + Services.obs.addObserver(eventObserver, "accessible-mac-event"); + }); +} + +function waitForMacEvent(notificationType, filter) { + return waitForMacEventWithInfo(notificationType, filter).then( + e => e.macIface + ); +} + +function NSRange(location, length) { + return { + valueType: "NSRange", + value: [location, length], + }; +} + +function NSDictionary(dict) { + return { + objectType: "NSDictionary", + object: dict, + }; +} + +function stringForRange(macDoc, range) { + if (!range) { + return ""; + } + + return macDoc.getParameterizedAttributeValue( + "AXStringForTextMarkerRange", + range + ); +} diff --git a/accessible/tests/browser/scroll/browser.ini b/accessible/tests/browser/scroll/browser.ini new file mode 100644 index 0000000000..1e24426535 --- /dev/null +++ b/accessible/tests/browser/scroll/browser.ini @@ -0,0 +1,9 @@ +[DEFAULT] +support-files = + head.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/browser/*.jsm + !/accessible/tests/mochitest/*.js + +[browser_test_zoom_text.js] +skip-if = e10s && os == 'win' # bug 1372296 diff --git a/accessible/tests/browser/scroll/browser_test_zoom_text.js b/accessible/tests/browser/scroll/browser_test_zoom_text.js new file mode 100644 index 0000000000..be58361c7b --- /dev/null +++ b/accessible/tests/browser/scroll/browser_test_zoom_text.js @@ -0,0 +1,150 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/layout.js */ +loadScripts({ name: "layout.js", dir: MOCHITESTS_DIR }); + +async function waitForContentPaint(browser) { + await SpecialPowers.spawn(browser, [], () => { + return new Promise(function(r) { + content.requestAnimationFrame(() => content.setTimeout(r)); + }); + }); +} + +async function runTests(browser, accDoc) { + await loadContentScripts(browser, "Layout.jsm"); + + let paragraph = findAccessibleChildByID(accDoc, "paragraph", [ + nsIAccessibleText, + ]); + let offset = 64; // beginning of 4th stanza + + let [x /* ,y*/] = getPos(paragraph); + let [docX, docY] = getPos(accDoc); + + paragraph.scrollSubstringToPoint( + offset, + offset, + COORDTYPE_SCREEN_RELATIVE, + docX, + docY + ); + + await waitForContentPaint(browser); + testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE); + + await SpecialPowers.spawn(browser, [], () => { + content.Layout.zoomDocument(content.document, 2.0); + }); + + paragraph = findAccessibleChildByID(accDoc, "paragraph2", [ + nsIAccessibleText, + ]); + offset = 52; // // beginning of 4th stanza + [x /* ,y*/] = getPos(paragraph); + paragraph.scrollSubstringToPoint( + offset, + offset, + COORDTYPE_SCREEN_RELATIVE, + docX, + docY + ); + + await waitForContentPaint(browser); + testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE); +} + +/** + * Test caching of accessible object states + */ +addAccessibleTask( + ` +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +










    +

    + Пошел котик на торжок
    + Купил котик пирожок
    + Пошел котик на улочку
    + Купил котик булочку
    +

    +










    +









    +









    +









    +









    +










    +

    + Самому ли съесть
    + Либо Сашеньке снесть
    + Я и сам укушу
    + Я и Сашеньке снесу
    +

    +










    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    +









    `, + runTests +); diff --git a/accessible/tests/browser/scroll/head.js b/accessible/tests/browser/scroll/head.js new file mode 100644 index 0000000000..672aa46171 --- /dev/null +++ b/accessible/tests/browser/scroll/head.js @@ -0,0 +1,19 @@ +/* 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/. */ + +"use strict"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); diff --git a/accessible/tests/browser/shared-head.js b/accessible/tests/browser/shared-head.js new file mode 100644 index 0000000000..b848e09932 --- /dev/null +++ b/accessible/tests/browser/shared-head.js @@ -0,0 +1,779 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../mochitest/common.js */ +/* import-globals-from ../mochitest/promisified-events.js */ + +/* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus, + invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName, + addAccessibleTask, findAccessibleChildByID, isDefunct, + CURRENT_CONTENT_DIR, loadScripts, loadContentScripts, snippetToURL, + Cc, Cu, arrayFromChildren, forceGC, contentSpawnMutation, + DEFAULT_IFRAME_ID, DEFAULT_IFRAME_DOC_BODY_ID, invokeContentTask, + matchContentDoc, currentContentDoc, getContentDPR, + waitForImageMap, getContentBoundsForDOMElm */ + +const CURRENT_FILE_DIR = "/browser/accessible/tests/browser/"; + +/** + * Current browser test directory path used to load subscripts. + */ +const CURRENT_DIR = `chrome://mochitests/content${CURRENT_FILE_DIR}`; +/** + * A11y mochitest directory where we find common files used in both browser and + * plain tests. + */ +const MOCHITESTS_DIR = + "chrome://mochitests/content/a11y/accessible/tests/mochitest/"; +/** + * A base URL for test files used in content. + */ +const CURRENT_CONTENT_DIR = `http://example.com${CURRENT_FILE_DIR}`; + +const LOADED_CONTENT_SCRIPTS = new Map(); + +const DEFAULT_CONTENT_DOC_BODY_ID = "body"; +const DEFAULT_IFRAME_ID = "default-iframe-id"; +const DEFAULT_IFRAME_DOC_BODY_ID = "default-iframe-body-id"; + +const HTML_MIME_TYPE = "text/html"; +const XHTML_MIME_TYPE = "application/xhtml+xml"; + +function loadHTMLFromFile(path) { + // Load the HTML to return in the response from file. + // Since it's relative to the cwd of the test runner, we start there and + // append to get to the actual path of the file. + const testHTMLFile = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + const dirs = path.split("/"); + for (let i = 0; i < dirs.length; i++) { + testHTMLFile.append(dirs[i]); + } + + const testHTMLFileStream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); + testHTMLFileStream.init(testHTMLFile, -1, 0, 0); + const testHTML = NetUtil.readInputStreamToString( + testHTMLFileStream, + testHTMLFileStream.available() + ); + + return testHTML; +} + +let gIsIframe = false; +let gIsRemoteIframe = false; + +function currentContentDoc() { + return gIsIframe ? DEFAULT_IFRAME_DOC_BODY_ID : DEFAULT_CONTENT_DOC_BODY_ID; +} + +/** + * Accessible event match criteria based on the id of the current document + * accessible in test. + * + * @param {nsIAccessibleEvent} event + * Accessible event to be tested for a match. + * + * @return {Boolean} + * True if accessible event's accessible object ID matches current + * document accessible ID. + */ +function matchContentDoc(event) { + return getAccessibleDOMNodeID(event.accessible) === currentContentDoc(); +} + +/** + * Used to dump debug information. + */ +let Logger = { + /** + * Set up this variable to dump log messages into console. + */ + dumpToConsole: false, + + /** + * Set up this variable to dump log messages into error console. + */ + dumpToAppConsole: false, + + /** + * Return true if dump is enabled. + */ + get enabled() { + return this.dumpToConsole || this.dumpToAppConsole; + }, + + /** + * Dump information into console if applicable. + */ + log(msg) { + if (this.enabled) { + this.logToConsole(msg); + this.logToAppConsole(msg); + } + }, + + /** + * Log message to console. + */ + logToConsole(msg) { + if (this.dumpToConsole) { + dump(`\n${msg}\n`); + } + }, + + /** + * Log message to error console. + */ + logToAppConsole(msg) { + if (this.dumpToAppConsole) { + Services.console.logStringMessage(`${msg}`); + } + }, +}; + +/** + * Asynchronously set or remove content element's attribute (in content process + * if e10s is enabled). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @param {String} attr attribute name + * @param {String?} value optional attribute value, if not present, remove + * attribute + * @return {Promise} promise indicating that attribute is set/removed + */ +function invokeSetAttribute(browser, id, attr, value) { + if (value) { + Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`); + } else { + Logger.log(`Removing ${attr} attribute from node with id: ${id}`); + } + + return invokeContentTask( + browser, + [id, attr, value], + (contentId, contentAttr, contentValue) => { + let elm = content.document.getElementById(contentId); + if (contentValue) { + elm.setAttribute(contentAttr, contentValue); + } else { + elm.removeAttribute(contentAttr); + } + } + ); +} + +/** + * Asynchronously set or remove content element's style (in content process if + * e10s is enabled, or in fission process if fission is enabled and a fission + * frame is present). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @param {String} aStyle style property name + * @param {String?} aValue optional style property value, if not present, + * remove style + * @return {Promise} promise indicating that style is set/removed + */ +function invokeSetStyle(browser, id, style, value) { + if (value) { + Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`); + } else { + Logger.log(`Removing ${style} style from node with id: ${id}`); + } + + return invokeContentTask( + browser, + [id, style, value], + (contentId, contentStyle, contentValue) => { + const elm = content.document.getElementById(contentId); + if (contentValue) { + elm.style[contentStyle] = contentValue; + } else { + delete elm.style[contentStyle]; + } + } + ); +} + +/** + * Asynchronously set focus on a content element (in content process if e10s is + * enabled, or in fission process if fission is enabled and a fission frame is + * present). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @return {Promise} promise indicating that focus is set + */ +function invokeFocus(browser, id) { + Logger.log(`Setting focus on a node with id: ${id}`); + + return invokeContentTask(browser, [id], contentId => { + const elm = content.document.getElementById(contentId); + if (elm.editor) { + elm.selectionStart = elm.selectionEnd = elm.value.length; + } + + elm.focus(); + }); +} + +/** + * Get DPR for a specific content window. + * @param browser + * Browser for which we want its content window's DPR reported. + * + * @return {Promise} + * Promise with the value that resolves to the devicePixelRatio of the + * content window of a given browser. + * + */ +function getContentDPR(browser) { + return invokeContentTask(browser, [], () => content.window.devicePixelRatio); +} + +/** + * Asynchronously perform a task in content (in content process if e10s is + * enabled, or in fission process if fission is enabled and a fission frame is + * present). + * @param {Object} browser current "tabbrowser" element + * @param {Array} args arguments for the content task + * @param {Function} task content task function + * + * @return {Promise} promise indicating that content task is complete + */ +function invokeContentTask(browser, args, task) { + return SpecialPowers.spawn( + browser, + [DEFAULT_IFRAME_ID, task.toString(), ...args], + (iframeId, contentTask, ...contentArgs) => { + // eslint-disable-next-line no-eval + const runnableTask = eval(` + (() => { + return (${contentTask}); + })();`); + const frame = content.document.getElementById(iframeId); + + return frame + ? SpecialPowers.spawn(frame, contentArgs, runnableTask) + : runnableTask.call(this, ...contentArgs); + } + ); +} + +/** + * Compare process ID's between the top level content process and possible + * remote/local iframe proccess. + * @param {Object} browser + * Top level browser object for a tab. + * @param {Boolean} isRemote + * Indicates if we expect the iframe content process to be remote or not. + */ +async function comparePIDs(browser, isRemote) { + function getProcessID() { + const { Services } = ChromeUtils.import( + "resource://gre/modules/Services.jsm" + ); + + return Services.appinfo.processID; + } + + const contentPID = await SpecialPowers.spawn(browser, [], getProcessID); + const iframePID = await invokeContentTask(browser, [], getProcessID); + is( + isRemote, + contentPID !== iframePID, + isRemote + ? "Remote IFRAME is in a different process." + : "IFRAME is in the same process." + ); +} + +/** + * Load a list of scripts into the test + * @param {Array} scripts a list of scripts to load + */ +function loadScripts(...scripts) { + for (let script of scripts) { + let path = + typeof script === "string" + ? `${CURRENT_DIR}${script}` + : `${script.dir}${script.name}`; + Services.scriptloader.loadSubScript(path, this); + } +} + +/** + * Load a list of scripts into target's content. + * @param {Object} target + * target for loading scripts into + * @param {Array} scripts + * a list of scripts to load into content + */ +async function loadContentScripts(target, ...scripts) { + for (let script of scripts) { + let contentScript; + if (typeof script === "string") { + // If script string includes a .jsm extention, assume it is a module path. + contentScript = `${CURRENT_DIR}${script}`; + } else { + // Script is a object that has { dir, name } format. + contentScript = `${script.dir}${script.name}`; + } + + let loadedScriptSet = LOADED_CONTENT_SCRIPTS.get(contentScript); + if (!loadedScriptSet) { + loadedScriptSet = new WeakSet(); + LOADED_CONTENT_SCRIPTS.set(contentScript, loadedScriptSet); + } else if (loadedScriptSet.has(target)) { + continue; + } + + await SpecialPowers.spawn(target, [contentScript], async _contentScript => { + ChromeUtils.import(_contentScript, content.window); + }); + loadedScriptSet.add(target); + } +} + +function attrsToString(attrs) { + return Object.entries(attrs) + .map(([attr, value]) => `${attr}=${JSON.stringify(value)}`) + .join(" "); +} + +function wrapWithIFrame(doc, options = {}) { + let src; + let { iframeAttrs = {}, iframeDocBodyAttrs = {} } = options; + iframeDocBodyAttrs = { + id: DEFAULT_IFRAME_DOC_BODY_ID, + ...iframeDocBodyAttrs, + }; + if (options.remoteIframe) { + const srcURL = new URL(`http://example.net/document-builder.sjs`); + if (doc.endsWith("html")) { + srcURL.searchParams.append("file", `${CURRENT_FILE_DIR}${doc}`); + } else { + srcURL.searchParams.append( + "html", + ` + + + Accessibility Fission Test + + ${doc} + ` + ); + } + src = srcURL.href; + } else { + const mimeType = doc.endsWith("xhtml") ? XHTML_MIME_TYPE : HTML_MIME_TYPE; + if (doc.endsWith("html")) { + doc = loadHTMLFromFile(`${CURRENT_FILE_DIR}${doc}`); + doc = doc.replace( + //, + `` + ); + } else { + doc = `${doc}`; + } + + src = `data:${mimeType};charset=utf-8,${encodeURIComponent(doc)}`; + } + + iframeAttrs = { + id: DEFAULT_IFRAME_ID, + src, + ...iframeAttrs, + }; + + return ` + + + + + +
    excuse
    me
    +
    excuse
    me
    +
    excuse
    + +
    excuse
    me
    +
    excuse
    me
    +
    excuse
    me
    +
    excuse
    me
    +
    tablist
    tab
    +
    excuse
    me
    + + + + + + +
    + excuse
    me
    +
    + + + + + + + + + + + + + +
    Draggable div
    + + + + + + +
    SS#Social Security Number
    + +
      +
    • item +
    + + +
    Fake beer
    + + + + + + + +
    + + diff --git a/accessible/tests/mochitest/attributes/test_obj_css.html b/accessible/tests/mochitest/attributes/test_obj_css.html new file mode 100644 index 0000000000..6c702ba5a6 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_css.html @@ -0,0 +1,225 @@ + + + CSS-like attributes tests + + + + + + + + + + + + + + Mozilla Bug 439566 + + + Mozilla Bug 460932 + + + Mozilla Bug 689540 + + + Mozilla Bug 714579 + + + Mozilla Bug 729831 + + +

    + +
    +  
    + + + + + + + + + + + + + + + + +

    text-align: left

    +

    text-align: right

    +

    text-align: center

    +

    text-align: justify

    +

    text-align: inherit

    + +

    text-indent: 0.5em

    +

    text-indent: 1ex

    +

    text-indent: 0.5in

    +

    text-indent: 2cm

    +

    text-indent: 10mm

    +

    text-indent: 30pt

    +

    text-indent: 2pc

    +

    text-indent: 5px

    +

    text-indent: 10%

    +

    text-indent: inherit

    + +

    margin: 0.5em

    +

    margin: 1ex

    +

    margin: 0.5in

    +

    margin: 2cm

    +

    margin: 10mm

    +

    margin: 30pt

    +

    margin: 2pc

    +

    margin: 5px

    +

    margin: 10%

    +

    margin: auto

    +

    margin: inherit

    + +

    margin-left: 11px

    +

    margin-right

    +

    margin-top: 31px

    +

    margin-bottom: 41px

    + + It's span +
    It's div
    +

    It's paragraph"

    + + + + + +
    td
    + +
      +
    • item +
    + + diff --git a/accessible/tests/mochitest/attributes/test_obj_css.xhtml b/accessible/tests/mochitest/attributes/test_obj_css.xhtml new file mode 100644 index 0000000000..df9afceba0 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_css.xhtml @@ -0,0 +1,57 @@ + + + + + + + + + + + + Mozilla Bug 714579 +
    + +

    + +
    +    
    + + + + + + + +
    +
    diff --git a/accessible/tests/mochitest/attributes/test_obj_group.html b/accessible/tests/mochitest/attributes/test_obj_group.html new file mode 100644 index 0000000000..6bfaaf7285 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_group.html @@ -0,0 +1,566 @@ + + + + Group attributes tests + + + + + + + + + + + + + + Mozilla Bug 468418 + + + Bug 844023 + + + Bug 864224 + + + Mozilla Bug 907682 + + +

    + +
    +  
    + + + + + + + +
    + + +
    + + + + +
      +
    • Oranges
    • +
    • Apples
    • +
    • Bananas
    • +
    + +
      +
    1. Oranges
    2. +
    3. Apples
    4. +
    5. Bananas +
        +
      • Oranges
      • +
      • Apples
      • +
      • Bananas
      • +
      +
    6. +
    + + + Oranges + Apples + Bananas + + + + Oranges + Apples + Bananas + + Oranges + Apples + Bananas + + + + +
    +
    Item 1 +
    +
    Item 1A
    +
    Item 1B
    +
    +
    +
    Item 2 +
    +
    Item 2A
    +
    Item 2B
    +
    +
    +
    + + + +
      + + + +
    + +
      + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    vegetables
    cucumber
    carrot
    mercedes
    BMW
    Audi
    people
    + +
      +
    • Item 1 +
        +
      • Item 1A
      • +
      • Item 1B
      • +
      +
    • +
    • Item 2 +
        +
      • Item 2A
      • +
      • Item 2B
      • +
      +
    • +
    + +
    +
    Item 1
    +
    +
  1. Item 1A
  2. +
  3. Item 1B
  4. +
    +
    Item 2
    +
    +
    Item 2A
    +
    Item 2B
    +
    +
    + + +
    Item 1
    Item 2
    + + + + + + + + + + +
    cell1cell2
    cell3cell4
    + +
    +
    +
    cell1
    +
    cell2
    +
    +
    +
    cell1
    +
    cell2
    +
    +
    +
    cell1
    +
    cell2
    +
    +
    + +
    +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    +
    ariaHeadingNoLevel
    +
    + +
      Password +
    • Xyzzy
    • +
    • Plughs
    • +
    • Shazaam
    • +
    • JoeSentMe
    • +
    + +
    + + +
    + +
    +
    +
    cell
    +
    +
    + +
    +
    +
    +
    cell content
    +
    +
    +
    +
    +
    cell content
    +
    +
    +
    + +
    +
    Apples
    +
    Oranges
    +
    +
    Bananas
    + + +
    + + +
    + + +
    + + + +
    + + diff --git a/accessible/tests/mochitest/attributes/test_obj_group.xhtml b/accessible/tests/mochitest/attributes/test_obj_group.xhtml new file mode 100644 index 0000000000..0eda4b6f2d --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_group.xhtml @@ -0,0 +1,215 @@ + + + + + + + + + + + + Mozilla Bug 417317 +
    + + Mozilla Bug 443881 +
    + + Mozilla Bug 441888 +
    + +

    + +
    +    
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + diff --git a/accessible/tests/mochitest/attributes/test_obj_group_tree.xhtml b/accessible/tests/mochitest/attributes/test_obj_group_tree.xhtml new file mode 100644 index 0000000000..287ee7989e --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_obj_group_tree.xhtml @@ -0,0 +1,84 @@ + + + + + + + + + + + + Mozilla Bug 503727 +
    +

    + +
    +      
    + + + + + + + + + + + + +
    + +
    + diff --git a/accessible/tests/mochitest/attributes/test_tag.html b/accessible/tests/mochitest/attributes/test_tag.html new file mode 100644 index 0000000000..b57cc2dca8 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_tag.html @@ -0,0 +1,80 @@ + + + + HTML landmark tests + + + + + + + + + + + + + + Bug 593368 +
    + + Bug 613502 + + + Bug 610650 + + + Mozilla Bug 614310 + + + Bug 734982 + +

    + +
    +  
    + + + +
    a footer
    + +
    a section
    + +
    an article
    +
    a main area
    +
    a form area
    + + + diff --git a/accessible/tests/mochitest/attributes/test_xml-roles.html b/accessible/tests/mochitest/attributes/test_xml-roles.html new file mode 100644 index 0000000000..b6cb95eb39 --- /dev/null +++ b/accessible/tests/mochitest/attributes/test_xml-roles.html @@ -0,0 +1,267 @@ + + + + XML roles tests + + + + + + + + + + + + + + Bug 593368 +
    + + Bug 613502 + + + Bug 610650 + + + Mozilla Bug 614310 + + + Bug 734982 + + + Bug 761891 + + + Bug 849624 + + + Bug 1121518 + + + Bug 1356049 + + +

    + +
    +  
    + + + +
    a footer
    +
    +
    a header within an article
    +
    a footer within an article
    +
    +
    +
    a header within a main
    +
    a footer within a main
    +
    + + +
    a section
    +
    a main area
    +
    a form area
    +
    a feed
    +
    article
    +
    another main area
    +
    a figure
    + + + +
    + + + (x,y) + (x,y) + (x,y) + (x,y) + (x,y) + (x,y) + (x,y) + + + + + a + b + + + + + + + a + b + + + + + a + b + + + + + a + b + c + + + + + a + b + c + + d + + e + + f + g + + + + + + + a + b + + + + + a + b + + + + + a + b + c + + + + + + + a + b + + + + + diff --git a/accessible/tests/mochitest/autocomplete.js b/accessible/tests/mochitest/autocomplete.js new file mode 100644 index 0000000000..130d9526e7 --- /dev/null +++ b/accessible/tests/mochitest/autocomplete.js @@ -0,0 +1,195 @@ +const nsISupports = Ci.nsISupports; +const nsIAutoCompleteResult = Ci.nsIAutoCompleteResult; +const nsIAutoCompleteSearch = Ci.nsIAutoCompleteSearch; +const nsIFactory = Ci.nsIFactory; +const nsIUUIDGenerator = Ci.nsIUUIDGenerator; +const nsIComponentRegistrar = Ci.nsIComponentRegistrar; + +var gDefaultAutoCompleteSearch = null; + +/** + * Register 'test-a11y-search' AutoCompleteSearch. + * + * @param aValues [in] set of possible results values + * @param aComments [in] set of possible results descriptions + */ +function initAutoComplete(aValues, aComments) { + var allResults = new ResultsHeap(aValues, aComments); + gDefaultAutoCompleteSearch = new AutoCompleteSearch( + "test-a11y-search", + allResults + ); + registerAutoCompleteSearch( + gDefaultAutoCompleteSearch, + "Accessibility Test AutoCompleteSearch" + ); +} + +/** + * Unregister 'test-a11y-search' AutoCompleteSearch. + */ +function shutdownAutoComplete() { + unregisterAutoCompleteSearch(gDefaultAutoCompleteSearch); + gDefaultAutoCompleteSearch.cid = null; + gDefaultAutoCompleteSearch = null; +} + +/** + * Register the given AutoCompleteSearch. + * + * @param aSearch [in] AutoCompleteSearch object + * @param aDescription [in] description of the search object + */ +function registerAutoCompleteSearch(aSearch, aDescription) { + var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name; + + var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService( + nsIUUIDGenerator + ); + var cid = uuidGenerator.generateUUID(); + + var componentManager = Components.manager.QueryInterface( + nsIComponentRegistrar + ); + componentManager.registerFactory(cid, aDescription, name, aSearch); + + // Keep the id on the object so we can unregister later. + aSearch.cid = cid; +} + +/** + * Unregister the given AutoCompleteSearch. + */ +function unregisterAutoCompleteSearch(aSearch) { + var componentManager = Components.manager.QueryInterface( + nsIComponentRegistrar + ); + componentManager.unregisterFactory(aSearch.cid, aSearch); +} + +/** + * A container to keep all possible results of autocomplete search. + */ +function ResultsHeap(aValues, aComments) { + this.values = aValues; + this.comments = aComments; +} + +ResultsHeap.prototype = { + constructor: ResultsHeap, + + /** + * Return AutoCompleteResult for the given search string. + */ + getAutoCompleteResultFor(aSearchString) { + var values = [], + comments = []; + for (var idx = 0; idx < this.values.length; idx++) { + if (this.values[idx].includes(aSearchString)) { + values.push(this.values[idx]); + comments.push(this.comments[idx]); + } + } + return new AutoCompleteResult(values, comments); + }, +}; + +/** + * nsIAutoCompleteSearch implementation. + * + * @param aName [in] the name of autocomplete search + * @param aAllResults [in] ResultsHeap object + */ +function AutoCompleteSearch(aName, aAllResults) { + this.name = aName; + this.allResults = aAllResults; +} + +AutoCompleteSearch.prototype = { + constructor: AutoCompleteSearch, + + // nsIAutoCompleteSearch implementation + startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) { + var result = this.allResults.getAutoCompleteResultFor(aSearchString); + aListener.onSearchResult(this, result); + }, + + stopSearch() {}, + + // nsISupports implementation + QueryInterface: ChromeUtils.generateQI([ + "nsIFactory", + "nsIAutoCompleteSearch", + ]), + + // nsIFactory implementation + createInstance(outer, iid) { + return this.QueryInterface(iid); + }, + + // Search name. Used by AutoCompleteController. + name: null, + + // Results heap. + allResults: null, +}; + +/** + * nsIAutoCompleteResult implementation. + */ +function AutoCompleteResult(aValues, aComments) { + this.values = aValues; + this.comments = aComments; + + if (this.values.length > 0) { + this.searchResult = nsIAutoCompleteResult.RESULT_SUCCESS; + } else { + this.searchResult = nsIAutoCompleteResult.NOMATCH; + } +} + +AutoCompleteResult.prototype = { + constructor: AutoCompleteResult, + + searchString: "", + searchResult: null, + + defaultIndex: 0, + + get matchCount() { + return this.values.length; + }, + + getValueAt(aIndex) { + return this.values[aIndex]; + }, + + getLabelAt(aIndex) { + return this.getValueAt(aIndex); + }, + + getCommentAt(aIndex) { + return this.comments[aIndex]; + }, + + getStyleAt(aIndex) { + return null; + }, + + getImageAt(aIndex) { + return ""; + }, + + getFinalCompleteValueAt(aIndex) { + return this.getValueAt(aIndex); + }, + + removeValueAt(aRowIndex) {}, + + // nsISupports implementation + QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]), + + // Data + values: null, + comments: null, +}; diff --git a/accessible/tests/mochitest/bounds/a11y.ini b/accessible/tests/mochitest/bounds/a11y.ini new file mode 100644 index 0000000000..bdb8c02f90 --- /dev/null +++ b/accessible/tests/mochitest/bounds/a11y.ini @@ -0,0 +1,6 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + +[test_list.html] +[test_select.html] diff --git a/accessible/tests/mochitest/bounds/test_list.html b/accessible/tests/mochitest/bounds/test_list.html new file mode 100644 index 0000000000..7e5b75868d --- /dev/null +++ b/accessible/tests/mochitest/bounds/test_list.html @@ -0,0 +1,78 @@ + + + + Accessible boundaries when page is zoomed + + + + + + + + + + + + + + + Mozilla Bug 754627 + +

    + +
    +  
    + +
      +
    • item
    • +
    + +
      +
    • item
    • +
    + + + diff --git a/accessible/tests/mochitest/bounds/test_select.html b/accessible/tests/mochitest/bounds/test_select.html new file mode 100644 index 0000000000..ac9a501f32 --- /dev/null +++ b/accessible/tests/mochitest/bounds/test_select.html @@ -0,0 +1,78 @@ + + + + Accessible boundaries when page is zoomed + + + + + + + + + + + + + + +

    + +
    +  
    + + + + diff --git a/accessible/tests/mochitest/browser.js b/accessible/tests/mochitest/browser.js new file mode 100644 index 0000000000..ffe67b0ed0 --- /dev/null +++ b/accessible/tests/mochitest/browser.js @@ -0,0 +1,157 @@ +/* import-globals-from common.js */ + +var { AppConstants } = ChromeUtils.import( + "resource://gre/modules/AppConstants.jsm" +); + +/** + * Load the browser with the given url and then invokes the given function. + */ +function openBrowserWindow(aFunc, aURL, aRect) { + gBrowserContext.testFunc = aFunc; + gBrowserContext.startURL = aURL; + gBrowserContext.browserRect = aRect; + + addLoadEvent(openBrowserWindowIntl); +} + +/** + * Close the browser window. + */ +function closeBrowserWindow() { + gBrowserContext.browserWnd.close(); +} + +/** + * Return the browser window object. + */ +function browserWindow() { + return gBrowserContext.browserWnd; +} + +/** + * Return the document of the browser window. + */ +function browserDocument() { + return browserWindow().document; +} + +/** + * Return tab browser object. + */ +function tabBrowser() { + return browserWindow().gBrowser; +} + +/** + * Return browser element of the current tab. + */ +function currentBrowser() { + return tabBrowser().selectedBrowser; +} + +/** + * Return DOM document of the current tab. + */ +function currentTabDocument() { + return currentBrowser().contentDocument; +} + +/** + * Return window of the current tab. + */ +function currentTabWindow() { + return currentTabDocument().defaultView; +} + +/** + * Return browser element of the tab at the given index. + */ +function browserAt(aIndex) { + return tabBrowser().getBrowserAtIndex(aIndex); +} + +/** + * Return DOM document of the tab at the given index. + */ +function tabDocumentAt(aIndex) { + return browserAt(aIndex).contentDocument; +} + +/** + * Return input element of address bar. + */ +function urlbarInput() { + return browserWindow().document.getElementById("urlbar").inputField; +} + +/** + * Return reload button. + */ +function reloadButton() { + return browserWindow().document.getElementById("urlbar-reload-button"); +} + +// ////////////////////////////////////////////////////////////////////////////// +// private section + +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +var gBrowserContext = { + browserWnd: null, + testFunc: null, + startURL: "", +}; + +function openBrowserWindowIntl() { + var params = "chrome,all,dialog=no,non-remote"; + var rect = gBrowserContext.browserRect; + if (rect) { + if ("left" in rect) { + params += ",left=" + rect.left; + } + if ("top" in rect) { + params += ",top=" + rect.top; + } + if ("width" in rect) { + params += ",width=" + rect.width; + } + if ("height" in rect) { + params += ",height=" + rect.height; + } + } + + gBrowserContext.browserWnd = window.browsingContext.topChromeWindow.openDialog( + AppConstants.BROWSER_CHROME_URL, + "_blank", + params, + gBrowserContext.startURL || "data:text/html," + ); + + whenDelayedStartupFinished(browserWindow(), function() { + addA11yLoadEvent(startBrowserTests, browserWindow()); + }); +} + +function startBrowserTests() { + if (gBrowserContext.startURL) { + // Make sure the window is the one loading our URL. + if (currentBrowser().contentWindow.location != gBrowserContext.startURL) { + setTimeout(startBrowserTests, 0); + return; + } + // wait for load + addA11yLoadEvent(gBrowserContext.testFunc, currentBrowser().contentWindow); + } else { + gBrowserContext.testFunc(); + } +} + +function whenDelayedStartupFinished(aWindow, aCallback) { + Services.obs.addObserver(function observer(aSubject, aTopic) { + if (aWindow == aSubject) { + Services.obs.removeObserver(observer, aTopic); + setTimeout(aCallback, 0); + } + }, "browser-delayed-startup-finished"); +} diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js new file mode 100644 index 0000000000..69014c6cd3 --- /dev/null +++ b/accessible/tests/mochitest/common.js @@ -0,0 +1,1076 @@ +// This file has circular dependencies that may require other files. Rather +// than use import-globals-from, we list the globals individually here to save +// confusing ESLint. +// actions.js +/* globals testActionNames */ +// attributes.js +/* globals testAttrs, testAbsentAttrs, testTextAttrs */ +// relations.js +/* globals testRelation */ +// role.js +/* globals isRole */ +// state.js +/* globals testStates */ + +// ////////////////////////////////////////////////////////////////////////////// +// Interfaces + +const nsIAccessibilityService = Ci.nsIAccessibilityService; + +const nsIAccessibleEvent = Ci.nsIAccessibleEvent; +const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent; +const nsIAccessibleCaretMoveEvent = Ci.nsIAccessibleCaretMoveEvent; +const nsIAccessibleScrollingEvent = Ci.nsIAccessibleScrollingEvent; +const nsIAccessibleTextChangeEvent = Ci.nsIAccessibleTextChangeEvent; +const nsIAccessibleTextSelectionChangeEvent = + Ci.nsIAccessibleTextSelectionChangeEvent; +const nsIAccessibleVirtualCursorChangeEvent = + Ci.nsIAccessibleVirtualCursorChangeEvent; +const nsIAccessibleObjectAttributeChangedEvent = + Ci.nsIAccessibleObjectAttributeChangedEvent; +const nsIAccessibleAnnouncementEvent = Ci.nsIAccessibleAnnouncementEvent; + +const nsIAccessibleStates = Ci.nsIAccessibleStates; +const nsIAccessibleRole = Ci.nsIAccessibleRole; +const nsIAccessibleScrollType = Ci.nsIAccessibleScrollType; +const nsIAccessibleCoordinateType = Ci.nsIAccessibleCoordinateType; + +const nsIAccessibleRelation = Ci.nsIAccessibleRelation; +const nsIAccessibleTextRange = Ci.nsIAccessibleTextRange; + +const nsIAccessible = Ci.nsIAccessible; + +const nsIAccessibleDocument = Ci.nsIAccessibleDocument; +const nsIAccessibleApplication = Ci.nsIAccessibleApplication; + +const nsIAccessibleText = Ci.nsIAccessibleText; +const nsIAccessibleEditableText = Ci.nsIAccessibleEditableText; + +const nsIAccessibleHyperLink = Ci.nsIAccessibleHyperLink; +const nsIAccessibleHyperText = Ci.nsIAccessibleHyperText; + +const nsIAccessibleImage = Ci.nsIAccessibleImage; +const nsIAccessiblePivot = Ci.nsIAccessiblePivot; +const nsIAccessibleSelectable = Ci.nsIAccessibleSelectable; +const nsIAccessibleTable = Ci.nsIAccessibleTable; +const nsIAccessibleTableCell = Ci.nsIAccessibleTableCell; +const nsIAccessibleTraversalRule = Ci.nsIAccessibleTraversalRule; +const nsIAccessibleValue = Ci.nsIAccessibleValue; + +const nsIObserverService = Ci.nsIObserverService; + +const nsIDOMWindow = Ci.nsIDOMWindow; + +const nsIPropertyElement = Ci.nsIPropertyElement; + +// ////////////////////////////////////////////////////////////////////////////// +// OS detect + +const MAC = navigator.platform.includes("Mac"); +const LINUX = navigator.platform.includes("Linux"); +const SOLARIS = navigator.platform.includes("SunOS"); +const WIN = navigator.platform.includes("Win"); + +// ////////////////////////////////////////////////////////////////////////////// +// Application detect +// Firefox is assumed by default. + +const SEAMONKEY = navigator.userAgent.match(/ SeaMonkey\//); + +// ////////////////////////////////////////////////////////////////////////////// +// Accessible general + +const STATE_BUSY = nsIAccessibleStates.STATE_BUSY; + +const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE; + +const COORDTYPE_SCREEN_RELATIVE = + nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE; +const COORDTYPE_WINDOW_RELATIVE = + nsIAccessibleCoordinateType.COORDTYPE_WINDOW_RELATIVE; +const COORDTYPE_PARENT_RELATIVE = + nsIAccessibleCoordinateType.COORDTYPE_PARENT_RELATIVE; + +const kEmbedChar = String.fromCharCode(0xfffc); + +const kDiscBulletChar = String.fromCharCode(0x2022); +const kDiscBulletText = kDiscBulletChar + " "; +const kCircleBulletText = String.fromCharCode(0x25e6) + " "; +const kSquareBulletText = String.fromCharCode(0x25fe) + " "; + +const MAX_TRIM_LENGTH = 100; + +/** + * Services to determine if e10s is enabled. + */ +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +/** + * nsIAccessibilityService service. + */ +var gAccService = Cc["@mozilla.org/accessibilityService;1"].getService( + nsIAccessibilityService +); + +/** + * Enable/disable logging. + */ +function enableLogging(aModules) { + gAccService.setLogging(aModules); +} +function disableLogging() { + gAccService.setLogging(""); +} +function isLogged(aModule) { + return gAccService.isLogged(aModule); +} + +/** + * Dumps the accessible tree into console. + */ +function dumpTree(aId, aMsg) { + function dumpTreeIntl(acc, indent) { + dump(indent + prettyName(acc) + "\n"); + + var children = acc.children; + for (var i = 0; i < children.length; i++) { + var child = children.queryElementAt(i, nsIAccessible); + dumpTreeIntl(child, indent + " "); + } + } + + function dumpDOMTreeIntl(node, indent) { + dump(indent + prettyName(node) + "\n"); + + var children = node.childNodes; + for (var i = 0; i < children.length; i++) { + var child = children.item(i); + dumpDOMTreeIntl(child, indent + " "); + } + } + + dump(aMsg + "\n"); + var root = getAccessible(aId); + dumpTreeIntl(root, " "); + + dump("DOM tree:\n"); + dumpDOMTreeIntl(getNode(aId), " "); +} + +/** + * Invokes the given function when document is loaded and focused. Preferable + * to mochitests 'addLoadEvent' function -- additionally ensures state of the + * document accessible is not busy. + * + * @param aFunc the function to invoke + */ +function addA11yLoadEvent(aFunc, aWindow) { + function waitForDocLoad() { + window.setTimeout(function() { + var targetDocument = aWindow ? aWindow.document : document; + var accDoc = getAccessible(targetDocument); + var state = {}; + accDoc.getState(state, {}); + if (state.value & STATE_BUSY) { + waitForDocLoad(); + return; + } + + window.setTimeout(aFunc, 0); + }, 0); + } + + if ( + aWindow && + aWindow.document.activeElement && + aWindow.document.activeElement.localName == "browser" + ) { + waitForDocLoad(); + } else { + SimpleTest.waitForFocus(waitForDocLoad, aWindow); + } +} + +/** + * Analogy of SimpleTest.is function used to compare objects. + */ +function isObject(aObj, aExpectedObj, aMsg) { + if (aObj == aExpectedObj) { + ok(true, aMsg); + return; + } + + ok( + false, + aMsg + + " - got '" + + prettyName(aObj) + + "', expected '" + + prettyName(aExpectedObj) + + "'" + ); +} + +/** + * is() function checking the expected value is within the range. + */ +function isWithin(aExpected, aGot, aWithin, aMsg) { + if (Math.abs(aGot - aExpected) <= aWithin) { + ok(true, `${aMsg} - Got ${aGot}`); + } else { + ok( + false, + `${aMsg} - Got ${aGot}, expected ${aExpected} with error of ${aWithin}` + ); + } +} + +// ////////////////////////////////////////////////////////////////////////////// +// Helpers for getting DOM node/accessible + +/** + * Return the DOM node by identifier (may be accessible, DOM node or ID). + */ +function getNode(aAccOrNodeOrID, aDocument) { + if (!aAccOrNodeOrID) { + return null; + } + + if (Node.isInstance(aAccOrNodeOrID)) { + return aAccOrNodeOrID; + } + + if (aAccOrNodeOrID instanceof nsIAccessible) { + return aAccOrNodeOrID.DOMNode; + } + + var node = (aDocument || document).getElementById(aAccOrNodeOrID); + if (!node) { + ok(false, "Can't get DOM element for " + aAccOrNodeOrID); + return null; + } + + return node; +} + +/** + * Constants indicates getAccessible doesn't fail if there is no accessible. + */ +const DONOTFAIL_IF_NO_ACC = 1; + +/** + * Constants indicates getAccessible won't fail if accessible doesn't implement + * the requested interfaces. + */ +const DONOTFAIL_IF_NO_INTERFACE = 2; + +/** + * Return accessible for the given identifier (may be ID attribute or DOM + * element or accessible object) or null. + * + * @param aAccOrElmOrID [in] identifier to get an accessible implementing + * the given interfaces + * @param aInterfaces [in, optional] the interface or an array interfaces + * to query it/them from obtained accessible + * @param aElmObj [out, optional] object to store DOM element which + * accessible is obtained for + * @param aDoNotFailIf [in, optional] no error for special cases (see + * constants above) + */ +function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIf) { + if (!aAccOrElmOrID) { + return null; + } + + var elm = null; + + if (aAccOrElmOrID instanceof nsIAccessible) { + try { + elm = aAccOrElmOrID.DOMNode; + } catch (e) {} + } else if (Node.isInstance(aAccOrElmOrID)) { + elm = aAccOrElmOrID; + } else { + elm = document.getElementById(aAccOrElmOrID); + if (!elm) { + ok(false, "Can't get DOM element for " + aAccOrElmOrID); + return null; + } + } + + if (aElmObj && typeof aElmObj == "object") { + aElmObj.value = elm; + } + + var acc = aAccOrElmOrID instanceof nsIAccessible ? aAccOrElmOrID : null; + if (!acc) { + try { + acc = gAccService.getAccessibleFor(elm); + } catch (e) {} + + if (!acc) { + if (!(aDoNotFailIf & DONOTFAIL_IF_NO_ACC)) { + ok(false, "Can't get accessible for " + prettyName(aAccOrElmOrID)); + } + + return null; + } + } + + if (!aInterfaces) { + return acc; + } + + if (!(aInterfaces instanceof Array)) { + aInterfaces = [aInterfaces]; + } + + for (var index = 0; index < aInterfaces.length; index++) { + if (acc instanceof aInterfaces[index]) { + continue; + } + try { + acc.QueryInterface(aInterfaces[index]); + } catch (e) { + if (!(aDoNotFailIf & DONOTFAIL_IF_NO_INTERFACE)) { + ok( + false, + "Can't query " + aInterfaces[index] + " for " + aAccOrElmOrID + ); + } + + return null; + } + } + + return acc; +} + +/** + * Return true if the given identifier has an accessible, or exposes the wanted + * interfaces. + */ +function isAccessible(aAccOrElmOrID, aInterfaces) { + return !!getAccessible( + aAccOrElmOrID, + aInterfaces, + null, + DONOTFAIL_IF_NO_ACC | DONOTFAIL_IF_NO_INTERFACE + ); +} + +/** + * Return an accessible that contains the DOM node for the given identifier. + */ +function getContainerAccessible(aAccOrElmOrID) { + var node = getNode(aAccOrElmOrID); + if (!node) { + return null; + } + + // eslint-disable-next-line no-empty + while ((node = node.parentNode) && !isAccessible(node)) {} + return node ? getAccessible(node) : null; +} + +/** + * Return root accessible for the given identifier. + */ +function getRootAccessible(aAccOrElmOrID) { + var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document); + return acc ? acc.rootDocument.QueryInterface(nsIAccessible) : null; +} + +/** + * Return tab document accessible the given accessible is contained by. + */ +function getTabDocAccessible(aAccOrElmOrID) { + var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document); + + var docAcc = acc.document.QueryInterface(nsIAccessible); + var containerDocAcc = docAcc.parent.document; + + // Test is running is stand-alone mode. + if (acc.rootDocument == containerDocAcc) { + return docAcc; + } + + // In the case of running all tests together. + return containerDocAcc.QueryInterface(nsIAccessible); +} + +/** + * Return application accessible. + */ +function getApplicationAccessible() { + return gAccService + .getApplicationAccessible() + .QueryInterface(nsIAccessibleApplication); +} + +/** + * A version of accessible tree testing, doesn't fail if tree is not complete. + */ +function testElm(aID, aTreeObj) { + testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck); +} + +/** + * Flags used for testAccessibleTree + */ +const kSkipTreeFullCheck = 1; + +/** + * Compare expected and actual accessibles trees. + * + * @param aAccOrElmOrID [in] accessible identifier + * @param aAccTree [in] JS object, each field corresponds to property of + * accessible object. Additionally special properties + * are presented: + * children - an array of JS objects representing + * children of accessible + * states - an object having states and extraStates + * fields + * @param aFlags [in, optional] flags, see constants above + */ +// eslint-disable-next-line complexity +function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags) { + var acc = getAccessible(aAccOrElmOrID); + if (!acc) { + return; + } + + var accTree = aAccTree; + + // Support of simplified accessible tree object. + accTree = normalizeAccTreeObj(accTree); + + // Test accessible properties. + for (var prop in accTree) { + var msg = + "Wrong value of property '" + prop + "' for " + prettyName(acc) + "."; + + switch (prop) { + case "actions": { + testActionNames(acc, accTree.actions); + break; + } + + case "attributes": + testAttrs(acc, accTree[prop], true); + break; + + case "absentAttributes": + testAbsentAttrs(acc, accTree[prop]); + break; + + case "interfaces": { + var ifaces = + accTree[prop] instanceof Array ? accTree[prop] : [accTree[prop]]; + for (let i = 0; i < ifaces.length; i++) { + ok( + acc instanceof ifaces[i], + "No " + ifaces[i] + " interface on " + prettyName(acc) + ); + } + break; + } + + case "relations": { + for (var rel in accTree[prop]) { + testRelation(acc, window[rel], accTree[prop][rel]); + } + break; + } + + case "role": + isRole(acc, accTree[prop], msg); + break; + + case "states": + case "extraStates": + case "absentStates": + case "absentExtraStates": { + testStates( + acc, + accTree.states, + accTree.extraStates, + accTree.absentStates, + accTree.absentExtraStates + ); + break; + } + + case "tagName": + is(accTree[prop], acc.DOMNode.tagName, msg); + break; + + case "textAttrs": { + var prevOffset = -1; + for (var offset in accTree[prop]) { + if (prevOffset != -1) { + let attrs = accTree[prop][prevOffset]; + testTextAttrs( + acc, + prevOffset, + attrs, + {}, + prevOffset, + +offset, + true + ); + } + prevOffset = +offset; + } + + if (prevOffset != -1) { + var charCount = getAccessible(acc, [nsIAccessibleText]) + .characterCount; + let attrs = accTree[prop][prevOffset]; + testTextAttrs( + acc, + prevOffset, + attrs, + {}, + prevOffset, + charCount, + true + ); + } + + break; + } + + default: + if (prop.indexOf("todo_") == 0) { + todo(false, msg); + } else if (prop != "children") { + is(acc[prop], accTree[prop], msg); + } + } + } + + // Test children. + if ("children" in accTree && accTree.children instanceof Array) { + var children = acc.children; + var childCount = children.length; + + if (accTree.children.length != childCount) { + for (let i = 0; i < Math.max(accTree.children.length, childCount); i++) { + var accChild = null, + testChild = null; + try { + testChild = accTree.children[i]; + accChild = children.queryElementAt(i, nsIAccessible); + + if (!testChild) { + ok( + false, + prettyName(acc) + + " has an extra child at index " + + i + + " : " + + prettyName(accChild) + ); + continue; + } + + testChild = normalizeAccTreeObj(testChild); + if (accChild.role !== testChild.role) { + ok( + false, + prettyName(accTree) + + " and " + + prettyName(acc) + + " have different children at index " + + i + + " : " + + prettyName(testChild) + + ", " + + prettyName(accChild) + ); + } + info( + "Matching " + + prettyName(accTree) + + " and " + + prettyName(acc) + + " child at index " + + i + + " : " + + prettyName(accChild) + ); + } catch (e) { + ok( + false, + prettyName(accTree) + + " is expected to have a child at index " + + i + + " : " + + prettyName(testChild) + + ", original tested: " + + prettyName(aAccOrElmOrID) + + ", " + + e + ); + } + } + } else { + if (aFlags & kSkipTreeFullCheck) { + for (let i = 0; i < childCount; i++) { + let child = children.queryElementAt(i, nsIAccessible); + testAccessibleTree(child, accTree.children[i], aFlags); + } + return; + } + + // nsIAccessible::firstChild + var expectedFirstChild = + childCount > 0 ? children.queryElementAt(0, nsIAccessible) : null; + var firstChild = null; + try { + firstChild = acc.firstChild; + } catch (e) {} + is( + firstChild, + expectedFirstChild, + "Wrong first child of " + prettyName(acc) + ); + + // nsIAccessible::lastChild + var expectedLastChild = + childCount > 0 + ? children.queryElementAt(childCount - 1, nsIAccessible) + : null; + var lastChild = null; + try { + lastChild = acc.lastChild; + } catch (e) {} + is( + lastChild, + expectedLastChild, + "Wrong last child of " + prettyName(acc) + ); + + for (var i = 0; i < childCount; i++) { + let child = children.queryElementAt(i, nsIAccessible); + + // nsIAccessible::parent + var parent = null; + try { + parent = child.parent; + } catch (e) {} + is(parent, acc, "Wrong parent of " + prettyName(child)); + + // nsIAccessible::indexInParent + var indexInParent = -1; + try { + indexInParent = child.indexInParent; + } catch (e) {} + is(indexInParent, i, "Wrong index in parent of " + prettyName(child)); + + // nsIAccessible::nextSibling + var expectedNextSibling = + i < childCount - 1 + ? children.queryElementAt(i + 1, nsIAccessible) + : null; + var nextSibling = null; + try { + nextSibling = child.nextSibling; + } catch (e) {} + is( + nextSibling, + expectedNextSibling, + "Wrong next sibling of " + prettyName(child) + ); + + // nsIAccessible::previousSibling + var expectedPrevSibling = + i > 0 ? children.queryElementAt(i - 1, nsIAccessible) : null; + var prevSibling = null; + try { + prevSibling = child.previousSibling; + } catch (e) {} + is( + prevSibling, + expectedPrevSibling, + "Wrong previous sibling of " + prettyName(child) + ); + + // Go down through subtree + testAccessibleTree(child, accTree.children[i], aFlags); + } + } + } +} + +/** + * Return true if accessible for the given node is in cache. + */ +function isAccessibleInCache(aNodeOrId) { + var node = getNode(aNodeOrId); + return !!gAccService.getAccessibleFromCache(node); +} + +/** + * Test accessible tree for defunct accessible. + * + * @param aAcc [in] the defunct accessible + * @param aNodeOrId [in] the DOM node identifier for the defunct accessible + */ +function testDefunctAccessible(aAcc, aNodeOrId) { + if (aNodeOrId) { + ok( + !isAccessible(aNodeOrId), + "Accessible for " + aNodeOrId + " wasn't properly shut down!" + ); + } + + var msg = + " doesn't fail for shut down accessible " + prettyName(aNodeOrId) + "!"; + + // firstChild + var success = false; + try { + aAcc.firstChild; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "firstChild" + msg); + + // lastChild + success = false; + try { + aAcc.lastChild; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "lastChild" + msg); + + // childCount + success = false; + try { + aAcc.childCount; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "childCount" + msg); + + // children + success = false; + try { + aAcc.children; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "children" + msg); + + // nextSibling + success = false; + try { + aAcc.nextSibling; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "nextSibling" + msg); + + // previousSibling + success = false; + try { + aAcc.previousSibling; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "previousSibling" + msg); + + // parent + success = false; + try { + aAcc.parent; + } catch (e) { + success = e.result == Cr.NS_ERROR_FAILURE; + } + ok(success, "parent" + msg); +} + +/** + * Convert role to human readable string. + */ +function roleToString(aRole) { + return gAccService.getStringRole(aRole); +} + +/** + * Convert states to human readable string. + */ +function statesToString(aStates, aExtraStates) { + var list = gAccService.getStringStates(aStates, aExtraStates); + + var str = ""; + for (var index = 0; index < list.length - 1; index++) { + str += list.item(index) + ", "; + } + + if (list.length != 0) { + str += list.item(index); + } + + return str; +} + +/** + * Convert event type to human readable string. + */ +function eventTypeToString(aEventType) { + return gAccService.getStringEventType(aEventType); +} + +/** + * Convert relation type to human readable string. + */ +function relationTypeToString(aRelationType) { + return gAccService.getStringRelationType(aRelationType); +} + +function getLoadContext() { + return window.docShell.QueryInterface(Ci.nsILoadContext); +} + +/** + * Return text from clipboard. + */ +function getTextFromClipboard() { + var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(getLoadContext()); + if (!trans) { + return ""; + } + + trans.addDataFlavor("text/unicode"); + Services.clipboard.getData(trans, Services.clipboard.kGlobalClipboard); + + var str = {}; + trans.getTransferData("text/unicode", str); + + if (str) { + str = str.value.QueryInterface(Ci.nsISupportsString); + } + if (str) { + return str.data; + } + + return ""; +} + +/** + * Extract DOMNode id from an accessible. If e10s is enabled, DOMNode is not + * present in parent process but, if available, DOMNode id is attached to an + * accessible object. + * @param {nsIAccessible} accessible accessible + * @return {String?} DOMNode id if available + */ +function getAccessibleDOMNodeID(accessible) { + if (accessible instanceof nsIAccessibleDocument) { + // If accessible is a document, trying to find its document body id. + try { + return accessible.DOMNode.body.id; + } catch (e) { + /* This only works if accessible is not a proxy. */ + } + } + try { + return accessible.DOMNode.id; + } catch (e) { + /* This will fail if DOMNode is in different process. */ + } + try { + // When e10s is enabled, accessible will have an "id" property if its + // corresponding DOMNode has an id. If accessible is a document, its "id" + // property corresponds to the "id" of its body element. + return accessible.id; + } catch (e) { + /* This will fail if accessible is not a proxy. */ + } + return null; +} + +/** + * Return pretty name for identifier, it may be ID, DOM node or accessible. + */ +function prettyName(aIdentifier) { + if (aIdentifier instanceof Array) { + let msg = ""; + for (var idx = 0; idx < aIdentifier.length; idx++) { + if (msg != "") { + msg += ", "; + } + + msg += prettyName(aIdentifier[idx]); + } + return msg; + } + + if (aIdentifier instanceof nsIAccessible) { + var acc = getAccessible(aIdentifier); + var domID = getAccessibleDOMNodeID(acc); + let msg = "["; + try { + if (Services.appinfo.browserTabsRemoteAutostart) { + if (domID) { + msg += `DOM node id: ${domID}, `; + } + } else { + msg += `${getNodePrettyName(acc.DOMNode)}, `; + } + msg += "role: " + roleToString(acc.role); + if (acc.name) { + msg += ", name: '" + shortenString(acc.name) + "'"; + } + } catch (e) { + msg += "defunct"; + } + + if (acc) { + msg += ", address: " + getObjAddress(acc); + } + msg += "]"; + + return msg; + } + + if (Node.isInstance(aIdentifier)) { + return "[ " + getNodePrettyName(aIdentifier) + " ]"; + } + + if (aIdentifier && typeof aIdentifier === "object") { + var treeObj = normalizeAccTreeObj(aIdentifier); + if ("role" in treeObj) { + function stringifyTree(aObj) { + var text = roleToString(aObj.role) + ": [ "; + if ("children" in aObj) { + for (var i = 0; i < aObj.children.length; i++) { + var c = normalizeAccTreeObj(aObj.children[i]); + text += stringifyTree(c); + if (i < aObj.children.length - 1) { + text += ", "; + } + } + } + return text + "] "; + } + return `{ ${stringifyTree(treeObj)} }`; + } + return JSON.stringify(aIdentifier); + } + + return " '" + aIdentifier + "' "; +} + +/** + * Shorten a long string if it exceeds MAX_TRIM_LENGTH. + * @param aString the string to shorten. + * @returns the shortened string. + */ +function shortenString(aString, aMaxLength) { + if (aString.length <= MAX_TRIM_LENGTH) { + return aString; + } + + // Trim the string if its length is > MAX_TRIM_LENGTH characters. + var trimOffset = MAX_TRIM_LENGTH / 2; + return ( + aString.substring(0, trimOffset - 1) + + "..." + + aString.substring(aString.length - trimOffset, aString.length) + ); +} + +// ////////////////////////////////////////////////////////////////////////////// +// General Utils +// ////////////////////////////////////////////////////////////////////////////// +/** + * Return main chrome window (crosses chrome boundary) + */ +function getMainChromeWindow(aWindow) { + return aWindow.browsingContext.topChromeWindow; +} + +/** Sets the test plugin(s) initially expected enabled state. + * It will automatically be reset to it's previous value after the test + * ends. + * @param aNewEnabledState [in] the enabled state, e.g. SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED + * @param aPluginName [in, optional] The name of the plugin, defaults to "Test Plug-in" + */ +function setTestPluginEnabledState(aNewEnabledState, aPluginName) { + var plugin = getTestPluginTag(aPluginName); + var oldEnabledState = plugin.enabledState; + plugin.enabledState = aNewEnabledState; + SimpleTest.registerCleanupFunction(function() { + getTestPluginTag(aPluginName).enabledState = oldEnabledState; + }); +} + +// ////////////////////////////////////////////////////////////////////////////// +// Private +// ////////////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////////////// +// Accessible general + +function getNodePrettyName(aNode) { + try { + var tag = ""; + if (aNode.nodeType == Node.DOCUMENT_NODE) { + tag = "document"; + } else { + tag = aNode.localName; + if (aNode.nodeType == Node.ELEMENT_NODE && aNode.hasAttribute("id")) { + tag += '@id="' + aNode.getAttribute("id") + '"'; + } + } + + return "'" + tag + " node', address: " + getObjAddress(aNode); + } catch (e) { + return "' no node info '"; + } +} + +function getObjAddress(aObj) { + var exp = /native\s*@\s*(0x[a-f0-9]+)/g; + var match = exp.exec(aObj.toString()); + if (match) { + return match[1]; + } + + return aObj.toString(); +} + +function getTestPluginTag(aPluginName) { + var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"].getService( + SpecialPowers.Ci.nsIPluginHost + ); + var tags = ph.getPluginTags(); + var name = aPluginName || "Test Plug-in"; + for (var tag of tags) { + if (tag.name == name) { + return tag; + } + } + + ok(false, "Could not find plugin tag with plugin name '" + name + "'"); + return null; +} + +function normalizeAccTreeObj(aObj) { + var key = Object.keys(aObj)[0]; + var roleName = "ROLE_" + key; + if (roleName in nsIAccessibleRole) { + return { + role: nsIAccessibleRole[roleName], + children: aObj[key], + }; + } + return aObj; +} diff --git a/accessible/tests/mochitest/dumbfile.zip b/accessible/tests/mochitest/dumbfile.zip new file mode 100644 index 0000000000..15cb0ecb3e Binary files /dev/null and b/accessible/tests/mochitest/dumbfile.zip differ diff --git a/accessible/tests/mochitest/editabletext/a11y.ini b/accessible/tests/mochitest/editabletext/a11y.ini new file mode 100644 index 0000000000..68466fdf23 --- /dev/null +++ b/accessible/tests/mochitest/editabletext/a11y.ini @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = + editabletext.js + !/accessible/tests/mochitest/*.js + +[test_1.html] +[test_2.html] diff --git a/accessible/tests/mochitest/editabletext/editabletext.js b/accessible/tests/mochitest/editabletext/editabletext.js new file mode 100644 index 0000000000..ef305c7842 --- /dev/null +++ b/accessible/tests/mochitest/editabletext/editabletext.js @@ -0,0 +1,409 @@ +/* import-globals-from ../common.js */ +/* import-globals-from ../events.js */ + +/** + * Perform all editable text tests. + */ +function editableTextTestRun() { + this.add = function add(aTest) { + this.seq.push(aTest); + }; + + this.run = function run() { + this.iterate(); + }; + + this.index = 0; + this.seq = []; + + this.iterate = function iterate() { + if (this.index < this.seq.length) { + this.seq[this.index++].startTest(this); + return; + } + + this.seq = null; + SimpleTest.finish(); + }; +} + +/** + * Used to test nsIEditableTextAccessible methods. + */ +function editableTextTest(aID) { + /** + * Schedule a test, the given function with its arguments will be executed + * when preceding test is complete. + */ + this.scheduleTest = function scheduleTest(aFunc, ...aFuncArgs) { + // A data container acts like a dummy invoker, it's never invoked but + // it's used to generate real invoker when previous invoker was handled. + var dataContainer = { + func: aFunc, + funcArgs: aFuncArgs, + }; + this.mEventQueue.push(dataContainer); + + if (!this.mEventQueueReady) { + this.unwrapNextTest(); + this.mEventQueueReady = true; + } + }; + + /** + * setTextContents test. + */ + this.setTextContents = function setTextContents(aValue, aSkipStartOffset) { + var testID = "setTextContents '" + aValue + "' for " + prettyName(aID); + + function setTextContentsInvoke() { + dump(`\nsetTextContents '${aValue}'\n`); + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.setTextContents(aValue); + } + + aSkipStartOffset = aSkipStartOffset || 0; + var insertTripple = aValue + ? [aSkipStartOffset, aSkipStartOffset + aValue.length, aValue] + : null; + var oldValue = getValue(); + var removeTripple = oldValue + ? [aSkipStartOffset, aSkipStartOffset + oldValue.length, oldValue] + : null; + + this.generateTest( + removeTripple, + insertTripple, + setTextContentsInvoke, + getValueChecker(aValue), + testID + ); + }; + + /** + * insertText test. + */ + this.insertText = function insertText(aStr, aPos, aResStr, aResPos) { + var testID = + "insertText '" + aStr + "' at " + aPos + " for " + prettyName(aID); + + function insertTextInvoke() { + dump(`\ninsertText '${aStr}' at ${aPos} pos\n`); + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.insertText(aStr, aPos); + } + + var resPos = aResPos != undefined ? aResPos : aPos; + this.generateTest( + null, + [resPos, resPos + aStr.length, aStr], + insertTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + /** + * copyText test. + */ + this.copyText = function copyText(aStartPos, aEndPos, aClipboardStr) { + var testID = + "copyText from " + + aStartPos + + " to " + + aEndPos + + " for " + + prettyName(aID); + + function copyTextInvoke() { + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.copyText(aStartPos, aEndPos); + } + + this.generateTest( + null, + null, + copyTextInvoke, + getClipboardChecker(aClipboardStr), + testID + ); + }; + + /** + * copyText and pasteText test. + */ + this.copyNPasteText = function copyNPasteText( + aStartPos, + aEndPos, + aPos, + aResStr + ) { + var testID = + "copyText from " + + aStartPos + + " to " + + aEndPos + + "and pasteText at " + + aPos + + " for " + + prettyName(aID); + + function copyNPasteTextInvoke() { + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.copyText(aStartPos, aEndPos); + acc.pasteText(aPos); + } + + this.generateTest( + null, + [aStartPos, aEndPos, getTextFromClipboard], + copyNPasteTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + /** + * cutText test. + */ + this.cutText = function cutText( + aStartPos, + aEndPos, + aResStr, + aResStartPos, + aResEndPos + ) { + var testID = + "cutText from " + + aStartPos + + " to " + + aEndPos + + " for " + + prettyName(aID); + + function cutTextInvoke() { + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.cutText(aStartPos, aEndPos); + } + + var resStartPos = aResStartPos != undefined ? aResStartPos : aStartPos; + var resEndPos = aResEndPos != undefined ? aResEndPos : aEndPos; + this.generateTest( + [resStartPos, resEndPos, getTextFromClipboard], + null, + cutTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + /** + * cutText and pasteText test. + */ + this.cutNPasteText = function copyNPasteText( + aStartPos, + aEndPos, + aPos, + aResStr + ) { + var testID = + "cutText from " + + aStartPos + + " to " + + aEndPos + + " and pasteText at " + + aPos + + " for " + + prettyName(aID); + + function cutNPasteTextInvoke() { + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.cutText(aStartPos, aEndPos); + acc.pasteText(aPos); + } + + this.generateTest( + [aStartPos, aEndPos, getTextFromClipboard], + [aPos, -1, getTextFromClipboard], + cutNPasteTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + /** + * pasteText test. + */ + this.pasteText = function pasteText(aPos, aResStr) { + var testID = "pasteText at " + aPos + " for " + prettyName(aID); + + function pasteTextInvoke() { + var acc = getAccessible(aID, nsIAccessibleEditableText); + acc.pasteText(aPos); + } + + this.generateTest( + null, + [aPos, -1, getTextFromClipboard], + pasteTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + /** + * deleteText test. + */ + this.deleteText = function deleteText(aStartPos, aEndPos, aResStr) { + var testID = + "deleteText from " + + aStartPos + + " to " + + aEndPos + + " for " + + prettyName(aID); + + var oldValue = getValue().substring(aStartPos, aEndPos); + var removeTripple = oldValue ? [aStartPos, aEndPos, oldValue] : null; + + function deleteTextInvoke() { + var acc = getAccessible(aID, [nsIAccessibleEditableText]); + acc.deleteText(aStartPos, aEndPos); + } + + this.generateTest( + removeTripple, + null, + deleteTextInvoke, + getValueChecker(aResStr), + testID + ); + }; + + // //////////////////////////////////////////////////////////////////////////// + // Implementation details. + + function getValue() { + var elm = getNode(aID); + var elmClass = ChromeUtils.getClassName(elm); + if (elmClass === "HTMLTextAreaElement" || elmClass === "HTMLInputElement") { + return elm.value; + } + + if (elmClass === "HTMLDocument") { + return elm.body.textContent; + } + + return elm.textContent; + } + + /** + * Common checkers. + */ + function getValueChecker(aValue) { + var checker = { + check: function valueChecker_check() { + is(getValue(), aValue, "Wrong value " + aValue); + }, + }; + return checker; + } + + function getClipboardChecker(aText) { + var checker = { + check: function clipboardChecker_check() { + is(getTextFromClipboard(), aText, "Wrong text in clipboard."); + }, + }; + return checker; + } + + /** + * Process next scheduled test. + */ + this.unwrapNextTest = function unwrapNextTest() { + var data = this.mEventQueue.mInvokers[this.mEventQueue.mIndex + 1]; + if (data) { + data.func.apply(this, data.funcArgs); + } + }; + + /** + * Used to generate an invoker object for the sheduled test. + */ + this.generateTest = function generateTest( + aRemoveTriple, + aInsertTriple, + aInvokeFunc, + aChecker, + aInvokerID + ) { + var et = this; + var invoker = { + eventSeq: [], + + invoke: aInvokeFunc, + finalCheck: function finalCheck() { + // dumpTree(aID, `'${aID}' tree:`); + + aChecker.check(); + et.unwrapNextTest(); // replace dummy invoker on real invoker object. + }, + getID: function getID() { + return aInvokerID; + }, + }; + + if (aRemoveTriple) { + let checker = new textChangeChecker( + aID, + aRemoveTriple[0], + aRemoveTriple[1], + aRemoveTriple[2], + false + ); + invoker.eventSeq.push(checker); + } + + if (aInsertTriple) { + let checker = new textChangeChecker( + aID, + aInsertTriple[0], + aInsertTriple[1], + aInsertTriple[2], + true + ); + invoker.eventSeq.push(checker); + } + + // Claim that we don't want to fail when no events are expected. + if (!aRemoveTriple && !aInsertTriple) { + invoker.noEventsOnAction = true; + } + + this.mEventQueue.mInvokers[this.mEventQueue.mIndex + 1] = invoker; + }; + + /** + * Run the tests. + */ + this.startTest = function startTest(aTestRun) { + var testRunObj = aTestRun; + var thisObj = this; + this.mEventQueue.onFinish = function finishCallback() { + // Notify textRun object that all tests were finished. + testRunObj.iterate(); + + // Help GC to avoid leaks (refer to aTestRun from local variable, drop + // onFinish function). + thisObj.mEventQueue.onFinish = null; + + return DO_NOT_FINISH_TEST; + }; + + this.mEventQueue.invoke(); + }; + + this.mEventQueue = new eventQueue(); + this.mEventQueueReady = false; +} diff --git a/accessible/tests/mochitest/editabletext/test_1.html b/accessible/tests/mochitest/editabletext/test_1.html new file mode 100644 index 0000000000..10b5719374 --- /dev/null +++ b/accessible/tests/mochitest/editabletext/test_1.html @@ -0,0 +1,140 @@ + + + + + nsIAccessibleEditableText chrome tests + + + + + + + + + + + + + + + Bug 452161 + + + Bug 626660 + + + Bug 1105611 + +

    + +
    +  
    + + + +
    +
    +
    +
    + + + +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    + + +
    +
    Not logo
    +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    +
    +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    +
    +
    + +
    +
    Not logo
    +
    + +
    +
    Not logo
    +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    +
    +

    heading1

    +

    heading2

    +

    heading3

    +

    heading4

    +
    heading5
    +
    heading6
    +
    +
    +
    +
    Not logo
    +
    +
    +
    Not logo
    +
    + +
    Not logo
    +
    +
    +
    Not logo
    +
    +
    +
    Not logo
    +
    +
    +
    Not logo
    +
    + +
    +

    normalitalic

    + + + + + + + + +
    + +
    + +
    + +
    + + +
    + + + + + +
    + + + + + + + + + +
    + +
    + + + +

    normalInserted

    +

    normalcmd

    + + + + + +
      +
    • item1
    • +
    +
      +
    1. item1
    2. +
    + +
    main
    + + + b + a + + + + +

    + [Bypass navigation bar] + [Home] +

    +
    + +

    normalhighlighted

    + + + + + + a + 2 + + + + + b + 2 + + + = + + c + 2 + + + + + +
  5. Home
  6. +
  7. About + +
  8. Our Story
  9. +
    +
  10. +
    + + +
  11. + + +
  12. + +
  13. +
    +
  14. +
    + + 200 Euro + + + + + + + + + + + + + + + + + + + + + + + + +
    pre
    + + + + + + Oh my God, they killed Kenny! + + + + 漢 (Kan) + 字 (ji) + + +

    normalstriked

    +

    normalsample

    +
    section
    +
    named section
    +

    normalsmall

    + + +

    normalstrong

    +

    normalsub

    +

    normalsup

    + + + + +

    The concert took place on

    +

    normalunderline

    +

    An equation: x = y

    + + + + + + diff --git a/accessible/tests/mochitest/elm/test_MathMLSpec.html b/accessible/tests/mochitest/elm/test_MathMLSpec.html new file mode 100644 index 0000000000..c711acf05f --- /dev/null +++ b/accessible/tests/mochitest/elm/test_MathMLSpec.html @@ -0,0 +1,617 @@ + + + + HTML a11y spec tests + + + + + + + + + + + + + + + + + + Mozilla Bug 658272 +
    +

    + +
    +  
    + + + + + + a + 2 + + + + + 2 + + + = + + c + 2 + + + + Arbitrary text + + InterpretedStringLiteral + + + + + x + 2 + + + x + 5 + + + + + x + y + + + + [ + + X + , + Y + + closing-fence + + + + a + + + b + + + + x + + + + y + + + + + + b + 1 + 2 + + + + x + + + y + + + z + + + + + + x + ^ + + + + 0 + + + + R + i + + + j + k + + l + + + + + + + (2.1) + + + + E + = + + m + + + c + 2 + + + + + + + + ( + + + 1 + 0 + 0 + + + 0 + 1 + 0 + + + 0 + 0 + 1 + + + ) + + + + + 6 + 8 + + + + 3 + + 2 + + + 4 + + 2 + + + + 3 + 4 + + + + + + Division by zero: + + 1 + 0 + + + + + + + + + x + 2 + + + + y + + + + + + + + x + 2 + + y + + + + + x^{2} + y + + + + + + + + 1 + + + 1 + + + 523 + + - + + 15 + + + 508 + + + + 5 + 1 + 5 + + + + + 123 + ×321 + + + + 123 + 246 + 369 + + + + + + + diff --git a/accessible/tests/mochitest/elm/test_canvas.html b/accessible/tests/mochitest/elm/test_canvas.html new file mode 100644 index 0000000000..65d6d4bca4 --- /dev/null +++ b/accessible/tests/mochitest/elm/test_canvas.html @@ -0,0 +1,55 @@ + + + + Accessible boundaries for hit regions + + + + + + + + + + + + + + + + + + + diff --git a/accessible/tests/mochitest/elm/test_figure.html b/accessible/tests/mochitest/elm/test_figure.html new file mode 100644 index 0000000000..82ac961e36 --- /dev/null +++ b/accessible/tests/mochitest/elm/test_figure.html @@ -0,0 +1,60 @@ + + + + HTML5 figure/figcaption tests + + + + + + + + + + + + + + + + Mozilla Bug 658272 +
    +

    + +
    +  
    + +
    +
    figure caption
    +
    + + + diff --git a/accessible/tests/mochitest/elm/test_listbox.xhtml b/accessible/tests/mochitest/elm/test_listbox.xhtml new file mode 100644 index 0000000000..2315959e3a --- /dev/null +++ b/accessible/tests/mochitest/elm/test_listbox.xhtml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + Mozilla Bug 418371 + +

    + +
    +    
    + + + + +
    + +
    + diff --git a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html new file mode 100644 index 0000000000..2e7aabf882 --- /dev/null +++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html @@ -0,0 +1,67 @@ + + + + application accessible name + + + + + + + + + + + Mozilla Bug 454211 + + +

    + +
    +  
    + + + diff --git a/accessible/tests/mochitest/elm/test_shadowroot.html b/accessible/tests/mochitest/elm/test_shadowroot.html new file mode 100644 index 0000000000..bc221090b4 --- /dev/null +++ b/accessible/tests/mochitest/elm/test_shadowroot.html @@ -0,0 +1,35 @@ + + + + ShadowRoot tests + + + + + + + + + Mozilla Bug 1026125 +
    +

    + +
    +  
    + + + + + diff --git a/accessible/tests/mochitest/elm/test_shadowroot_subframe.html b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html new file mode 100644 index 0000000000..fe158f2cf4 --- /dev/null +++ b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html @@ -0,0 +1,68 @@ + + + + ShadowRoot tests + + + + + + + + +
    +
    + + + diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js new file mode 100644 index 0000000000..2d5e3d9e38 --- /dev/null +++ b/accessible/tests/mochitest/events.js @@ -0,0 +1,2631 @@ +/* import-globals-from common.js */ +/* import-globals-from states.js */ +/* import-globals-from text.js */ + +// XXX Bug 1425371 - enable no-redeclare and fix the issues with the tests. +/* eslint-disable no-redeclare */ + +// ////////////////////////////////////////////////////////////////////////////// +// Constants + +const EVENT_ALERT = nsIAccessibleEvent.EVENT_ALERT; +const EVENT_ANNOUNCEMENT = nsIAccessibleEvent.EVENT_ANNOUNCEMENT; +const EVENT_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE; +const EVENT_DOCUMENT_LOAD_COMPLETE = + nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE; +const EVENT_DOCUMENT_RELOAD = nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD; +const EVENT_DOCUMENT_LOAD_STOPPED = + nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED; +const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE; +const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS; +const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE; +const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START; +const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END; +const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START; +const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END; +const EVENT_OBJECT_ATTRIBUTE_CHANGED = + nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED; +const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER; +const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START; +const EVENT_SELECTION = nsIAccessibleEvent.EVENT_SELECTION; +const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD; +const EVENT_SELECTION_REMOVE = nsIAccessibleEvent.EVENT_SELECTION_REMOVE; +const EVENT_SELECTION_WITHIN = nsIAccessibleEvent.EVENT_SELECTION_WITHIN; +const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW; +const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE; +const EVENT_TEXT_ATTRIBUTE_CHANGED = + nsIAccessibleEvent.EVENT_TEXT_ATTRIBUTE_CHANGED; +const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED; +const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED; +const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED; +const EVENT_TEXT_SELECTION_CHANGED = + nsIAccessibleEvent.EVENT_TEXT_SELECTION_CHANGED; +const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE; +const EVENT_TEXT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_TEXT_VALUE_CHANGE; +const EVENT_VIRTUALCURSOR_CHANGED = + nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED; + +const kNotFromUserInput = 0; +const kFromUserInput = 1; + +// ////////////////////////////////////////////////////////////////////////////// +// General + +var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +/** + * Set up this variable to dump events into DOM. + */ +var gA11yEventDumpID = ""; + +/** + * Set up this variable to dump event processing into console. + */ +var gA11yEventDumpToConsole = false; + +/** + * Set up this variable to dump event processing into error console. + */ +var gA11yEventDumpToAppConsole = false; + +/** + * Semicolon separated set of logging features. + */ +var gA11yEventDumpFeature = ""; + +/** + * Function to detect HTML elements when given a node. + */ +function isHTMLElement(aNode) { + return ( + aNode.nodeType == aNode.ELEMENT_NODE && + aNode.namespaceURI == "http://www.w3.org/1999/xhtml" + ); +} + +function isXULElement(aNode) { + return ( + aNode.nodeType == aNode.ELEMENT_NODE && + aNode.namespaceURI == + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + ); +} + +/** + * Executes the function when requested event is handled. + * + * @param aEventType [in] event type + * @param aTarget [in] event target + * @param aFunc [in] function to call when event is handled + * @param aContext [in, optional] object in which context the function is + * called + * @param aArg1 [in, optional] argument passed into the function + * @param aArg2 [in, optional] argument passed into the function + */ +function waitForEvent( + aEventType, + aTargetOrFunc, + aFunc, + aContext, + aArg1, + aArg2 +) { + var handler = { + handleEvent: function handleEvent(aEvent) { + var target = aTargetOrFunc; + if (typeof aTargetOrFunc == "function") { + target = aTargetOrFunc.call(); + } + + if (target) { + if (target instanceof nsIAccessible && target != aEvent.accessible) { + return; + } + + if (Node.isInstance(target) && target != aEvent.DOMNode) { + return; + } + } + + unregisterA11yEventListener(aEventType, this); + + window.setTimeout(function() { + aFunc.call(aContext, aArg1, aArg2); + }, 0); + }, + }; + + registerA11yEventListener(aEventType, handler); +} + +/** + * Generate mouse move over image map what creates image map accessible (async). + * See waitForImageMap() function. + */ +function waveOverImageMap(aImageMapID) { + var imageMapNode = getNode(aImageMapID); + synthesizeMouse( + imageMapNode, + 10, + 10, + { type: "mousemove" }, + imageMapNode.ownerGlobal + ); +} + +/** + * Call the given function when the tree of the given image map is built. + */ +function waitForImageMap(aImageMapID, aTestFunc) { + waveOverImageMap(aImageMapID); + + var imageMapAcc = getAccessible(aImageMapID); + if (imageMapAcc.firstChild) { + aTestFunc(); + return; + } + + waitForEvent(EVENT_REORDER, imageMapAcc, aTestFunc); +} + +/** + * Register accessibility event listener. + * + * @param aEventType the accessible event type (see nsIAccessibleEvent for + * available constants). + * @param aEventHandler event listener object, when accessible event of the + * given type is handled then 'handleEvent' method of + * this object is invoked with nsIAccessibleEvent object + * as the first argument. + */ +function registerA11yEventListener(aEventType, aEventHandler) { + listenA11yEvents(true); + addA11yEventListener(aEventType, aEventHandler); +} + +/** + * Unregister accessibility event listener. Must be called for every registered + * event listener (see registerA11yEventListener() function) when the listener + * is not needed. + */ +function unregisterA11yEventListener(aEventType, aEventHandler) { + removeA11yEventListener(aEventType, aEventHandler); + listenA11yEvents(false); +} + +// ////////////////////////////////////////////////////////////////////////////// +// Event queue + +/** + * Return value of invoke method of invoker object. Indicates invoker was unable + * to prepare action. + */ +const INVOKER_ACTION_FAILED = 1; + +/** + * Return value of eventQueue.onFinish. Indicates eventQueue should not finish + * tests. + */ +const DO_NOT_FINISH_TEST = 1; + +/** + * Creates event queue for the given event type. The queue consists of invoker + * objects, each of them generates the event of the event type. When queue is + * started then every invoker object is asked to generate event after timeout. + * When event is caught then current invoker object is asked to check whether + * event was handled correctly. + * + * Invoker interface is: + * + * var invoker = { + * // Generates accessible event or event sequence. If returns + * // INVOKER_ACTION_FAILED constant then stop tests. + * invoke: function(){}, + * + * // [optional] Invoker's check of handled event for correctness. + * check: function(aEvent){}, + * + * // [optional] Invoker's check before the next invoker is proceeded. + * finalCheck: function(aEvent){}, + * + * // [optional] Is called when event of any registered type is handled. + * debugCheck: function(aEvent){}, + * + * // [ignored if 'eventSeq' is defined] DOM node event is generated for + * // (used in the case when invoker expects single event). + * DOMNode getter: function() {}, + * + * // [optional] if true then event sequences are ignored (no failure if + * // sequences are empty). Use you need to invoke an action, do some check + * // after timeout and proceed a next invoker. + * noEventsOnAction getter: function() {}, + * + * // Array of checker objects defining expected events on invoker's action. + * // + * // Checker object interface: + * // + * // var checker = { + * // * DOM or a11y event type. * + * // type getter: function() {}, + * // + * // * DOM node or accessible. * + * // target getter: function() {}, + * // + * // * DOM event phase (false - bubbling). * + * // phase getter: function() {}, + * // + * // * Callback, called to match handled event. * + * // match : function(aEvent) {}, + * // + * // * Callback, called when event is handled + * // check: function(aEvent) {}, + * // + * // * Checker ID * + * // getID: function() {}, + * // + * // * Event that don't have predefined order relative other events. * + * // async getter: function() {}, + * // + * // * Event that is not expected. * + * // unexpected getter: function() {}, + * // + * // * No other event of the same type is not allowed. * + * // unique getter: function() {} + * // }; + * eventSeq getter() {}, + * + * // Array of checker objects defining unexpected events on invoker's + * // action. + * unexpectedEventSeq getter() {}, + * + * // The ID of invoker. + * getID: function(){} // returns invoker ID + * }; + * + * // Used to add a possible scenario of expected/unexpected events on + * // invoker's action. + * defineScenario(aInvokerObj, aEventSeq, aUnexpectedEventSeq) + * + * + * @param aEventType [in, optional] the default event type (isn't used if + * invoker defines eventSeq property). + */ +function eventQueue(aEventType) { + // public + + /** + * Add invoker object into queue. + */ + this.push = function eventQueue_push(aEventInvoker) { + this.mInvokers.push(aEventInvoker); + }; + + /** + * Start the queue processing. + */ + this.invoke = function eventQueue_invoke() { + listenA11yEvents(true); + + // XXX: Intermittent test_events_caretmove.html fails withouth timeout, + // see bug 474952. + this.processNextInvokerInTimeout(true); + }; + + /** + * This function is called when all events in the queue were handled. + * Override it if you need to be notified of this. + */ + this.onFinish = function eventQueue_finish() {}; + + // private + + /** + * Process next invoker. + */ + // eslint-disable-next-line complexity + this.processNextInvoker = function eventQueue_processNextInvoker() { + // Some scenario was matched, we wait on next invoker processing. + if (this.mNextInvokerStatus == kInvokerCanceled) { + this.setInvokerStatus( + kInvokerNotScheduled, + "scenario was matched, wait for next invoker activation" + ); + return; + } + + this.setInvokerStatus( + kInvokerNotScheduled, + "the next invoker is processed now" + ); + + // Finish processing of the current invoker if any. + var testFailed = false; + + var invoker = this.getInvoker(); + if (invoker) { + if ("finalCheck" in invoker) { + invoker.finalCheck(); + } + + if (this.mScenarios && this.mScenarios.length) { + var matchIdx = -1; + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + if (!this.areExpectedEventsLeft(eventSeq)) { + for (var idx = 0; idx < eventSeq.length; idx++) { + var checker = eventSeq[idx]; + if ( + (checker.unexpected && checker.wasCaught) || + (!checker.unexpected && checker.wasCaught != 1) + ) { + break; + } + } + + // Ok, we have matched scenario. Report it was completed ok. In + // case of empty scenario guess it was matched but if later we + // find out that non empty scenario was matched then it will be + // a final match. + if (idx == eventSeq.length) { + if ( + matchIdx != -1 && + eventSeq.length > 0 && + this.mScenarios[matchIdx].length > 0 + ) { + ok( + false, + "We have a matched scenario at index " + + matchIdx + + " already." + ); + } + + if (matchIdx == -1 || eventSeq.length > 0) { + matchIdx = scnIdx; + } + + // Report everything is ok. + for (var idx = 0; idx < eventSeq.length; idx++) { + var checker = eventSeq[idx]; + + var typeStr = eventQueue.getEventTypeAsString(checker); + var msg = + "Test with ID = '" + this.getEventID(checker) + "' succeed. "; + + if (checker.unexpected) { + ok(true, msg + `There's no unexpected '${typeStr}' event.`); + } else if (checker.todo) { + todo(false, `Todo event '${typeStr}' was caught`); + } else { + ok(true, `${msg} Event '${typeStr}' was handled.`); + } + } + } + } + } + + // We don't have completely matched scenario. Report each failure/success + // for every scenario. + if (matchIdx == -1) { + testFailed = true; + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + for (var idx = 0; idx < eventSeq.length; idx++) { + var checker = eventSeq[idx]; + + var typeStr = eventQueue.getEventTypeAsString(checker); + var msg = + "Scenario #" + + scnIdx + + " of test with ID = '" + + this.getEventID(checker) + + "' failed. "; + + if (checker.wasCaught > 1) { + ok(false, msg + "Dupe " + typeStr + " event."); + } + + if (checker.unexpected) { + if (checker.wasCaught) { + ok(false, msg + "There's unexpected " + typeStr + " event."); + } + } else if (!checker.wasCaught) { + var rf = checker.todo ? todo : ok; + rf(false, `${msg} '${typeStr} event is missed.`); + } + } + } + } + } + } + + this.clearEventHandler(); + + // Check if need to stop the test. + if (testFailed || this.mIndex == this.mInvokers.length - 1) { + listenA11yEvents(false); + + var res = this.onFinish(); + if (res != DO_NOT_FINISH_TEST) { + SimpleTest.executeSoon(SimpleTest.finish); + } + + return; + } + + // Start processing of next invoker. + invoker = this.getNextInvoker(); + + // Set up event listeners. Process a next invoker if no events were added. + if (!this.setEventHandler(invoker)) { + this.processNextInvoker(); + return; + } + + if (gLogger.isEnabled()) { + gLogger.logToConsole("Event queue: \n invoke: " + invoker.getID()); + gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true); + } + + var infoText = "Invoke the '" + invoker.getID() + "' test { "; + var scnCount = this.mScenarios ? this.mScenarios.length : 0; + for (var scnIdx = 0; scnIdx < scnCount; scnIdx++) { + infoText += "scenario #" + scnIdx + ": "; + var eventSeq = this.mScenarios[scnIdx]; + for (var idx = 0; idx < eventSeq.length; idx++) { + infoText += eventSeq[idx].unexpected + ? "un" + : "" + + "expected '" + + eventQueue.getEventTypeAsString(eventSeq[idx]) + + "' event; "; + } + } + infoText += " }"; + info(infoText); + + if (invoker.invoke() == INVOKER_ACTION_FAILED) { + // Invoker failed to prepare action, fail and finish tests. + this.processNextInvoker(); + return; + } + + if (this.hasUnexpectedEventsScenario()) { + this.processNextInvokerInTimeout(true); + } + }; + + this.processNextInvokerInTimeout = function eventQueue_processNextInvokerInTimeout( + aUncondProcess + ) { + this.setInvokerStatus(kInvokerPending, "Process next invoker in timeout"); + + // No need to wait extra timeout when a) we know we don't need to do that + // and b) there's no any single unexpected event. + if (!aUncondProcess && this.areAllEventsExpected()) { + // We need delay to avoid events coalesce from different invokers. + var queue = this; + SimpleTest.executeSoon(function() { + queue.processNextInvoker(); + }); + return; + } + + // Check in timeout invoker didn't fire registered events. + window.setTimeout( + function(aQueue) { + aQueue.processNextInvoker(); + }, + 300, + this + ); + }; + + /** + * Handle events for the current invoker. + */ + // eslint-disable-next-line complexity + this.handleEvent = function eventQueue_handleEvent(aEvent) { + var invoker = this.getInvoker(); + if (!invoker) { + // skip events before test was started + return; + } + + if (!this.mScenarios) { + // Bad invoker object, error will be reported before processing of next + // invoker in the queue. + this.processNextInvoker(); + return; + } + + if ("debugCheck" in invoker) { + invoker.debugCheck(aEvent); + } + + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + for (var idx = 0; idx < eventSeq.length; idx++) { + var checker = eventSeq[idx]; + + // Search through handled expected events to report error if one of them + // is handled for a second time. + if ( + !checker.unexpected && + checker.wasCaught > 0 && + eventQueue.isSameEvent(checker, aEvent) + ) { + checker.wasCaught++; + continue; + } + + // Search through unexpected events, any match results in error report + // after this invoker processing (in case of matched scenario only). + if (checker.unexpected && eventQueue.compareEvents(checker, aEvent)) { + checker.wasCaught++; + continue; + } + + // Report an error if we hanlded not expected event of unique type + // (i.e. event types are matched, targets differs). + if ( + !checker.unexpected && + checker.unique && + eventQueue.compareEventTypes(checker, aEvent) + ) { + var isExpected = false; + for (var jdx = 0; jdx < eventSeq.length; jdx++) { + isExpected = eventQueue.compareEvents(eventSeq[jdx], aEvent); + if (isExpected) { + break; + } + } + + if (!isExpected) { + ok( + false, + "Unique type " + + eventQueue.getEventTypeAsString(checker) + + " event was handled." + ); + } + } + } + } + + var hasMatchedCheckers = false; + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + + // Check if handled event matches expected sync event. + var nextChecker = this.getNextExpectedEvent(eventSeq); + if (nextChecker) { + if (eventQueue.compareEvents(nextChecker, aEvent)) { + this.processMatchedChecker(aEvent, nextChecker, scnIdx, eventSeq.idx); + hasMatchedCheckers = true; + continue; + } + } + + // Check if handled event matches any expected async events. + var haveUnmatchedAsync = false; + for (idx = 0; idx < eventSeq.length; idx++) { + if (eventSeq[idx] instanceof orderChecker && haveUnmatchedAsync) { + break; + } + + if (!eventSeq[idx].wasCaught) { + haveUnmatchedAsync = true; + } + + if (!eventSeq[idx].unexpected && eventSeq[idx].async) { + if (eventQueue.compareEvents(eventSeq[idx], aEvent)) { + this.processMatchedChecker(aEvent, eventSeq[idx], scnIdx, idx); + hasMatchedCheckers = true; + break; + } + } + } + } + + if (hasMatchedCheckers) { + var invoker = this.getInvoker(); + if ("check" in invoker) { + invoker.check(aEvent); + } + } + + for (idx = 0; idx < eventSeq.length; idx++) { + if (!eventSeq[idx].wasCaught) { + if (eventSeq[idx] instanceof orderChecker) { + eventSeq[idx].wasCaught++; + } else { + break; + } + } + } + + // If we don't have more events to wait then schedule next invoker. + if (this.hasMatchedScenario()) { + if (this.mNextInvokerStatus == kInvokerNotScheduled) { + this.processNextInvokerInTimeout(); + } else if (this.mNextInvokerStatus == kInvokerCanceled) { + this.setInvokerStatus( + kInvokerPending, + "Full match. Void the cancelation of next invoker processing" + ); + } + return; + } + + // If we have scheduled a next invoker then cancel in case of match. + if (this.mNextInvokerStatus == kInvokerPending && hasMatchedCheckers) { + this.setInvokerStatus( + kInvokerCanceled, + "Cancel the scheduled invoker in case of match" + ); + } + }; + + // Helpers + this.processMatchedChecker = function eventQueue_function( + aEvent, + aMatchedChecker, + aScenarioIdx, + aEventIdx + ) { + aMatchedChecker.wasCaught++; + + if ("check" in aMatchedChecker) { + aMatchedChecker.check(aEvent); + } + + eventQueue.logEvent( + aEvent, + aMatchedChecker, + aScenarioIdx, + aEventIdx, + this.areExpectedEventsLeft(), + this.mNextInvokerStatus + ); + }; + + this.getNextExpectedEvent = function eventQueue_getNextExpectedEvent( + aEventSeq + ) { + if (!("idx" in aEventSeq)) { + aEventSeq.idx = 0; + } + + while ( + aEventSeq.idx < aEventSeq.length && + (aEventSeq[aEventSeq.idx].unexpected || + aEventSeq[aEventSeq.idx].todo || + aEventSeq[aEventSeq.idx].async || + aEventSeq[aEventSeq.idx] instanceof orderChecker || + aEventSeq[aEventSeq.idx].wasCaught > 0) + ) { + aEventSeq.idx++; + } + + return aEventSeq.idx != aEventSeq.length ? aEventSeq[aEventSeq.idx] : null; + }; + + this.areExpectedEventsLeft = function eventQueue_areExpectedEventsLeft( + aScenario + ) { + function scenarioHasUnhandledExpectedEvent(aEventSeq) { + // Check if we have unhandled async (can be anywhere in the sequance) or + // sync expcected events yet. + for (var idx = 0; idx < aEventSeq.length; idx++) { + if ( + !aEventSeq[idx].unexpected && + !aEventSeq[idx].todo && + !aEventSeq[idx].wasCaught && + !(aEventSeq[idx] instanceof orderChecker) + ) { + return true; + } + } + + return false; + } + + if (aScenario) { + return scenarioHasUnhandledExpectedEvent(aScenario); + } + + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + if (scenarioHasUnhandledExpectedEvent(eventSeq)) { + return true; + } + } + return false; + }; + + this.areAllEventsExpected = function eventQueue_areAllEventsExpected() { + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + for (var idx = 0; idx < eventSeq.length; idx++) { + if (eventSeq[idx].unexpected || eventSeq[idx].todo) { + return false; + } + } + } + + return true; + }; + + this.isUnexpectedEventScenario = function eventQueue_isUnexpectedEventsScenario( + aScenario + ) { + for (var idx = 0; idx < aScenario.length; idx++) { + if (!aScenario[idx].unexpected && !aScenario[idx].todo) { + break; + } + } + + return idx == aScenario.length; + }; + + this.hasUnexpectedEventsScenario = function eventQueue_hasUnexpectedEventsScenario() { + if (this.getInvoker().noEventsOnAction) { + return true; + } + + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + if (this.isUnexpectedEventScenario(this.mScenarios[scnIdx])) { + return true; + } + } + + return false; + }; + + this.hasMatchedScenario = function eventQueue_hasMatchedScenario() { + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var scn = this.mScenarios[scnIdx]; + if ( + !this.isUnexpectedEventScenario(scn) && + !this.areExpectedEventsLeft(scn) + ) { + return true; + } + } + return false; + }; + + this.getInvoker = function eventQueue_getInvoker() { + return this.mInvokers[this.mIndex]; + }; + + this.getNextInvoker = function eventQueue_getNextInvoker() { + return this.mInvokers[++this.mIndex]; + }; + + this.setEventHandler = function eventQueue_setEventHandler(aInvoker) { + if (!("scenarios" in aInvoker) || aInvoker.scenarios.length == 0) { + var eventSeq = aInvoker.eventSeq; + var unexpectedEventSeq = aInvoker.unexpectedEventSeq; + if (!eventSeq && !unexpectedEventSeq && this.mDefEventType) { + eventSeq = [new invokerChecker(this.mDefEventType, aInvoker.DOMNode)]; + } + + if (eventSeq || unexpectedEventSeq) { + defineScenario(aInvoker, eventSeq, unexpectedEventSeq); + } + } + + if (aInvoker.noEventsOnAction) { + return true; + } + + this.mScenarios = aInvoker.scenarios; + if (!this.mScenarios || !this.mScenarios.length) { + ok(false, "Broken invoker '" + aInvoker.getID() + "'"); + return false; + } + + // Register event listeners. + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + + if (gLogger.isEnabled()) { + var msg = + "scenario #" + + scnIdx + + ", registered events number: " + + eventSeq.length; + gLogger.logToConsole(msg); + gLogger.logToDOM(msg, true); + } + + // Do not warn about empty event sequances when more than one scenario + // was registered. + if (this.mScenarios.length == 1 && eventSeq.length == 0) { + ok( + false, + "Broken scenario #" + + scnIdx + + " of invoker '" + + aInvoker.getID() + + "'. No registered events" + ); + return false; + } + + for (var idx = 0; idx < eventSeq.length; idx++) { + eventSeq[idx].wasCaught = 0; + } + + for (var idx = 0; idx < eventSeq.length; idx++) { + if (gLogger.isEnabled()) { + var msg = "registered"; + if (eventSeq[idx].unexpected) { + msg += " unexpected"; + } + if (eventSeq[idx].async) { + msg += " async"; + } + + msg += + ": event type: " + + eventQueue.getEventTypeAsString(eventSeq[idx]) + + ", target: " + + eventQueue.getEventTargetDescr(eventSeq[idx], true); + + gLogger.logToConsole(msg); + gLogger.logToDOM(msg, true); + } + + var eventType = eventSeq[idx].type; + if (typeof eventType == "string") { + // DOM event + var target = eventQueue.getEventTarget(eventSeq[idx]); + if (!target) { + ok(false, "no target for DOM event!"); + return false; + } + var phase = eventQueue.getEventPhase(eventSeq[idx]); + target.addEventListener(eventType, this, phase); + } else { + // A11y event + addA11yEventListener(eventType, this); + } + } + } + + return true; + }; + + this.clearEventHandler = function eventQueue_clearEventHandler() { + if (!this.mScenarios) { + return; + } + + for (var scnIdx = 0; scnIdx < this.mScenarios.length; scnIdx++) { + var eventSeq = this.mScenarios[scnIdx]; + for (var idx = 0; idx < eventSeq.length; idx++) { + var eventType = eventSeq[idx].type; + if (typeof eventType == "string") { + // DOM event + var target = eventQueue.getEventTarget(eventSeq[idx]); + var phase = eventQueue.getEventPhase(eventSeq[idx]); + target.removeEventListener(eventType, this, phase); + } else { + // A11y event + removeA11yEventListener(eventType, this); + } + } + } + this.mScenarios = null; + }; + + this.getEventID = function eventQueue_getEventID(aChecker) { + if ("getID" in aChecker) { + return aChecker.getID(); + } + + var invoker = this.getInvoker(); + return invoker.getID(); + }; + + this.setInvokerStatus = function eventQueue_setInvokerStatus( + aStatus, + aLogMsg + ) { + this.mNextInvokerStatus = aStatus; + + // Uncomment it to debug invoker processing logic. + // gLogger.log(eventQueue.invokerStatusToMsg(aStatus, aLogMsg)); + }; + + this.mDefEventType = aEventType; + + this.mInvokers = []; + this.mIndex = -1; + this.mScenarios = null; + + this.mNextInvokerStatus = kInvokerNotScheduled; +} + +// ////////////////////////////////////////////////////////////////////////////// +// eventQueue static members and constants + +const kInvokerNotScheduled = 0; +const kInvokerPending = 1; +const kInvokerCanceled = 2; + +eventQueue.getEventTypeAsString = function eventQueue_getEventTypeAsString( + aEventOrChecker +) { + if (Event.isInstance(aEventOrChecker)) { + return aEventOrChecker.type; + } + + if (aEventOrChecker instanceof nsIAccessibleEvent) { + return eventTypeToString(aEventOrChecker.eventType); + } + + return typeof aEventOrChecker.type == "string" + ? aEventOrChecker.type + : eventTypeToString(aEventOrChecker.type); +}; + +eventQueue.getEventTargetDescr = function eventQueue_getEventTargetDescr( + aEventOrChecker, + aDontForceTarget +) { + if (Event.isInstance(aEventOrChecker)) { + return prettyName(aEventOrChecker.originalTarget); + } + + // XXXbz this block doesn't seem to be reachable... + if (Event.isInstance(aEventOrChecker)) { + return prettyName(aEventOrChecker.accessible); + } + + var descr = aEventOrChecker.targetDescr; + if (descr) { + return descr; + } + + if (aDontForceTarget) { + return "no target description"; + } + + var target = "target" in aEventOrChecker ? aEventOrChecker.target : null; + return prettyName(target); +}; + +eventQueue.getEventPhase = function eventQueue_getEventPhase(aChecker) { + return "phase" in aChecker ? aChecker.phase : true; +}; + +eventQueue.getEventTarget = function eventQueue_getEventTarget(aChecker) { + if ("eventTarget" in aChecker) { + switch (aChecker.eventTarget) { + case "element": + return aChecker.target; + case "document": + default: + return aChecker.target.ownerDocument; + } + } + return aChecker.target.ownerDocument; +}; + +eventQueue.compareEventTypes = function eventQueue_compareEventTypes( + aChecker, + aEvent +) { + var eventType = Event.isInstance(aEvent) ? aEvent.type : aEvent.eventType; + return aChecker.type == eventType; +}; + +eventQueue.compareEvents = function eventQueue_compareEvents(aChecker, aEvent) { + if (!eventQueue.compareEventTypes(aChecker, aEvent)) { + return false; + } + + // If checker provides "match" function then allow the checker to decide + // whether event is matched. + if ("match" in aChecker) { + return aChecker.match(aEvent); + } + + var target1 = aChecker.target; + if (target1 instanceof nsIAccessible) { + var target2 = Event.isInstance(aEvent) + ? getAccessible(aEvent.target) + : aEvent.accessible; + + return target1 == target2; + } + + // If original target isn't suitable then extend interface to support target + // (original target is used in test_elm_media.html). + var target2 = Event.isInstance(aEvent) + ? aEvent.originalTarget + : aEvent.DOMNode; + return target1 == target2; +}; + +eventQueue.isSameEvent = function eventQueue_isSameEvent(aChecker, aEvent) { + // We don't have stored info about handled event other than its type and + // target, thus we should filter text change and state change events since + // they may occur on the same element because of complex changes. + return ( + this.compareEvents(aChecker, aEvent) && + !(aEvent instanceof nsIAccessibleTextChangeEvent) && + !(aEvent instanceof nsIAccessibleStateChangeEvent) + ); +}; + +eventQueue.invokerStatusToMsg = function eventQueue_invokerStatusToMsg( + aInvokerStatus, + aMsg +) { + var msg = "invoker status: "; + switch (aInvokerStatus) { + case kInvokerNotScheduled: + msg += "not scheduled"; + break; + case kInvokerPending: + msg += "pending"; + break; + case kInvokerCanceled: + msg += "canceled"; + break; + } + + if (aMsg) { + msg += " (" + aMsg + ")"; + } + + return msg; +}; + +eventQueue.logEvent = function eventQueue_logEvent( + aOrigEvent, + aMatchedChecker, + aScenarioIdx, + aEventIdx, + aAreExpectedEventsLeft, + aInvokerStatus +) { + // Dump DOM event information. Skip a11y event since it is dumped by + // gA11yEventObserver. + if (Event.isInstance(aOrigEvent)) { + var info = "Event type: " + eventQueue.getEventTypeAsString(aOrigEvent); + info += ". Target: " + eventQueue.getEventTargetDescr(aOrigEvent); + gLogger.logToDOM(info); + } + + var infoMsg = + "unhandled expected events: " + + aAreExpectedEventsLeft + + ", " + + eventQueue.invokerStatusToMsg(aInvokerStatus); + + var currType = eventQueue.getEventTypeAsString(aMatchedChecker); + var currTargetDescr = eventQueue.getEventTargetDescr(aMatchedChecker); + var consoleMsg = + "*****\nScenario " + + aScenarioIdx + + ", event " + + aEventIdx + + " matched: " + + currType + + "\n" + + infoMsg + + "\n*****"; + gLogger.logToConsole(consoleMsg); + + var emphText = "matched "; + var msg = + "EQ event, type: " + + currType + + ", target: " + + currTargetDescr + + ", " + + infoMsg; + gLogger.logToDOM(msg, true, emphText); +}; + +// ////////////////////////////////////////////////////////////////////////////// +// Action sequence + +/** + * Deal with action sequence. Used when you need to execute couple of actions + * each after other one. + */ +function sequence() { + /** + * Append new sequence item. + * + * @param aProcessor [in] object implementing interface + * { + * // execute item action + * process: function() {}, + * // callback, is called when item was processed + * onProcessed: function() {} + * }; + * @param aEventType [in] event type of expected event on item action + * @param aTarget [in] event target of expected event on item action + * @param aItemID [in] identifier of item + */ + this.append = function sequence_append( + aProcessor, + aEventType, + aTarget, + aItemID + ) { + var item = new sequenceItem(aProcessor, aEventType, aTarget, aItemID); + this.items.push(item); + }; + + /** + * Process next sequence item. + */ + this.processNext = function sequence_processNext() { + this.idx++; + if (this.idx >= this.items.length) { + ok(false, "End of sequence: nothing to process!"); + SimpleTest.finish(); + return; + } + + this.items[this.idx].startProcess(); + }; + + this.items = []; + this.idx = -1; +} + +// ////////////////////////////////////////////////////////////////////////////// +// Event queue invokers + +/** + * Defines a scenario of expected/unexpected events. Each invoker can have + * one or more scenarios of events. Only one scenario must be completed. + */ +function defineScenario(aInvoker, aEventSeq, aUnexpectedEventSeq) { + if (!("scenarios" in aInvoker)) { + aInvoker.scenarios = []; + } + + // Create unified event sequence concatenating expected and unexpected + // events. + if (!aEventSeq) { + aEventSeq = []; + } + + for (var idx = 0; idx < aEventSeq.length; idx++) { + aEventSeq[idx].unexpected |= false; + aEventSeq[idx].async |= false; + } + + if (aUnexpectedEventSeq) { + for (var idx = 0; idx < aUnexpectedEventSeq.length; idx++) { + aUnexpectedEventSeq[idx].unexpected = true; + aUnexpectedEventSeq[idx].async = false; + } + + aEventSeq = aEventSeq.concat(aUnexpectedEventSeq); + } + + aInvoker.scenarios.push(aEventSeq); +} + +/** + * Invokers defined below take a checker object (or array of checker objects). + * An invoker listens for default event type registered in event queue object + * until its checker is provided. + * + * Note, checker object or array of checker objects is optional. + */ + +/** + * Click invoker. + */ +function synthClick(aNodeOrID, aCheckerOrEventSeq, aArgs) { + this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq); + + this.invoke = function synthClick_invoke() { + var targetNode = this.DOMNode; + if (targetNode.nodeType == targetNode.DOCUMENT_NODE) { + targetNode = this.DOMNode.body + ? this.DOMNode.body + : this.DOMNode.documentElement; + } + + // Scroll the node into view, otherwise synth click may fail. + if (isHTMLElement(targetNode)) { + targetNode.scrollIntoView(true); + } else if (isXULElement(targetNode)) { + var targetAcc = getAccessible(targetNode); + targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE); + } + + var x = 1, + y = 1; + if (aArgs && "where" in aArgs && aArgs.where == "right") { + if (isHTMLElement(targetNode)) { + x = targetNode.offsetWidth - 1; + } else if (isXULElement(targetNode)) { + x = targetNode.getBoundingClientRect().width - 1; + } + } + synthesizeMouse(targetNode, x, y, aArgs ? aArgs : {}); + }; + + this.finalCheck = function synthClick_finalCheck() { + // Scroll top window back. + window.top.scrollTo(0, 0); + }; + + this.getID = function synthClick_getID() { + return prettyName(aNodeOrID) + " click"; + }; +} + +/** + * Mouse move invoker. + */ +function synthMouseMove(aID, aCheckerOrEventSeq) { + this.__proto__ = new synthAction(aID, aCheckerOrEventSeq); + + this.invoke = function synthMouseMove_invoke() { + synthesizeMouse(this.DOMNode, 1, 1, { type: "mousemove" }); + synthesizeMouse(this.DOMNode, 2, 2, { type: "mousemove" }); + }; + + this.getID = function synthMouseMove_getID() { + return prettyName(aID) + " mouse move"; + }; +} + +/** + * General key press invoker. + */ +function synthKey(aNodeOrID, aKey, aArgs, aCheckerOrEventSeq) { + this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq); + + this.invoke = function synthKey_invoke() { + synthesizeKey(this.mKey, this.mArgs, this.mWindow); + }; + + this.getID = function synthKey_getID() { + var key = this.mKey; + switch (this.mKey) { + case "VK_TAB": + key = "tab"; + break; + case "VK_DOWN": + key = "down"; + break; + case "VK_UP": + key = "up"; + break; + case "VK_LEFT": + key = "left"; + break; + case "VK_RIGHT": + key = "right"; + break; + case "VK_HOME": + key = "home"; + break; + case "VK_END": + key = "end"; + break; + case "VK_ESCAPE": + key = "escape"; + break; + case "VK_RETURN": + key = "enter"; + break; + } + if (aArgs) { + if (aArgs.shiftKey) { + key += " shift"; + } + if (aArgs.ctrlKey) { + key += " ctrl"; + } + if (aArgs.altKey) { + key += " alt"; + } + } + return prettyName(aNodeOrID) + " '" + key + " ' key"; + }; + + this.mKey = aKey; + this.mArgs = aArgs ? aArgs : {}; + this.mWindow = aArgs ? aArgs.window : null; +} + +/** + * Tab key invoker. + */ +function synthTab(aNodeOrID, aCheckerOrEventSeq, aWindow) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_TAB", + { shiftKey: false, window: aWindow }, + aCheckerOrEventSeq + ); +} + +/** + * Shift tab key invoker. + */ +function synthShiftTab(aNodeOrID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_TAB", + { shiftKey: true }, + aCheckerOrEventSeq + ); +} + +/** + * Escape key invoker. + */ +function synthEscapeKey(aNodeOrID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_ESCAPE", + null, + aCheckerOrEventSeq + ); +} + +/** + * Down arrow key invoker. + */ +function synthDownKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_DOWN", + aArgs, + aCheckerOrEventSeq + ); +} + +/** + * Up arrow key invoker. + */ +function synthUpKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { + this.__proto__ = new synthKey(aNodeOrID, "VK_UP", aArgs, aCheckerOrEventSeq); +} + +/** + * Left arrow key invoker. + */ +function synthLeftKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_LEFT", + aArgs, + aCheckerOrEventSeq + ); +} + +/** + * Right arrow key invoker. + */ +function synthRightKey(aNodeOrID, aCheckerOrEventSeq, aArgs) { + this.__proto__ = new synthKey( + aNodeOrID, + "VK_RIGHT", + aArgs, + aCheckerOrEventSeq + ); +} + +/** + * Home key invoker. + */ +function synthHomeKey(aNodeOrID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey(aNodeOrID, "VK_HOME", null, aCheckerOrEventSeq); +} + +/** + * End key invoker. + */ +function synthEndKey(aNodeOrID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey(aNodeOrID, "VK_END", null, aCheckerOrEventSeq); +} + +/** + * Enter key invoker + */ +function synthEnterKey(aID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey(aID, "VK_RETURN", null, aCheckerOrEventSeq); +} + +/** + * Synth alt + down arrow to open combobox. + */ +function synthOpenComboboxKey(aID, aCheckerOrEventSeq) { + this.__proto__ = new synthDownKey(aID, aCheckerOrEventSeq, { altKey: true }); + + this.getID = function synthOpenComboboxKey_getID() { + return "open combobox (atl + down arrow) " + prettyName(aID); + }; +} + +/** + * Focus invoker. + */ +function synthFocus(aNodeOrID, aCheckerOrEventSeq) { + var checkerOfEventSeq = aCheckerOrEventSeq + ? aCheckerOrEventSeq + : new focusChecker(aNodeOrID); + this.__proto__ = new synthAction(aNodeOrID, checkerOfEventSeq); + + this.invoke = function synthFocus_invoke() { + if (this.DOMNode.editor) { + this.DOMNode.selectionStart = this.DOMNode.selectionEnd = this.DOMNode.value.length; + } + this.DOMNode.focus(); + }; + + this.getID = function synthFocus_getID() { + return prettyName(aNodeOrID) + " focus"; + }; +} + +/** + * Focus invoker. Focus the HTML body of content document of iframe. + */ +function synthFocusOnFrame(aNodeOrID, aCheckerOrEventSeq) { + var frameDoc = getNode(aNodeOrID).contentDocument; + var checkerOrEventSeq = aCheckerOrEventSeq + ? aCheckerOrEventSeq + : new focusChecker(frameDoc); + this.__proto__ = new synthAction(frameDoc, checkerOrEventSeq); + + this.invoke = function synthFocus_invoke() { + this.DOMNode.body.focus(); + }; + + this.getID = function synthFocus_getID() { + return prettyName(aNodeOrID) + " frame document focus"; + }; +} + +/** + * Change the current item when the widget doesn't have a focus. + */ +function changeCurrentItem(aID, aItemID) { + this.eventSeq = [new nofocusChecker()]; + + this.invoke = function changeCurrentItem_invoke() { + var controlNode = getNode(aID); + var itemNode = getNode(aItemID); + + // HTML + if (controlNode.localName == "input") { + if (controlNode.checked) { + this.reportError(); + } + + controlNode.checked = true; + return; + } + + if (controlNode.localName == "select") { + if (controlNode.selectedIndex == itemNode.index) { + this.reportError(); + } + + controlNode.selectedIndex = itemNode.index; + return; + } + + // XUL + if (controlNode.localName == "tree") { + if (controlNode.currentIndex == aItemID) { + this.reportError(); + } + + controlNode.currentIndex = aItemID; + return; + } + + if (controlNode.localName == "menulist") { + if (controlNode.selectedItem == itemNode) { + this.reportError(); + } + + controlNode.selectedItem = itemNode; + return; + } + + if (controlNode.currentItem == itemNode) { + ok( + false, + "Error in test: proposed current item is already current" + + prettyName(aID) + ); + } + + controlNode.currentItem = itemNode; + }; + + this.getID = function changeCurrentItem_getID() { + return "current item change for " + prettyName(aID); + }; + + this.reportError = function changeCurrentItem_reportError() { + ok( + false, + "Error in test: proposed current item '" + + aItemID + + "' is already current" + ); + }; +} + +/** + * Toggle top menu invoker. + */ +function toggleTopMenu(aID, aCheckerOrEventSeq) { + this.__proto__ = new synthKey(aID, "VK_ALT", null, aCheckerOrEventSeq); + + this.getID = function toggleTopMenu_getID() { + return "toggle top menu on " + prettyName(aID); + }; +} + +/** + * Context menu invoker. + */ +function synthContextMenu(aID, aCheckerOrEventSeq) { + this.__proto__ = new synthClick(aID, aCheckerOrEventSeq, { + button: 0, + type: "contextmenu", + }); + + this.getID = function synthContextMenu_getID() { + return "context menu on " + prettyName(aID); + }; +} + +/** + * Open combobox, autocomplete and etc popup, check expandable states. + */ +function openCombobox(aComboboxID) { + this.eventSeq = [ + new stateChangeChecker(STATE_EXPANDED, false, true, aComboboxID), + ]; + + this.invoke = function openCombobox_invoke() { + getNode(aComboboxID).focus(); + synthesizeKey("VK_DOWN", { altKey: true }); + }; + + this.getID = function openCombobox_getID() { + return "open combobox " + prettyName(aComboboxID); + }; +} + +/** + * Close combobox, autocomplete and etc popup, check expandable states. + */ +function closeCombobox(aComboboxID) { + this.eventSeq = [ + new stateChangeChecker(STATE_EXPANDED, false, false, aComboboxID), + ]; + + this.invoke = function closeCombobox_invoke() { + synthesizeKey("KEY_Escape"); + }; + + this.getID = function closeCombobox_getID() { + return "close combobox " + prettyName(aComboboxID); + }; +} + +/** + * Select all invoker. + */ +function synthSelectAll(aNodeOrID, aCheckerOrEventSeq) { + this.__proto__ = new synthAction(aNodeOrID, aCheckerOrEventSeq); + + this.invoke = function synthSelectAll_invoke() { + if (ChromeUtils.getClassName(this.DOMNode) === "HTMLInputElement") { + this.DOMNode.select(); + } else { + window.getSelection().selectAllChildren(this.DOMNode); + } + }; + + this.getID = function synthSelectAll_getID() { + return aNodeOrID + " selectall"; + }; +} + +/** + * Move the caret to the end of line. + */ +function moveToLineEnd(aID, aCaretOffset) { + if (MAC) { + this.__proto__ = new synthKey( + aID, + "VK_RIGHT", + { metaKey: true }, + new caretMoveChecker(aCaretOffset, true, aID) + ); + } else { + this.__proto__ = new synthEndKey( + aID, + new caretMoveChecker(aCaretOffset, true, aID) + ); + } + + this.getID = function moveToLineEnd_getID() { + return "move to line end in " + prettyName(aID); + }; +} + +/** + * Move the caret to the end of previous line if any. + */ +function moveToPrevLineEnd(aID, aCaretOffset) { + this.__proto__ = new synthAction( + aID, + new caretMoveChecker(aCaretOffset, true, aID) + ); + + this.invoke = function moveToPrevLineEnd_invoke() { + synthesizeKey("KEY_ArrowUp"); + + if (MAC) { + synthesizeKey("Key_ArrowRight", { metaKey: true }); + } else { + synthesizeKey("KEY_End"); + } + }; + + this.getID = function moveToPrevLineEnd_getID() { + return "move to previous line end in " + prettyName(aID); + }; +} + +/** + * Move the caret to begining of the line. + */ +function moveToLineStart(aID, aCaretOffset) { + if (MAC) { + this.__proto__ = new synthKey( + aID, + "VK_LEFT", + { metaKey: true }, + new caretMoveChecker(aCaretOffset, true, aID) + ); + } else { + this.__proto__ = new synthHomeKey( + aID, + new caretMoveChecker(aCaretOffset, true, aID) + ); + } + + this.getID = function moveToLineEnd_getID() { + return "move to line start in " + prettyName(aID); + }; +} + +/** + * Move the caret to begining of the text. + */ +function moveToTextStart(aID) { + if (MAC) { + this.__proto__ = new synthKey( + aID, + "VK_UP", + { metaKey: true }, + new caretMoveChecker(0, true, aID) + ); + } else { + this.__proto__ = new synthKey( + aID, + "VK_HOME", + { ctrlKey: true }, + new caretMoveChecker(0, true, aID) + ); + } + + this.getID = function moveToTextStart_getID() { + return "move to text start in " + prettyName(aID); + }; +} + +/** + * Move the caret in text accessible. + */ +function moveCaretToDOMPoint( + aID, + aDOMPointNodeID, + aDOMPointOffset, + aExpectedOffset, + aFocusTargetID, + aCheckFunc +) { + this.target = getAccessible(aID, [nsIAccessibleText]); + this.DOMPointNode = getNode(aDOMPointNodeID); + this.focus = aFocusTargetID ? getAccessible(aFocusTargetID) : null; + this.focusNode = this.focus ? this.focus.DOMNode : null; + + this.invoke = function moveCaretToDOMPoint_invoke() { + if (this.focusNode) { + this.focusNode.focus(); + } + + var selection = this.DOMPointNode.ownerGlobal.getSelection(); + var selRange = selection.getRangeAt(0); + selRange.setStart(this.DOMPointNode, aDOMPointOffset); + selRange.collapse(true); + + selection.removeRange(selRange); + selection.addRange(selRange); + }; + + this.getID = function moveCaretToDOMPoint_getID() { + return ( + "Set caret on " + + prettyName(aID) + + " at point: " + + prettyName(aDOMPointNodeID) + + " node with offset " + + aDOMPointOffset + ); + }; + + this.finalCheck = function moveCaretToDOMPoint_finalCheck() { + if (aCheckFunc) { + aCheckFunc.call(); + } + }; + + this.eventSeq = [new caretMoveChecker(aExpectedOffset, true, this.target)]; + + if (this.focus) { + this.eventSeq.push(new asyncInvokerChecker(EVENT_FOCUS, this.focus)); + } +} + +/** + * Set caret offset in text accessible. + */ +function setCaretOffset(aID, aOffset, aFocusTargetID) { + this.target = getAccessible(aID, [nsIAccessibleText]); + this.offset = aOffset == -1 ? this.target.characterCount : aOffset; + this.focus = aFocusTargetID ? getAccessible(aFocusTargetID) : null; + + this.invoke = function setCaretOffset_invoke() { + this.target.caretOffset = this.offset; + }; + + this.getID = function setCaretOffset_getID() { + return "Set caretOffset on " + prettyName(aID) + " at " + this.offset; + }; + + this.eventSeq = [new caretMoveChecker(this.offset, true, this.target)]; + + if (this.focus) { + this.eventSeq.push(new asyncInvokerChecker(EVENT_FOCUS, this.focus)); + } +} + +// ////////////////////////////////////////////////////////////////////////////// +// Event queue checkers + +/** + * Common invoker checker (see eventSeq of eventQueue). + */ +function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync) { + this.type = aEventType; + this.async = aIsAsync; + + this.__defineGetter__("target", invokerChecker_targetGetter); + this.__defineSetter__("target", invokerChecker_targetSetter); + + // implementation details + function invokerChecker_targetGetter() { + if (typeof this.mTarget == "function") { + return this.mTarget.call(null, this.mTargetFuncArg); + } + if (typeof this.mTarget == "string") { + return getNode(this.mTarget); + } + + return this.mTarget; + } + + function invokerChecker_targetSetter(aValue) { + this.mTarget = aValue; + return this.mTarget; + } + + this.__defineGetter__("targetDescr", invokerChecker_targetDescrGetter); + + function invokerChecker_targetDescrGetter() { + if (typeof this.mTarget == "function") { + return this.mTarget.name + ", arg: " + this.mTargetFuncArg; + } + + return prettyName(this.mTarget); + } + + this.mTarget = aTargetOrFunc; + this.mTargetFuncArg = aTargetFuncArg; +} + +/** + * event checker that forces preceeding async events to happen before this + * checker. + */ +function orderChecker() { + // XXX it doesn't actually work to inherit from invokerChecker, but maybe we + // should fix that? + // this.__proto__ = new invokerChecker(null, null, null, false); +} + +/** + * Generic invoker checker for todo events. + */ +function todo_invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new invokerChecker( + aEventType, + aTargetOrFunc, + aTargetFuncArg, + true + ); + this.todo = true; +} + +/** + * Generic invoker checker for unexpected events. + */ +function unexpectedInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new invokerChecker( + aEventType, + aTargetOrFunc, + aTargetFuncArg, + true + ); + + this.unexpected = true; +} + +/** + * Common invoker checker for async events. + */ +function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new invokerChecker( + aEventType, + aTargetOrFunc, + aTargetFuncArg, + true + ); +} + +function focusChecker(aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new invokerChecker( + EVENT_FOCUS, + aTargetOrFunc, + aTargetFuncArg, + false + ); + + this.unique = true; // focus event must be unique for invoker action + + this.check = function focusChecker_check(aEvent) { + testStates(aEvent.accessible, STATE_FOCUSED); + }; +} + +function nofocusChecker(aID) { + this.__proto__ = new focusChecker(aID); + this.unexpected = true; +} + +/** + * Text inserted/removed events checker. + * @param aFromUser [in, optional] kNotFromUserInput or kFromUserInput + */ +function textChangeChecker( + aID, + aStart, + aEnd, + aTextOrFunc, + aIsInserted, + aFromUser, + aAsync +) { + this.target = getNode(aID); + this.type = aIsInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED; + this.startOffset = aStart; + this.endOffset = aEnd; + this.textOrFunc = aTextOrFunc; + this.async = aAsync; + + this.match = function stextChangeChecker_match(aEvent) { + if ( + !(aEvent instanceof nsIAccessibleTextChangeEvent) || + aEvent.accessible !== getAccessible(this.target) + ) { + return false; + } + + let tcEvent = aEvent.QueryInterface(nsIAccessibleTextChangeEvent); + let modifiedText = + typeof this.textOrFunc === "function" + ? this.textOrFunc() + : this.textOrFunc; + return modifiedText === tcEvent.modifiedText; + }; + + this.check = function textChangeChecker_check(aEvent) { + aEvent.QueryInterface(nsIAccessibleTextChangeEvent); + + var modifiedText = + typeof this.textOrFunc == "function" + ? this.textOrFunc() + : this.textOrFunc; + var modifiedTextLen = + this.endOffset == -1 ? modifiedText.length : aEnd - aStart; + + is( + aEvent.start, + this.startOffset, + "Wrong start offset for " + prettyName(aID) + ); + is(aEvent.length, modifiedTextLen, "Wrong length for " + prettyName(aID)); + var changeInfo = aIsInserted ? "inserted" : "removed"; + is( + aEvent.isInserted, + aIsInserted, + "Text was " + changeInfo + " for " + prettyName(aID) + ); + is( + aEvent.modifiedText, + modifiedText, + "Wrong " + changeInfo + " text for " + prettyName(aID) + ); + if (typeof aFromUser != "undefined") { + is( + aEvent.isFromUserInput, + aFromUser, + "wrong value of isFromUserInput() for " + prettyName(aID) + ); + } + }; +} + +/** + * Caret move events checker. + */ +function caretMoveChecker( + aCaretOffset, + aIsSelectionCollapsed, + aTargetOrFunc, + aTargetFuncArg, + aIsAsync +) { + this.__proto__ = new invokerChecker( + EVENT_TEXT_CARET_MOVED, + aTargetOrFunc, + aTargetFuncArg, + aIsAsync + ); + + this.check = function caretMoveChecker_check(aEvent) { + let evt = aEvent.QueryInterface(nsIAccessibleCaretMoveEvent); + is( + evt.caretOffset, + aCaretOffset, + "Wrong caret offset for " + prettyName(aEvent.accessible) + ); + is( + evt.isSelectionCollapsed, + aIsSelectionCollapsed, + "wrong collapsed value for " + prettyName(aEvent.accessible) + ); + }; +} + +function asyncCaretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new caretMoveChecker( + aCaretOffset, + true, // Caret is collapsed + aTargetOrFunc, + aTargetFuncArg, + true + ); +} + +/** + * Text selection change checker. + */ +function textSelectionChecker( + aID, + aStartOffset, + aEndOffset, + aRangeStartContainer, + aRangeStartOffset, + aRangeEndContainer, + aRangeEndOffset +) { + this.__proto__ = new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID); + + this.check = function textSelectionChecker_check(aEvent) { + if (aStartOffset == aEndOffset) { + ok(true, "Collapsed selection triggered text selection change event."); + } else { + testTextGetSelection(aID, aStartOffset, aEndOffset, 0); + + // Test selection test range + let selectionRanges = aEvent.QueryInterface( + nsIAccessibleTextSelectionChangeEvent + ).selectionRanges; + let range = selectionRanges.queryElementAt(0, nsIAccessibleTextRange); + is( + range.startContainer, + getAccessible(aRangeStartContainer), + "correct range start container" + ); + is(range.startOffset, aRangeStartOffset, "correct range start offset"); + is(range.endOffset, aRangeEndOffset, "correct range end offset"); + is( + range.endContainer, + getAccessible(aRangeEndContainer), + "correct range end container" + ); + } + }; +} + +/** + * Object attribute changed checker + */ +function objAttrChangedChecker(aID, aAttr) { + this.__proto__ = new invokerChecker(EVENT_OBJECT_ATTRIBUTE_CHANGED, aID); + + this.check = function objAttrChangedChecker_check(aEvent) { + var event = null; + try { + var event = aEvent.QueryInterface( + nsIAccessibleObjectAttributeChangedEvent + ); + } catch (e) { + ok(false, "Object attribute changed event was expected"); + } + + if (!event) { + return; + } + + is( + event.changedAttribute, + aAttr, + "Wrong attribute name of the object attribute changed event." + ); + }; + + this.match = function objAttrChangedChecker_match(aEvent) { + if (aEvent instanceof nsIAccessibleObjectAttributeChangedEvent) { + var scEvent = aEvent.QueryInterface( + nsIAccessibleObjectAttributeChangedEvent + ); + return ( + aEvent.accessible == getAccessible(this.target) && + scEvent.changedAttribute == aAttr + ); + } + return false; + }; +} + +/** + * State change checker. + */ +function stateChangeChecker( + aState, + aIsExtraState, + aIsEnabled, + aTargetOrFunc, + aTargetFuncArg, + aIsAsync, + aSkipCurrentStateCheck +) { + this.__proto__ = new invokerChecker( + EVENT_STATE_CHANGE, + aTargetOrFunc, + aTargetFuncArg, + aIsAsync + ); + + this.check = function stateChangeChecker_check(aEvent) { + var event = null; + try { + var event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent); + } catch (e) { + ok(false, "State change event was expected"); + } + + if (!event) { + return; + } + + is( + event.isExtraState, + aIsExtraState, + "Wrong extra state bit of the statechange event." + ); + isState( + event.state, + aState, + aIsExtraState, + "Wrong state of the statechange event." + ); + is(event.isEnabled, aIsEnabled, "Wrong state of statechange event state"); + + if (aSkipCurrentStateCheck) { + todo(false, "State checking was skipped!"); + return; + } + + var state = aIsEnabled ? (aIsExtraState ? 0 : aState) : 0; + var extraState = aIsEnabled ? (aIsExtraState ? aState : 0) : 0; + var unxpdState = aIsEnabled ? 0 : aIsExtraState ? 0 : aState; + var unxpdExtraState = aIsEnabled ? 0 : aIsExtraState ? aState : 0; + testStates( + event.accessible, + state, + extraState, + unxpdState, + unxpdExtraState + ); + }; + + this.match = function stateChangeChecker_match(aEvent) { + if (aEvent instanceof nsIAccessibleStateChangeEvent) { + var scEvent = aEvent.QueryInterface(nsIAccessibleStateChangeEvent); + return ( + aEvent.accessible == getAccessible(this.target) && + scEvent.state == aState + ); + } + return false; + }; +} + +function asyncStateChangeChecker( + aState, + aIsExtraState, + aIsEnabled, + aTargetOrFunc, + aTargetFuncArg +) { + this.__proto__ = new stateChangeChecker( + aState, + aIsExtraState, + aIsEnabled, + aTargetOrFunc, + aTargetFuncArg, + true + ); +} + +/** + * Expanded state change checker. + */ +function expandedStateChecker(aIsEnabled, aTargetOrFunc, aTargetFuncArg) { + this.__proto__ = new invokerChecker( + EVENT_STATE_CHANGE, + aTargetOrFunc, + aTargetFuncArg + ); + + this.check = function expandedStateChecker_check(aEvent) { + var event = null; + try { + var event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent); + } catch (e) { + ok(false, "State change event was expected"); + } + + if (!event) { + return; + } + + is(event.state, STATE_EXPANDED, "Wrong state of the statechange event."); + is( + event.isExtraState, + false, + "Wrong extra state bit of the statechange event." + ); + is(event.isEnabled, aIsEnabled, "Wrong state of statechange event state"); + + testStates(event.accessible, aIsEnabled ? STATE_EXPANDED : STATE_COLLAPSED); + }; +} + +// ////////////////////////////////////////////////////////////////////////////// +// Event sequances (array of predefined checkers) + +/** + * Event seq for single selection change. + */ +function selChangeSeq(aUnselectedID, aSelectedID) { + if (!aUnselectedID) { + return [ + new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID), + new invokerChecker(EVENT_SELECTION, aSelectedID), + ]; + } + + // Return two possible scenarios: depending on widget type when selection is + // moved the the order of items that get selected and unselected may vary. + return [ + [ + new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID), + new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID), + new invokerChecker(EVENT_SELECTION, aSelectedID), + ], + [ + new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID), + new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID), + new invokerChecker(EVENT_SELECTION, aSelectedID), + ], + ]; +} + +/** + * Event seq for item removed form the selection. + */ +function selRemoveSeq(aUnselectedID) { + return [ + new stateChangeChecker(STATE_SELECTED, false, false, aUnselectedID), + new invokerChecker(EVENT_SELECTION_REMOVE, aUnselectedID), + ]; +} + +/** + * Event seq for item added to the selection. + */ +function selAddSeq(aSelectedID) { + return [ + new stateChangeChecker(STATE_SELECTED, false, true, aSelectedID), + new invokerChecker(EVENT_SELECTION_ADD, aSelectedID), + ]; +} + +// ////////////////////////////////////////////////////////////////////////////// +// Private implementation details. +// ////////////////////////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////////////////////////// +// General + +var gA11yEventListeners = {}; +var gA11yEventApplicantsCount = 0; + +var gA11yEventObserver = { + // eslint-disable-next-line complexity + observe: function observe(aSubject, aTopic, aData) { + if (aTopic != "accessible-event") { + return; + } + + var event; + try { + event = aSubject.QueryInterface(nsIAccessibleEvent); + } catch (ex) { + // After a test is aborted (i.e. timed out by the harness), this exception is soon triggered. + // Remove the leftover observer, otherwise it "leaks" to all the following tests. + Services.obs.removeObserver(this, "accessible-event"); + // Forward the exception, with added explanation. + throw new Error( + "[accessible/events.js, gA11yEventObserver.observe] This is expected " + + `if a previous test has been aborted... Initial exception was: [ ${ex} ]` + ); + } + var listenersArray = gA11yEventListeners[event.eventType]; + + var eventFromDumpArea = false; + if (gLogger.isEnabled()) { + // debug stuff + eventFromDumpArea = true; + + var target = event.DOMNode; + var dumpElm = gA11yEventDumpID + ? document.getElementById(gA11yEventDumpID) + : null; + + if (dumpElm) { + var parent = target; + while (parent && parent != dumpElm) { + parent = parent.parentNode; + } + } + + if (!dumpElm || parent != dumpElm) { + var type = eventTypeToString(event.eventType); + var info = "Event type: " + type; + + if (event instanceof nsIAccessibleStateChangeEvent) { + var stateStr = statesToString( + event.isExtraState ? 0 : event.state, + event.isExtraState ? event.state : 0 + ); + info += ", state: " + stateStr + ", is enabled: " + event.isEnabled; + } else if (event instanceof nsIAccessibleTextChangeEvent) { + info += + ", start: " + + event.start + + ", length: " + + event.length + + ", " + + (event.isInserted ? "inserted" : "removed") + + " text: " + + event.modifiedText; + } + + info += ". Target: " + prettyName(event.accessible); + + if (listenersArray) { + info += ". Listeners count: " + listenersArray.length; + } + + if (gLogger.hasFeature("parentchain:" + type)) { + info += "\nParent chain:\n"; + var acc = event.accessible; + while (acc) { + info += " " + prettyName(acc) + "\n"; + acc = acc.parent; + } + } + + eventFromDumpArea = false; + gLogger.log(info); + } + } + + // Do not notify listeners if event is result of event log changes. + if (!listenersArray || eventFromDumpArea) { + return; + } + + for (var index = 0; index < listenersArray.length; index++) { + listenersArray[index].handleEvent(event); + } + }, +}; + +function listenA11yEvents(aStartToListen) { + if (aStartToListen) { + // Add observer when adding the first applicant only. + if (!gA11yEventApplicantsCount++) { + Services.obs.addObserver(gA11yEventObserver, "accessible-event"); + } + } else { + // Remove observer when there are no more applicants only. + // '< 0' case should not happen, but just in case: removeObserver() will throw. + // eslint-disable-next-line no-lonely-if + if (--gA11yEventApplicantsCount <= 0) { + Services.obs.removeObserver(gA11yEventObserver, "accessible-event"); + } + } +} + +function addA11yEventListener(aEventType, aEventHandler) { + if (!(aEventType in gA11yEventListeners)) { + gA11yEventListeners[aEventType] = []; + } + + var listenersArray = gA11yEventListeners[aEventType]; + var index = listenersArray.indexOf(aEventHandler); + if (index == -1) { + listenersArray.push(aEventHandler); + } +} + +function removeA11yEventListener(aEventType, aEventHandler) { + var listenersArray = gA11yEventListeners[aEventType]; + if (!listenersArray) { + return false; + } + + var index = listenersArray.indexOf(aEventHandler); + if (index == -1) { + return false; + } + + listenersArray.splice(index, 1); + + if (!listenersArray.length) { + gA11yEventListeners[aEventType] = null; + delete gA11yEventListeners[aEventType]; + } + + return true; +} + +/** + * Used to dump debug information. + */ +var gLogger = { + /** + * Return true if dump is enabled. + */ + isEnabled: function debugOutput_isEnabled() { + return ( + gA11yEventDumpID || gA11yEventDumpToConsole || gA11yEventDumpToAppConsole + ); + }, + + /** + * Dump information into DOM and console if applicable. + */ + log: function logger_log(aMsg) { + this.logToConsole(aMsg); + this.logToAppConsole(aMsg); + this.logToDOM(aMsg); + }, + + /** + * Log message to DOM. + * + * @param aMsg [in] the primary message + * @param aHasIndent [in, optional] if specified the message has an indent + * @param aPreEmphText [in, optional] the text is colored and appended prior + * primary message + */ + logToDOM: function logger_logToDOM(aMsg, aHasIndent, aPreEmphText) { + if (gA11yEventDumpID == "") { + return; + } + + var dumpElm = document.getElementById(gA11yEventDumpID); + if (!dumpElm) { + ok( + false, + "No dump element '" + gA11yEventDumpID + "' within the document!" + ); + return; + } + + var containerTagName = + ChromeUtils.getClassName(document) == "HTMLDocument" + ? "div" + : "description"; + + var container = document.createElement(containerTagName); + if (aHasIndent) { + container.setAttribute("style", "padding-left: 10px;"); + } + + if (aPreEmphText) { + var inlineTagName = + ChromeUtils.getClassName(document) == "HTMLDocument" + ? "span" + : "description"; + var emphElm = document.createElement(inlineTagName); + emphElm.setAttribute("style", "color: blue;"); + emphElm.textContent = aPreEmphText; + + container.appendChild(emphElm); + } + + var textNode = document.createTextNode(aMsg); + container.appendChild(textNode); + + dumpElm.appendChild(container); + }, + + /** + * Log message to console. + */ + logToConsole: function logger_logToConsole(aMsg) { + if (gA11yEventDumpToConsole) { + dump("\n" + aMsg + "\n"); + } + }, + + /** + * Log message to error console. + */ + logToAppConsole: function logger_logToAppConsole(aMsg) { + if (gA11yEventDumpToAppConsole) { + Services.console.logStringMessage("events: " + aMsg); + } + }, + + /** + * Return true if logging feature is enabled. + */ + hasFeature: function logger_hasFeature(aFeature) { + var startIdx = gA11yEventDumpFeature.indexOf(aFeature); + if (startIdx == -1) { + return false; + } + + var endIdx = startIdx + aFeature.length; + return ( + endIdx == gA11yEventDumpFeature.length || + gA11yEventDumpFeature[endIdx] == ";" + ); + }, +}; + +// ////////////////////////////////////////////////////////////////////////////// +// Sequence + +/** + * Base class of sequence item. + */ +function sequenceItem(aProcessor, aEventType, aTarget, aItemID) { + // private + + this.startProcess = function sequenceItem_startProcess() { + this.queue.invoke(); + }; + + this.queue = new eventQueue(); + this.queue.onFinish = function() { + aProcessor.onProcessed(); + return DO_NOT_FINISH_TEST; + }; + + var invoker = { + invoke: function invoker_invoke() { + return aProcessor.process(); + }, + getID: function invoker_getID() { + return aItemID; + }, + eventSeq: [new invokerChecker(aEventType, aTarget)], + }; + + this.queue.push(invoker); +} + +// ////////////////////////////////////////////////////////////////////////////// +// Event queue invokers + +/** + * Invoker base class for prepare an action. + */ +function synthAction(aNodeOrID, aEventsObj) { + this.DOMNode = getNode(aNodeOrID); + + if (aEventsObj) { + var scenarios = null; + if (aEventsObj instanceof Array) { + if (aEventsObj[0] instanceof Array) { + scenarios = aEventsObj; + } + // scenarios + else { + scenarios = [aEventsObj]; + } // event sequance + } else { + scenarios = [[aEventsObj]]; // a single checker object + } + + for (var i = 0; i < scenarios.length; i++) { + defineScenario(this, scenarios[i]); + } + } + + this.getID = function synthAction_getID() { + return prettyName(aNodeOrID) + " action"; + }; +} diff --git a/accessible/tests/mochitest/events/a11y.ini b/accessible/tests/mochitest/events/a11y.ini new file mode 100644 index 0000000000..076f146ba2 --- /dev/null +++ b/accessible/tests/mochitest/events/a11y.ini @@ -0,0 +1,63 @@ +[DEFAULT] +support-files = + focus.html + scroll.html + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + +[test_announcement.html] +[test_aria_alert.html] +[test_aria_menu.html] +[test_aria_objattr.html] +[test_aria_owns.html] +[test_aria_statechange.html] +[test_attrs.html] +[test_bug1322593.html] +[test_bug1322593-2.html] +[test_caretmove.html] +[test_coalescence.html] +[test_contextmenu.html] +[test_descrchange.html] +[test_dragndrop.html] +[test_flush.html] +[test_focusable_statechange.html] +[test_focus_aria_activedescendant.html] +[test_focus_autocomplete.xhtml] +# Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795 +skip-if = os == 'win' || os == 'linux' +[test_focus_canvas.html] +[test_focus_contextmenu.xhtml] +[test_focus_controls.html] +[test_focus_doc.html] +[test_focus_general.html] +[test_focus_general.xhtml] +[test_focus_listcontrols.xhtml] +[test_focus_menu.xhtml] +[test_focus_name.html] +[test_focus_removal.html] +[test_focus_selects.html] +[test_focus_tabbox.xhtml] +skip-if = webrender +[test_focus_tree.xhtml] +[test_fromUserInput.html] +[test_label.xhtml] +[test_menu.xhtml] +[test_mutation.html] +[test_namechange.xhtml] +[test_namechange.html] +[test_scroll.xhtml] +[test_scroll_caret.xhtml] +[test_selection.html] +skip-if = os == 'mac' +[test_selection.xhtml] +skip-if = os == 'mac' +[test_selection_aria.html] +[test_statechange.html] +[test_statechange_tabpanels.xhtml] +[test_text.html] +[test_text_alg.html] +[test_textattrchange.html] +[test_textselchange.html] +[test_tree.xhtml] +[test_valuechange.html] +skip-if = os == 'mac' diff --git a/accessible/tests/mochitest/events/docload/a11y.ini b/accessible/tests/mochitest/events/docload/a11y.ini new file mode 100644 index 0000000000..6e014d511c --- /dev/null +++ b/accessible/tests/mochitest/events/docload/a11y.ini @@ -0,0 +1,13 @@ +[DEFAULT] +support-files = + docload_wnd.html + !/accessible/tests/mochitest/*.js + +[test_docload_aria.html] +[test_docload_busy.html] +[test_docload_embedded.html] +[test_docload_iframe.html] +[test_docload_root.html] +skip-if = os == 'mac' # bug 1456997 +[test_docload_shutdown.html] +skip-if = os == 'mac' # bug 1456997 diff --git a/accessible/tests/mochitest/events/docload/docload_wnd.html b/accessible/tests/mochitest/events/docload/docload_wnd.html new file mode 100644 index 0000000000..93df1e86d4 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/docload_wnd.html @@ -0,0 +1,37 @@ + + + Accessible events testing for document + + + + + + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_aria.html b/accessible/tests/mochitest/events/docload/test_docload_aria.html new file mode 100644 index 0000000000..c5fc099918 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_aria.html @@ -0,0 +1,75 @@ + + + + Accessible events testing for ARIA document + + + + + + + + + + + + + + + + + Mozilla Bug 759833 + + +

    + +
    +  
    + + + + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_busy.html b/accessible/tests/mochitest/events/docload/test_docload_busy.html new file mode 100644 index 0000000000..37caf306bb --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_busy.html @@ -0,0 +1,83 @@ + + + + Accessible events testing for document + + + + + + + + + + + + + + + + + Mozilla Bug 658185 + + +

    + +
    +  
    + +
    + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_embedded.html b/accessible/tests/mochitest/events/docload/test_docload_embedded.html new file mode 100644 index 0000000000..18873dc904 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_embedded.html @@ -0,0 +1,85 @@ + + + + Accessible events testing for document + + + + + + + + + + + + + + + + Mozilla Bug 420845 + + + Mozilla Bug 754165 + + +

    + +
    +  
    + +
    + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_iframe.html b/accessible/tests/mochitest/events/docload/test_docload_iframe.html new file mode 100644 index 0000000000..d410ebb7e2 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_iframe.html @@ -0,0 +1,99 @@ + + + + Accessible events testing for document + + + + + + + + + + + + + + + + + Mozilla Bug 566103 + + +

    + +
    +  
    + +
    + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_root.html b/accessible/tests/mochitest/events/docload/test_docload_root.html new file mode 100644 index 0000000000..7947327c28 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_root.html @@ -0,0 +1,126 @@ + + + + Accessible events testing for document + + + + + + + + + + + + + + + + Mozilla Bug 506206 + + +

    + +
    +  
    + + diff --git a/accessible/tests/mochitest/events/docload/test_docload_shutdown.html b/accessible/tests/mochitest/events/docload/test_docload_shutdown.html new file mode 100644 index 0000000000..5838b80083 --- /dev/null +++ b/accessible/tests/mochitest/events/docload/test_docload_shutdown.html @@ -0,0 +1,143 @@ + + + + Accessible events testing for document + + + + + + + + + + + + + + + + Mozilla Bug 571459 + + +

    + +
    +  
    + + diff --git a/accessible/tests/mochitest/events/focus.html b/accessible/tests/mochitest/events/focus.html new file mode 100644 index 0000000000..ab055df82c --- /dev/null +++ b/accessible/tests/mochitest/events/focus.html @@ -0,0 +1,10 @@ + + + + editable document + + + + editable document + + diff --git a/accessible/tests/mochitest/events/scroll.html b/accessible/tests/mochitest/events/scroll.html new file mode 100644 index 0000000000..562e0a3825 --- /dev/null +++ b/accessible/tests/mochitest/events/scroll.html @@ -0,0 +1,181 @@ + + + + nsIAccessible actions testing for anchors + + + +

    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    +

    + link1 + +

    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    +

    + +

    heading 1

    +

    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    + text text text text text text text text text text text text text text
    +

    + + diff --git a/accessible/tests/mochitest/events/test_announcement.html b/accessible/tests/mochitest/events/test_announcement.html new file mode 100644 index 0000000000..eb303e4aa9 --- /dev/null +++ b/accessible/tests/mochitest/events/test_announcement.html @@ -0,0 +1,61 @@ + + + + Announcement event and method testing + + + + + + + + + + + + + + + + + Mozilla Bug 1525980 + + +

    + +
    +  
    + + + diff --git a/accessible/tests/mochitest/events/test_aria_alert.html b/accessible/tests/mochitest/events/test_aria_alert.html new file mode 100644 index 0000000000..48f4197b50 --- /dev/null +++ b/accessible/tests/mochitest/events/test_aria_alert.html @@ -0,0 +1,84 @@ + + + + ARIA alert event testing + + + + + + + + + + + + + + + + + Mozilla Bug 591199 + + +

    + +
    +  
    + + + diff --git a/accessible/tests/mochitest/events/test_aria_menu.html b/accessible/tests/mochitest/events/test_aria_menu.html new file mode 100644 index 0000000000..b240090cb9 --- /dev/null +++ b/accessible/tests/mochitest/events/test_aria_menu.html @@ -0,0 +1,267 @@ + + + + ARIA menu events testing + + + + + + + + + + + + + + + + + + Bug 606207 + + + Bug 614829 + + + Bug 615189 + + + Bug 673958 + + + Bug 933322 + + + Bug 934460 + + + Bug 970005 + + +

    + +
    +  
    + + + +
    outsidemenu
    + + + + + + + + + + + + + +
    Obla
    + +
    + + + diff --git a/accessible/tests/mochitest/events/test_aria_objattr.html b/accessible/tests/mochitest/events/test_aria_objattr.html new file mode 100644 index 0000000000..709089ca02 --- /dev/null +++ b/accessible/tests/mochitest/events/test_aria_objattr.html @@ -0,0 +1,68 @@ + + + + Accessible ARIA object attribute changes + + + + + + + + + + + + + +

    + +
    +  
    + +
    aria-sort
    + +
    Fat free cheese
    + + diff --git a/accessible/tests/mochitest/events/test_aria_owns.html b/accessible/tests/mochitest/events/test_aria_owns.html new file mode 100644 index 0000000000..3c638ad838 --- /dev/null +++ b/accessible/tests/mochitest/events/test_aria_owns.html @@ -0,0 +1,122 @@ + + + + Aria-owns targets shouldn't be on invalidation list so shouldn't have + show/hide events + + + + + + + + + + + + + + + + + Mozilla Bug 1296420 +
    + +
    +
    + +
    +
    + + + diff --git a/accessible/tests/mochitest/events/test_aria_statechange.html b/accessible/tests/mochitest/events/test_aria_statechange.html new file mode 100644 index 0000000000..2ba3516bd2 --- /dev/null +++ b/accessible/tests/mochitest/events/test_aria_statechange.html @@ -0,0 +1,224 @@ + + + + ARIA state change event testing + + + + + + + + + + + + + + + + + Mozilla Bug 551684 +
    + + Mozilla Bug 648133 +
    + + Mozilla Bug 467143 + + + Mozilla Bug 989958 + + + Mozilla Bug 1136563 + + + Mozilla Bug 1355921 + + +

    + +
    +  
    +
    + + + + + + +
    A document
    + + +
    + + + + +
    + + +
    1
    +
    2
    +
    3
    + + diff --git a/accessible/tests/mochitest/events/test_attrs.html b/accessible/tests/mochitest/events/test_attrs.html new file mode 100644 index 0000000000..c09bd9cf1e --- /dev/null +++ b/accessible/tests/mochitest/events/test_attrs.html @@ -0,0 +1,85 @@ + + + + Event object attributes tests + + + + + + + + + + + + + + + + + Mozilla Bug 540285 + +

    + +
    +  
    + + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_bug1322593-2.html b/accessible/tests/mochitest/events/test_bug1322593-2.html new file mode 100644 index 0000000000..05bd31ffa6 --- /dev/null +++ b/accessible/tests/mochitest/events/test_bug1322593-2.html @@ -0,0 +1,77 @@ + + + + Accessible mutation events testing + + + + + + + + + + + + + + + + Mozilla Bug 1322593 + + +

    + +
    +  
    + +
    + hello + your + world +
    + + diff --git a/accessible/tests/mochitest/events/test_bug1322593.html b/accessible/tests/mochitest/events/test_bug1322593.html new file mode 100644 index 0000000000..968e808106 --- /dev/null +++ b/accessible/tests/mochitest/events/test_bug1322593.html @@ -0,0 +1,74 @@ + + + + Accessible mutation events testing + + + + + + + + + + + + + + + + Mozilla Bug 1322593 + + +

    + +
    +  
    + +
    hello
    +
    world
    + + diff --git a/accessible/tests/mochitest/events/test_caretmove.html b/accessible/tests/mochitest/events/test_caretmove.html new file mode 100644 index 0000000000..d1091ac7f1 --- /dev/null +++ b/accessible/tests/mochitest/events/test_caretmove.html @@ -0,0 +1,142 @@ + + + + Accessible caret move events testing + + + + + + + + + + + + + + + + Bug 454377 + + + Bug 567571 + + + Bug 824901 + +

    + +
    +  
    + + + + +

    text
    text

    +

    text

    text

    + +

    textohoho

    +

    textohoho

    +

    +

    + + + + diff --git a/accessible/tests/mochitest/events/test_coalescence.html b/accessible/tests/mochitest/events/test_coalescence.html new file mode 100644 index 0000000000..0f8ad52a8b --- /dev/null +++ b/accessible/tests/mochitest/events/test_coalescence.html @@ -0,0 +1,817 @@ + + + + Accessible mutation events coalescence testing + + + + + + + + + + + + + + + + + Mozilla Bug 513213 +
    + + Mozilla Bug 570275 + + +

    + +
    +  
    + +
    + + + + + + + + + + + + + +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    opt
    +
    +
    + +
    +
    +
    opt1
    +
    opt2
    +
    +
    + +
    +
    btn
    +
    +
    opt
    +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + diff --git a/accessible/tests/mochitest/events/test_contextmenu.html b/accessible/tests/mochitest/events/test_contextmenu.html new file mode 100644 index 0000000000..1dca41886e --- /dev/null +++ b/accessible/tests/mochitest/events/test_contextmenu.html @@ -0,0 +1,125 @@ + + + + Context menu tests + + + + + + + + + + + + + + + + + + Mozilla Bug 580535 +
    + +

    + +
    +  
    + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_descrchange.html b/accessible/tests/mochitest/events/test_descrchange.html new file mode 100644 index 0000000000..b7cb3e1433 --- /dev/null +++ b/accessible/tests/mochitest/events/test_descrchange.html @@ -0,0 +1,79 @@ + + + + Accessible description change event testing + + + + + + + + + + + + + + + + + + Bug 991969 + + +

    + +
    +  
    + + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_dragndrop.html b/accessible/tests/mochitest/events/test_dragndrop.html new file mode 100644 index 0000000000..2613a310a2 --- /dev/null +++ b/accessible/tests/mochitest/events/test_dragndrop.html @@ -0,0 +1,106 @@ + + + + Accessible drag and drop event testing + + + + + + + + + + + + + + + + + Mozilla Bug 510441 + + +

    + +
    +  
    +
    + + +
    button
    + + +
    button
    + + diff --git a/accessible/tests/mochitest/events/test_flush.html b/accessible/tests/mochitest/events/test_flush.html new file mode 100644 index 0000000000..7d7b60b81e --- /dev/null +++ b/accessible/tests/mochitest/events/test_flush.html @@ -0,0 +1,74 @@ + + + + Flush delayed events testing + + + + + + + + + + + + + + + + Mozilla Bug 477551 + + +

    + +
    +  
    + + + + + diff --git a/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html new file mode 100644 index 0000000000..5f005bad49 --- /dev/null +++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html @@ -0,0 +1,260 @@ + + + + + aria-activedescendant focus tests + + + + + + + + + + + + + + + Mozilla Bug 429547 + + + Mozilla Bug 761102 + +

    + +
    +  
    + +
    +
    item1
    +
    item2
    +
    roaming
    +
    roaming2
    +
    +
    item3
    + +
    + +
      +
    • option1
    • +
    • option2
    • +
    +
    + + + +
    +
    option
    +
    + +
    + + +
    +
    + + + diff --git a/accessible/tests/mochitest/events/test_focus_autocomplete.xhtml b/accessible/tests/mochitest/events/test_focus_autocomplete.xhtml new file mode 100644 index 0000000000..69cdac14c5 --- /dev/null +++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xhtml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + Mozilla Bug 383759 + + + Mozilla Bug 673958 + + + Mozilla Bug 559766 + +

    + +
    +      
    + + + + + + + + + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_focus_general.html b/accessible/tests/mochitest/events/test_focus_general.html new file mode 100644 index 0000000000..abf6a53085 --- /dev/null +++ b/accessible/tests/mochitest/events/test_focus_general.html @@ -0,0 +1,164 @@ + + + + Accessible focus testing + + + + + + + + + + + + + + + + + Mozilla Bug 352220 + + + Mozilla Bug 550338 + + + Mozilla Bug 673958 + + + Mozilla Bug 961696 + +

    + +
    +  
    + +
    editable area
    + + + link + + + + + + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_focus_general.xhtml b/accessible/tests/mochitest/events/test_focus_general.xhtml new file mode 100644 index 0000000000..f3f9a014c1 --- /dev/null +++ b/accessible/tests/mochitest/events/test_focus_general.xhtml @@ -0,0 +1,118 @@ + + + + + + + + + + + + Mozilla Bug 552368 + +

    + +
    +      
    + + + + + + + + diff --git a/accessible/tests/mochitest/events/test_namechange.html b/accessible/tests/mochitest/events/test_namechange.html new file mode 100644 index 0000000000..02d888a4ae --- /dev/null +++ b/accessible/tests/mochitest/events/test_namechange.html @@ -0,0 +1,119 @@ + + + + Accessible name change event testing + + + + + + + + + + + + + + + + + + Bug 991969 + + +

    + +
    +  
    + + initial + + + + +
    + + diff --git a/accessible/tests/mochitest/events/test_namechange.xhtml b/accessible/tests/mochitest/events/test_namechange.xhtml new file mode 100644 index 0000000000..a6dd8cb218 --- /dev/null +++ b/accessible/tests/mochitest/events/test_namechange.xhtml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + Mozilla Bug 986054 + + +

    + +
    +      
    + + + + + + + +
    +
    diff --git a/accessible/tests/mochitest/events/test_scroll.xhtml b/accessible/tests/mochitest/events/test_scroll.xhtml new file mode 100644 index 0000000000..cf3aaa7cb4 --- /dev/null +++ b/accessible/tests/mochitest/events/test_scroll.xhtml @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + Mozilla Bug 691734 + + +

    + +
    +      
    + + + +
    +
    diff --git a/accessible/tests/mochitest/events/test_scroll_caret.xhtml b/accessible/tests/mochitest/events/test_scroll_caret.xhtml new file mode 100644 index 0000000000..e696b128bd --- /dev/null +++ b/accessible/tests/mochitest/events/test_scroll_caret.xhtml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + Mozilla Bug 1056459 + + +

    + +
    +      
    + + + +
    +
    diff --git a/accessible/tests/mochitest/events/test_selection.html b/accessible/tests/mochitest/events/test_selection.html new file mode 100644 index 0000000000..a976d54b30 --- /dev/null +++ b/accessible/tests/mochitest/events/test_selection.html @@ -0,0 +1,115 @@ + + + + Accessible selection event testing + + + + + + + + + + + + + + + + + + Bug 414302 + + + Bug 810268 + + +

    + +
    +  
    + + + + + +

    Pizza

    + + + diff --git a/accessible/tests/mochitest/events/test_selection.xhtml b/accessible/tests/mochitest/events/test_selection.xhtml new file mode 100644 index 0000000000..9c34ddf286 --- /dev/null +++ b/accessible/tests/mochitest/events/test_selection.xhtml @@ -0,0 +1,254 @@ + + + + + + + + + + + + Mozilla Bug 414302 + +

    + +
    +      
    + + + + + + + + + + + + + + diff --git a/accessible/tests/mochitest/events/test_statechange_tabpanels.xhtml b/accessible/tests/mochitest/events/test_statechange_tabpanels.xhtml new file mode 100644 index 0000000000..90e8fad75b --- /dev/null +++ b/accessible/tests/mochitest/events/test_statechange_tabpanels.xhtml @@ -0,0 +1,98 @@ + + + + + + + + + + +

    + +
    +      
    + + + + + + + + + + +
    + + + text1 + text2 + +
    + + + + + + + nomore text + +
    + + + + nomore + hidden + text2 + hidden2 + + + +
    + + + + +
    + + +
    text
    more text
    + +
    + + + + +
    textspacetext
    + +
    + + + textimage + +
    + + + + + + + + + +
    + + + + + + +
    + + + text
    text
    + +
    + + + + + + +

    Choose country from.

    + + + +

    Country

    + + + + + + +    + + + + + + + +
    + + +
    + + +
    + + + +
    + +
    + + + + + + + + + + + + + + + 14 + + + + + +
    +
    + i am visible + i am hidden +
    +
    + + + + + + + + +
    menuitem 2
    + + + +
    +

    This is a paragraph inside the article.

    +
    + +

    heading

    +
    aria_heading
    + + + 15 + + +
    + +
    + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + +

    Image: + x^2 + y^2 + z^2 + +

    + +

    Text: + x2 + + y2 + z2 + + +

    subtree
    + + +
    ab
    + + + + +
    ab
    + + +
    ab
    + + +
    ab
    + + +
    label
    + + +
    + + + + +
    label
    +
    + +
    root +
    +
    sub
    +
    +
    + + +
    label +
    + This content should not be included in the grouping's label. +
    +
    + + diff --git a/accessible/tests/mochitest/name/test_general.xhtml b/accessible/tests/mochitest/name/test_general.xhtml new file mode 100644 index 0000000000..2d8abaf4d5 --- /dev/null +++ b/accessible/tests/mochitest/name/test_general.xhtml @@ -0,0 +1,339 @@ + + + + + + + + + + + + + + + + + Mozilla Bug 444279 + + + Mozilla Bug 441991 + +

    + +
    +    
    + + + + + + + + + + + + + + + + + + + label + + + label1 + label2 + + + description + + + description1 + description2 + + +
    Yellow
    +
    Orange
    +
    +
    Blue
    +
    Green
    +
    Light green
    +
    Green2
    +
    +
    Super light green
    +
    +
    + +
    +
    +
    cell 1,1
    +
    cell 1,2
    +
    +
    +
    cell 2,1
    +
    cell 2,2
    +
    +
    +
    cell 3,1
    +
    cell 3,2
    +
    +
    + +
      +
    • Item 1 +
        +
      • Item 1A
      • +
      • Item 1B
      • +
      +
    • +
    + +
    +
    + cell1cell2 +
    +
    + cell3cell4 +
    +
    + cell5cell6 +
    +
    + +
    +
    Item 1 +
    +
    Item 1A
    +
    Item 1B
    +
    +
    +
    + + + +
    + +
    +
    tabpanel
    + +
    1
    +
    a
    + + +
    live region
    + + flow to + flow from + + flow to + flow from + flow from + +
    + + + + + +
    + + + + + + +
    tabple caption
    cell1cell2
    + +
    + legend + +
    + +
    +
    +
    + + diff --git a/accessible/tests/mochitest/relations/test_general.xhtml b/accessible/tests/mochitest/relations/test_general.xhtml new file mode 100644 index 0000000000..bc3b328fd9 --- /dev/null +++ b/accessible/tests/mochitest/relations/test_general.xhtml @@ -0,0 +1,237 @@ + + + + + + + + + + + + Mozilla Bug 475298 +
    + + Mozilla Bug 67389 +
    + +

    + +
    +      
    + + + + + + + + + + label + + + label + label + + + + + + description + + + label + label + + + description + + + description + description + + + Yellow + Orange + + Blue + Green + Light green + + + Dark green + + + + + + + + + diff --git a/accessible/tests/mochitest/role/test_aria.xhtml b/accessible/tests/mochitest/role/test_aria.xhtml new file mode 100644 index 0000000000..9aea4ec222 --- /dev/null +++ b/accessible/tests/mochitest/role/test_aria.xhtml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + Mozilla Bug 494345 + + + Mozilla Bug 1033283 + +

    + +
    +    
    + + + +