summaryrefslogtreecommitdiffstats
path: root/accessible/tests
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /accessible/tests
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'accessible/tests')
-rw-r--r--accessible/tests/browser/.eslintrc.js218
-rw-r--r--accessible/tests/browser/browser.ini17
-rw-r--r--accessible/tests/browser/browser_shutdown_multi_reference.js48
-rw-r--r--accessible/tests/browser/browser_shutdown_parent_own_reference.js72
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_no_reference.js48
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_only.js40
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_own_reference.js75
-rw-r--r--accessible/tests/browser/browser_shutdown_scope_lifecycle.js21
-rw-r--r--accessible/tests/browser/browser_shutdown_start_restart.js41
-rw-r--r--accessible/tests/browser/e10s/browser.ini51
-rw-r--r--accessible/tests/browser/e10s/browser_caching_attributes.js117
-rw-r--r--accessible/tests/browser/e10s/browser_caching_description.js164
-rw-r--r--accessible/tests/browser/e10s/browser_caching_name.js434
-rw-r--r--accessible/tests/browser/e10s/browser_caching_relations.js86
-rw-r--r--accessible/tests/browser/e10s/browser_caching_states.js120
-rw-r--r--accessible/tests/browser/e10s/browser_caching_value.js155
-rw-r--r--accessible/tests/browser/e10s/browser_events_caretmove.js21
-rw-r--r--accessible/tests/browser/e10s/browser_events_hide.js35
-rw-r--r--accessible/tests/browser/e10s/browser_events_show.js17
-rw-r--r--accessible/tests/browser/e10s/browser_events_statechange.js62
-rw-r--r--accessible/tests/browser/e10s/browser_events_textchange.js74
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js43
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js318
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_canvas.js25
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js64
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_doc.js312
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_gencontent.js78
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_hidden.js30
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_imagemap.js176
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_list.js43
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js39
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_listener.js29
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_optgroup.js91
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_removal.js39
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_table.js51
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_textleaf.js35
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_visibility.js196
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_whitespace.js71
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html23
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html44
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_imagemap.html21
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml11
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_visibility.html78
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_whitespace.html10
-rw-r--r--accessible/tests/browser/e10s/events.js127
-rw-r--r--accessible/tests/browser/e10s/head.js84
-rw-r--r--accessible/tests/browser/head.js116
-rw-r--r--accessible/tests/browser/shared-head.js229
-rw-r--r--accessible/tests/crashtests/448064.xhtml73
-rw-r--r--accessible/tests/crashtests/471493.xul35
-rw-r--r--accessible/tests/crashtests/crashtests.list3
-rw-r--r--accessible/tests/mochitest/a11y.ini17
-rw-r--r--accessible/tests/mochitest/actions.js187
-rw-r--r--accessible/tests/mochitest/actions/a11y.ini18
-rw-r--r--accessible/tests/mochitest/actions/test_anchors.html150
-rw-r--r--accessible/tests/mochitest/actions/test_aria.html202
-rw-r--r--accessible/tests/mochitest/actions/test_controls.html109
-rw-r--r--accessible/tests/mochitest/actions/test_general.html107
-rw-r--r--accessible/tests/mochitest/actions/test_general.xul145
-rw-r--r--accessible/tests/mochitest/actions/test_keys.html60
-rw-r--r--accessible/tests/mochitest/actions/test_keys_menu.xul99
-rw-r--r--accessible/tests/mochitest/actions/test_link.html147
-rw-r--r--accessible/tests/mochitest/actions/test_media.html121
-rw-r--r--accessible/tests/mochitest/actions/test_select.html105
-rw-r--r--accessible/tests/mochitest/actions/test_tree.xul128
-rw-r--r--accessible/tests/mochitest/actions/test_treegrid.xul197
-rw-r--r--accessible/tests/mochitest/aom/a11y.ini3
-rw-r--r--accessible/tests/mochitest/aom/test_general.html55
-rw-r--r--accessible/tests/mochitest/attributes.js382
-rw-r--r--accessible/tests/mochitest/attributes/a11y.ini12
-rw-r--r--accessible/tests/mochitest/attributes/test_obj.html278
-rw-r--r--accessible/tests/mochitest/attributes/test_obj_css.html231
-rw-r--r--accessible/tests/mochitest/attributes/test_obj_css.xul73
-rw-r--r--accessible/tests/mochitest/attributes/test_obj_group.html469
-rw-r--r--accessible/tests/mochitest/attributes/test_obj_group.xul216
-rw-r--r--accessible/tests/mochitest/attributes/test_obj_group_tree.xul85
-rw-r--r--accessible/tests/mochitest/attributes/test_tag.html82
-rw-r--r--accessible/tests/mochitest/attributes/test_xml-roles.html251
-rw-r--r--accessible/tests/mochitest/autocomplete.js221
-rw-r--r--accessible/tests/mochitest/bounds/a11y.ini8
-rw-r--r--accessible/tests/mochitest/bounds/test_list.html81
-rw-r--r--accessible/tests/mochitest/bounds/test_select.html85
-rw-r--r--accessible/tests/mochitest/bounds/test_zoom.html96
-rw-r--r--accessible/tests/mochitest/bounds/test_zoom_text.html77
-rw-r--r--accessible/tests/mochitest/browser.js153
-rw-r--r--accessible/tests/mochitest/common.js952
-rw-r--r--accessible/tests/mochitest/dumbfile.zipbin0 -> 22 bytes
-rw-r--r--accessible/tests/mochitest/editabletext/a11y.ini7
-rw-r--r--accessible/tests/mochitest/editabletext/editabletext.js353
-rw-r--r--accessible/tests/mochitest/editabletext/test_1.html144
-rw-r--r--accessible/tests/mochitest/editabletext/test_2.html63
-rw-r--r--accessible/tests/mochitest/elm/a11y.ini16
-rw-r--r--accessible/tests/mochitest/elm/test_HTMLSpec.html1671
-rw-r--r--accessible/tests/mochitest/elm/test_MathMLSpec.html620
-rw-r--r--accessible/tests/mochitest/elm/test_canvas.html58
-rw-r--r--accessible/tests/mochitest/elm/test_figure.html62
-rw-r--r--accessible/tests/mochitest/elm/test_listbox.xul74
-rw-r--r--accessible/tests/mochitest/elm/test_nsApplicationAcc.html75
-rw-r--r--accessible/tests/mochitest/elm/test_plugin.html79
-rw-r--r--accessible/tests/mochitest/elm/test_shadowroot.html60
-rw-r--r--accessible/tests/mochitest/events.js2329
-rw-r--r--accessible/tests/mochitest/events/a11y.ini67
-rw-r--r--accessible/tests/mochitest/events/docload_wnd.html39
-rw-r--r--accessible/tests/mochitest/events/focus.html10
-rw-r--r--accessible/tests/mochitest/events/scroll.html181
-rw-r--r--accessible/tests/mochitest/events/test_aria_alert.html92
-rw-r--r--accessible/tests/mochitest/events/test_aria_menu.html285
-rw-r--r--accessible/tests/mochitest/events/test_aria_objattr.html118
-rw-r--r--accessible/tests/mochitest/events/test_aria_owns.html129
-rw-r--r--accessible/tests/mochitest/events/test_aria_statechange.html208
-rw-r--r--accessible/tests/mochitest/events/test_attrs.html90
-rw-r--r--accessible/tests/mochitest/events/test_bug1322593-2.html83
-rw-r--r--accessible/tests/mochitest/events/test_bug1322593.html80
-rw-r--r--accessible/tests/mochitest/events/test_caretmove.html140
-rw-r--r--accessible/tests/mochitest/events/test_caretmove.xul72
-rw-r--r--accessible/tests/mochitest/events/test_coalescence.html864
-rw-r--r--accessible/tests/mochitest/events/test_contextmenu.html139
-rw-r--r--accessible/tests/mochitest/events/test_descrchange.html85
-rw-r--r--accessible/tests/mochitest/events/test_docload.html360
-rw-r--r--accessible/tests/mochitest/events/test_docload.xul243
-rw-r--r--accessible/tests/mochitest/events/test_docload_aria.html83
-rw-r--r--accessible/tests/mochitest/events/test_dragndrop.html110
-rw-r--r--accessible/tests/mochitest/events/test_flush.html77
-rw-r--r--accessible/tests/mochitest/events/test_focus_aria_activedescendant.html120
-rw-r--r--accessible/tests/mochitest/events/test_focus_autocomplete.xul518
-rw-r--r--accessible/tests/mochitest/events/test_focus_browserui.xul149
-rw-r--r--accessible/tests/mochitest/events/test_focus_canvas.html61
-rw-r--r--accessible/tests/mochitest/events/test_focus_contextmenu.xul99
-rw-r--r--accessible/tests/mochitest/events/test_focus_controls.html75
-rw-r--r--accessible/tests/mochitest/events/test_focus_dialog.html164
-rw-r--r--accessible/tests/mochitest/events/test_focus_doc.html95
-rw-r--r--accessible/tests/mochitest/events/test_focus_general.html179
-rw-r--r--accessible/tests/mochitest/events/test_focus_general.xul179
-rw-r--r--accessible/tests/mochitest/events/test_focus_listcontrols.xul189
-rw-r--r--accessible/tests/mochitest/events/test_focus_menu.xul119
-rw-r--r--accessible/tests/mochitest/events/test_focus_name.html122
-rw-r--r--accessible/tests/mochitest/events/test_focus_selects.html118
-rw-r--r--accessible/tests/mochitest/events/test_focus_tabbox.xul103
-rw-r--r--accessible/tests/mochitest/events/test_focus_tree.xul122
-rw-r--r--accessible/tests/mochitest/events/test_fromUserInput.html127
-rw-r--r--accessible/tests/mochitest/events/test_label.xul177
-rw-r--r--accessible/tests/mochitest/events/test_menu.xul202
-rw-r--r--accessible/tests/mochitest/events/test_mutation.html632
-rw-r--r--accessible/tests/mochitest/events/test_mutation.xhtml97
-rw-r--r--accessible/tests/mochitest/events/test_namechange.html123
-rw-r--r--accessible/tests/mochitest/events/test_namechange.xul92
-rw-r--r--accessible/tests/mochitest/events/test_scroll.xul131
-rw-r--r--accessible/tests/mochitest/events/test_scroll_caret.xul91
-rw-r--r--accessible/tests/mochitest/events/test_selection.html118
-rw-r--r--accessible/tests/mochitest/events/test_selection.xul255
-rw-r--r--accessible/tests/mochitest/events/test_selection_aria.html127
-rw-r--r--accessible/tests/mochitest/events/test_statechange.html287
-rw-r--r--accessible/tests/mochitest/events/test_text.html339
-rw-r--r--accessible/tests/mochitest/events/test_text_alg.html249
-rw-r--r--accessible/tests/mochitest/events/test_textattrchange.html115
-rw-r--r--accessible/tests/mochitest/events/test_textselchange.html86
-rw-r--r--accessible/tests/mochitest/events/test_tree.xul348
-rw-r--r--accessible/tests/mochitest/events/test_valuechange.html255
-rw-r--r--accessible/tests/mochitest/focus/a11y.ini9
-rw-r--r--accessible/tests/mochitest/focus/test_focusedChild.html87
-rw-r--r--accessible/tests/mochitest/focus/test_takeFocus.html128
-rw-r--r--accessible/tests/mochitest/focus/test_takeFocus.xul106
-rw-r--r--accessible/tests/mochitest/formimage.pngbin0 -> 20105 bytes
-rw-r--r--accessible/tests/mochitest/grid.js149
-rw-r--r--accessible/tests/mochitest/hittest/a11y.ini14
-rw-r--r--accessible/tests/mochitest/hittest/test_browser.html63
-rw-r--r--accessible/tests/mochitest/hittest/test_canvas_hitregion.html88
-rw-r--r--accessible/tests/mochitest/hittest/test_general.html115
-rw-r--r--accessible/tests/mochitest/hittest/test_menu.xul134
-rw-r--r--accessible/tests/mochitest/hittest/test_shadowroot.html72
-rw-r--r--accessible/tests/mochitest/hittest/test_zoom.html61
-rw-r--r--accessible/tests/mochitest/hittest/test_zoom_text.html57
-rw-r--r--accessible/tests/mochitest/hittest/test_zoom_tree.xul100
-rw-r--r--accessible/tests/mochitest/hittest/zoom_tree.xul18
-rw-r--r--accessible/tests/mochitest/hyperlink/a11y.ini7
-rw-r--r--accessible/tests/mochitest/hyperlink/hyperlink.js42
-rw-r--r--accessible/tests/mochitest/hyperlink/test_general.html279
-rw-r--r--accessible/tests/mochitest/hyperlink/test_general.xul97
-rw-r--r--accessible/tests/mochitest/hypertext/a11y.ini7
-rw-r--r--accessible/tests/mochitest/hypertext/test_general.html156
-rw-r--r--accessible/tests/mochitest/hypertext/test_update.html236
-rw-r--r--accessible/tests/mochitest/jsat/a11y.ini30
-rw-r--r--accessible/tests/mochitest/jsat/doc_content_integration.html115
-rw-r--r--accessible/tests/mochitest/jsat/doc_content_text.html15
-rw-r--r--accessible/tests/mochitest/jsat/doc_traversal.html164
-rw-r--r--accessible/tests/mochitest/jsat/dom_helper.js209
-rw-r--r--accessible/tests/mochitest/jsat/gestures.json352
-rw-r--r--accessible/tests/mochitest/jsat/jsatcommon.js739
-rw-r--r--accessible/tests/mochitest/jsat/output.js114
-rw-r--r--accessible/tests/mochitest/jsat/test_alive.html81
-rw-r--r--accessible/tests/mochitest/jsat/test_content_integration.html343
-rw-r--r--accessible/tests/mochitest/jsat/test_content_text.html292
-rw-r--r--accessible/tests/mochitest/jsat/test_explicit_names.html191
-rw-r--r--accessible/tests/mochitest/jsat/test_gesture_tracker.html51
-rw-r--r--accessible/tests/mochitest/jsat/test_hints.html89
-rw-r--r--accessible/tests/mochitest/jsat/test_landmarks.html183
-rw-r--r--accessible/tests/mochitest/jsat/test_live_regions.html472
-rw-r--r--accessible/tests/mochitest/jsat/test_output.html673
-rw-r--r--accessible/tests/mochitest/jsat/test_output_mathml.html313
-rw-r--r--accessible/tests/mochitest/jsat/test_pointer_relay.html95
-rw-r--r--accessible/tests/mochitest/jsat/test_quicknav_modes.html107
-rw-r--r--accessible/tests/mochitest/jsat/test_tables.html579
-rw-r--r--accessible/tests/mochitest/jsat/test_traversal.html167
-rw-r--r--accessible/tests/mochitest/jsat/test_traversal_helper.html113
-rw-r--r--accessible/tests/mochitest/layout.js258
-rw-r--r--accessible/tests/mochitest/letters.gifbin0 -> 5596 bytes
-rw-r--r--accessible/tests/mochitest/longdesc_src.html8
-rw-r--r--accessible/tests/mochitest/moz.build37
-rw-r--r--accessible/tests/mochitest/moz.pngbin0 -> 1991 bytes
-rw-r--r--accessible/tests/mochitest/name.js33
-rw-r--r--accessible/tests/mochitest/name/a11y.ini20
-rw-r--r--accessible/tests/mochitest/name/general.css11
-rw-r--r--accessible/tests/mochitest/name/general.xbl32
-rw-r--r--accessible/tests/mochitest/name/markup.js382
-rw-r--r--accessible/tests/mochitest/name/markuprules.xml373
-rw-r--r--accessible/tests/mochitest/name/test_browserui.xul107
-rw-r--r--accessible/tests/mochitest/name/test_counterstyle.html153
-rw-r--r--accessible/tests/mochitest/name/test_general.html631
-rw-r--r--accessible/tests/mochitest/name/test_general.xul382
-rw-r--r--accessible/tests/mochitest/name/test_link.html89
-rw-r--r--accessible/tests/mochitest/name/test_list.html89
-rw-r--r--accessible/tests/mochitest/name/test_markup.html60
-rw-r--r--accessible/tests/mochitest/name/test_svg.html55
-rw-r--r--accessible/tests/mochitest/name/test_toolbaritem.xul84
-rw-r--r--accessible/tests/mochitest/name/test_tree.xul211
-rw-r--r--accessible/tests/mochitest/pivot.js551
-rw-r--r--accessible/tests/mochitest/pivot/a11y.ini8
-rw-r--r--accessible/tests/mochitest/pivot/doc_virtualcursor.html38
-rw-r--r--accessible/tests/mochitest/pivot/doc_virtualcursor_text.html29
-rw-r--r--accessible/tests/mochitest/pivot/test_virtualcursor.html129
-rw-r--r--accessible/tests/mochitest/pivot/test_virtualcursor_text.html241
-rw-r--r--accessible/tests/mochitest/relations.js192
-rw-r--r--accessible/tests/mochitest/relations/a11y.ini12
-rw-r--r--accessible/tests/mochitest/relations/test_bindings.xhtml103
-rw-r--r--accessible/tests/mochitest/relations/test_embeds.xul122
-rw-r--r--accessible/tests/mochitest/relations/test_general.html406
-rw-r--r--accessible/tests/mochitest/relations/test_general.xul238
-rw-r--r--accessible/tests/mochitest/relations/test_tabbrowser.xul103
-rw-r--r--accessible/tests/mochitest/relations/test_tree.xul106
-rw-r--r--accessible/tests/mochitest/relations/test_ui_modalprompt.html107
-rw-r--r--accessible/tests/mochitest/relations/test_update.html225
-rw-r--r--accessible/tests/mochitest/role.js178
-rw-r--r--accessible/tests/mochitest/role/a11y.ini10
-rw-r--r--accessible/tests/mochitest/role/test_aria.html345
-rw-r--r--accessible/tests/mochitest/role/test_aria.xul72
-rw-r--r--accessible/tests/mochitest/role/test_general.html186
-rw-r--r--accessible/tests/mochitest/role/test_general.xul57
-rw-r--r--accessible/tests/mochitest/role/test_svg.html70
-rw-r--r--accessible/tests/mochitest/scroll/a11y.ini6
-rw-r--r--accessible/tests/mochitest/scroll/test_zoom.html148
-rw-r--r--accessible/tests/mochitest/scroll/test_zoom_text.html158
-rw-r--r--accessible/tests/mochitest/selectable.js80
-rw-r--r--accessible/tests/mochitest/selectable/a11y.ini11
-rw-r--r--accessible/tests/mochitest/selectable/test_aria.html225
-rw-r--r--accessible/tests/mochitest/selectable/test_listbox.xul152
-rw-r--r--accessible/tests/mochitest/selectable/test_menu.xul78
-rw-r--r--accessible/tests/mochitest/selectable/test_menulist.xul96
-rw-r--r--accessible/tests/mochitest/selectable/test_select.html243
-rw-r--r--accessible/tests/mochitest/selectable/test_tree.xul188
-rw-r--r--accessible/tests/mochitest/states.js266
-rw-r--r--accessible/tests/mochitest/states/a11y.ini37
-rw-r--r--accessible/tests/mochitest/states/test_aria.html629
-rw-r--r--accessible/tests/mochitest/states/test_aria.xul60
-rw-r--r--accessible/tests/mochitest/states/test_aria_imgmap.html79
-rw-r--r--accessible/tests/mochitest/states/test_aria_widgetitems.html162
-rw-r--r--accessible/tests/mochitest/states/test_buttons.html85
-rw-r--r--accessible/tests/mochitest/states/test_controls.html53
-rw-r--r--accessible/tests/mochitest/states/test_controls.xul182
-rw-r--r--accessible/tests/mochitest/states/test_doc.html89
-rw-r--r--accessible/tests/mochitest/states/test_doc_busy.html79
-rw-r--r--accessible/tests/mochitest/states/test_docarticle.html80
-rw-r--r--accessible/tests/mochitest/states/test_editablebody.html46
-rw-r--r--accessible/tests/mochitest/states/test_expandable.xul118
-rw-r--r--accessible/tests/mochitest/states/test_frames.html95
-rw-r--r--accessible/tests/mochitest/states/test_inputs.html271
-rw-r--r--accessible/tests/mochitest/states/test_link.html144
-rw-r--r--accessible/tests/mochitest/states/test_popup.xul55
-rw-r--r--accessible/tests/mochitest/states/test_selects.html203
-rw-r--r--accessible/tests/mochitest/states/test_stale.html115
-rw-r--r--accessible/tests/mochitest/states/test_tabs.xul70
-rw-r--r--accessible/tests/mochitest/states/test_textbox.xul153
-rw-r--r--accessible/tests/mochitest/states/test_tree.xul152
-rw-r--r--accessible/tests/mochitest/states/test_visibility.html175
-rw-r--r--accessible/tests/mochitest/states/test_visibility.xul152
-rw-r--r--accessible/tests/mochitest/states/z_frames.html11
-rw-r--r--accessible/tests/mochitest/states/z_frames_article.html11
-rw-r--r--accessible/tests/mochitest/states/z_frames_checkbox.html11
-rw-r--r--accessible/tests/mochitest/states/z_frames_textbox.html11
-rw-r--r--accessible/tests/mochitest/states/z_frames_update.html22
-rw-r--r--accessible/tests/mochitest/table.js778
-rw-r--r--accessible/tests/mochitest/table/a11y.ini27
-rw-r--r--accessible/tests/mochitest/table/test_css_tables.html116
-rw-r--r--accessible/tests/mochitest/table/test_headers_ariagrid.html185
-rw-r--r--accessible/tests/mochitest/table/test_headers_ariatable.html96
-rw-r--r--accessible/tests/mochitest/table/test_headers_listbox.xul194
-rw-r--r--accessible/tests/mochitest/table/test_headers_table.html713
-rw-r--r--accessible/tests/mochitest/table/test_headers_tree.xul101
-rw-r--r--accessible/tests/mochitest/table/test_indexes_ariagrid.html139
-rw-r--r--accessible/tests/mochitest/table/test_indexes_listbox.xul85
-rw-r--r--accessible/tests/mochitest/table/test_indexes_table.html410
-rw-r--r--accessible/tests/mochitest/table/test_indexes_tree.xul71
-rw-r--r--accessible/tests/mochitest/table/test_layoutguess.html506
-rw-r--r--accessible/tests/mochitest/table/test_mtable.html128
-rw-r--r--accessible/tests/mochitest/table/test_sels_ariagrid.html161
-rw-r--r--accessible/tests/mochitest/table/test_sels_listbox.xul247
-rw-r--r--accessible/tests/mochitest/table/test_sels_table.html180
-rw-r--r--accessible/tests/mochitest/table/test_sels_tree.xul79
-rw-r--r--accessible/tests/mochitest/table/test_struct_ariagrid.html149
-rw-r--r--accessible/tests/mochitest/table/test_struct_ariatreegrid.html76
-rw-r--r--accessible/tests/mochitest/table/test_struct_listbox.xul117
-rw-r--r--accessible/tests/mochitest/table/test_struct_table.html203
-rw-r--r--accessible/tests/mochitest/table/test_struct_tree.xul74
-rw-r--r--accessible/tests/mochitest/table/test_table_1.html105
-rw-r--r--accessible/tests/mochitest/table/test_table_2.html89
-rw-r--r--accessible/tests/mochitest/test_OuterDocAccessible.html89
-rw-r--r--accessible/tests/mochitest/test_aria_token_attrs.html329
-rw-r--r--accessible/tests/mochitest/test_bug420863.html103
-rw-r--r--accessible/tests/mochitest/test_descr.html121
-rw-r--r--accessible/tests/mochitest/test_nsIAccessibleDocument.html96
-rw-r--r--accessible/tests/mochitest/test_nsIAccessibleImage.html202
-rw-r--r--accessible/tests/mochitest/text.js634
-rw-r--r--accessible/tests/mochitest/text/a11y.ini16
-rw-r--r--accessible/tests/mochitest/text/doc.html9
-rw-r--r--accessible/tests/mochitest/text/test_atcaretoffset.html455
-rw-r--r--accessible/tests/mochitest/text/test_charboundary.html140
-rw-r--r--accessible/tests/mochitest/text/test_doc.html42
-rw-r--r--accessible/tests/mochitest/text/test_dynamic.html88
-rw-r--r--accessible/tests/mochitest/text/test_general.xul80
-rw-r--r--accessible/tests/mochitest/text/test_gettext.html112
-rw-r--r--accessible/tests/mochitest/text/test_hypertext.html147
-rw-r--r--accessible/tests/mochitest/text/test_lineboundary.html265
-rw-r--r--accessible/tests/mochitest/text/test_passwords.html60
-rw-r--r--accessible/tests/mochitest/text/test_selection.html101
-rw-r--r--accessible/tests/mochitest/text/test_wordboundary.html291
-rw-r--r--accessible/tests/mochitest/text/test_words.html133
-rw-r--r--accessible/tests/mochitest/textattrs/a11y.ini7
-rw-r--r--accessible/tests/mochitest/textattrs/test_general.html735
-rw-r--r--accessible/tests/mochitest/textattrs/test_invalid.html62
-rw-r--r--accessible/tests/mochitest/textcaret/a11y.ini6
-rw-r--r--accessible/tests/mochitest/textcaret/test_browserui.xul67
-rw-r--r--accessible/tests/mochitest/textcaret/test_general.html183
-rw-r--r--accessible/tests/mochitest/textrange/a11y.ini7
-rw-r--r--accessible/tests/mochitest/textrange/test_general.html108
-rw-r--r--accessible/tests/mochitest/textrange/test_selection.html120
-rw-r--r--accessible/tests/mochitest/textselection/a11y.ini6
-rw-r--r--accessible/tests/mochitest/textselection/test_general.html221
-rw-r--r--accessible/tests/mochitest/textselection/test_userinput.html95
-rw-r--r--accessible/tests/mochitest/tree/a11y.ini51
-rw-r--r--accessible/tests/mochitest/tree/dockids.html30
-rw-r--r--accessible/tests/mochitest/tree/test_applicationacc.xul74
-rw-r--r--accessible/tests/mochitest/tree/test_aria_globals.html129
-rw-r--r--accessible/tests/mochitest/tree/test_aria_grid.html279
-rw-r--r--accessible/tests/mochitest/tree/test_aria_imgmap.html108
-rw-r--r--accessible/tests/mochitest/tree/test_aria_list.html92
-rw-r--r--accessible/tests/mochitest/tree/test_aria_menu.html93
-rw-r--r--accessible/tests/mochitest/tree/test_aria_owns.html187
-rw-r--r--accessible/tests/mochitest/tree/test_aria_presentation.html179
-rw-r--r--accessible/tests/mochitest/tree/test_aria_table.html63
-rw-r--r--accessible/tests/mochitest/tree/test_brokencontext.html265
-rw-r--r--accessible/tests/mochitest/tree/test_button.xul73
-rw-r--r--accessible/tests/mochitest/tree/test_canvas.html55
-rw-r--r--accessible/tests/mochitest/tree/test_combobox.xul291
-rw-r--r--accessible/tests/mochitest/tree/test_cssflexbox.html80
-rw-r--r--accessible/tests/mochitest/tree/test_cssoverflow.html146
-rw-r--r--accessible/tests/mochitest/tree/test_dochierarchy.html86
-rw-r--r--accessible/tests/mochitest/tree/test_dockids.html65
-rw-r--r--accessible/tests/mochitest/tree/test_filectrl.html58
-rw-r--r--accessible/tests/mochitest/tree/test_formctrl.html132
-rw-r--r--accessible/tests/mochitest/tree/test_formctrl.xul130
-rw-r--r--accessible/tests/mochitest/tree/test_gencontent.html71
-rw-r--r--accessible/tests/mochitest/tree/test_groupbox.xul64
-rw-r--r--accessible/tests/mochitest/tree/test_iframe.html52
-rw-r--r--accessible/tests/mochitest/tree/test_img.html88
-rw-r--r--accessible/tests/mochitest/tree/test_invalid_img.xhtml50
-rw-r--r--accessible/tests/mochitest/tree/test_invalidationlist.html57
-rw-r--r--accessible/tests/mochitest/tree/test_list.html247
-rw-r--r--accessible/tests/mochitest/tree/test_map.html83
-rw-r--r--accessible/tests/mochitest/tree/test_media.html84
-rw-r--r--accessible/tests/mochitest/tree/test_select.html139
-rw-r--r--accessible/tests/mochitest/tree/test_tabbox.xul99
-rw-r--r--accessible/tests/mochitest/tree/test_tabbrowser.xul255
-rw-r--r--accessible/tests/mochitest/tree/test_table.html282
-rw-r--r--accessible/tests/mochitest/tree/test_tree.xul182
-rw-r--r--accessible/tests/mochitest/tree/test_txtcntr.html234
-rw-r--r--accessible/tests/mochitest/tree/test_txtctrl.html173
-rw-r--r--accessible/tests/mochitest/tree/test_txtctrl.xul219
-rw-r--r--accessible/tests/mochitest/tree/wnd.xul8
-rw-r--r--accessible/tests/mochitest/treeupdate/a11y.ini41
-rw-r--r--accessible/tests/mochitest/treeupdate/test_ariadialog.html119
-rw-r--r--accessible/tests/mochitest/treeupdate/test_ariaowns.html693
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1040735.html42
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1100602.html114
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1175913.html105
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1189277.html86
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1276857.html143
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug852150.xhtml59
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug883708.xhtml33
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug884251.xhtml21
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug895082.html51
-rw-r--r--accessible/tests/mochitest/treeupdate/test_canvas.html92
-rw-r--r--accessible/tests/mochitest/treeupdate/test_colorpicker.xul150
-rw-r--r--accessible/tests/mochitest/treeupdate/test_contextmenu.xul317
-rw-r--r--accessible/tests/mochitest/treeupdate/test_cssoverflow.html143
-rw-r--r--accessible/tests/mochitest/treeupdate/test_deck.xul109
-rw-r--r--accessible/tests/mochitest/treeupdate/test_doc.html466
-rw-r--r--accessible/tests/mochitest/treeupdate/test_gencontent.html160
-rw-r--r--accessible/tests/mochitest/treeupdate/test_general.html150
-rw-r--r--accessible/tests/mochitest/treeupdate/test_hidden.html135
-rw-r--r--accessible/tests/mochitest/treeupdate/test_imagemap.html442
-rw-r--r--accessible/tests/mochitest/treeupdate/test_list.html152
-rw-r--r--accessible/tests/mochitest/treeupdate/test_list_editabledoc.html106
-rw-r--r--accessible/tests/mochitest/treeupdate/test_listbox.xul180
-rw-r--r--accessible/tests/mochitest/treeupdate/test_menu.xul128
-rw-r--r--accessible/tests/mochitest/treeupdate/test_menubutton.xul198
-rw-r--r--accessible/tests/mochitest/treeupdate/test_optgroup.html137
-rw-r--r--accessible/tests/mochitest/treeupdate/test_recreation.html155
-rw-r--r--accessible/tests/mochitest/treeupdate/test_select.html130
-rw-r--r--accessible/tests/mochitest/treeupdate/test_shutdown.xul132
-rw-r--r--accessible/tests/mochitest/treeupdate/test_table.html81
-rw-r--r--accessible/tests/mochitest/treeupdate/test_textleaf.html180
-rw-r--r--accessible/tests/mochitest/treeupdate/test_visibility.html437
-rw-r--r--accessible/tests/mochitest/treeupdate/test_whitespace.html187
-rw-r--r--accessible/tests/mochitest/treeview.css15
-rw-r--r--accessible/tests/mochitest/treeview.js289
-rw-r--r--accessible/tests/mochitest/value.js32
-rw-r--r--accessible/tests/mochitest/value/a11y.ini9
-rw-r--r--accessible/tests/mochitest/value/test_general.html159
-rw-r--r--accessible/tests/mochitest/value/test_number.html59
-rw-r--r--accessible/tests/mochitest/value/test_progress.html61
-rw-r--r--accessible/tests/mochitest/value/test_progress.xul72
-rw-r--r--accessible/tests/mochitest/value/test_range.html59
431 files changed, 66338 insertions, 0 deletions
diff --git a/accessible/tests/browser/.eslintrc.js b/accessible/tests/browser/.eslintrc.js
new file mode 100644
index 000000000..53425abc4
--- /dev/null
+++ b/accessible/tests/browser/.eslintrc.js
@@ -0,0 +1,218 @@
+"use strict";
+
+module.exports = { // eslint-disable-line no-undef
+ "extends": [
+ "../../../testing/mochitest/browser.eslintrc.js"
+ ],
+ // All globals made available in the test environment.
+ "globals": {
+ // Content scripts have global 'content' object
+ "content": true,
+
+ "add_task": true,
+
+ // Defined in accessible/tests/mochitest/ common.js, name.js, states.js
+ "prettyName": true,
+ "statesToString": true,
+ "eventTypeToString": true,
+ "testAttrs": true,
+ "testAbsentAttrs": true,
+ "testName": true,
+ "testDescr": true,
+ "testStates": true,
+ "testRelation": true,
+ "testValue": true,
+ "testAccessibleTree": true,
+ "isAccessible": true,
+ "getAccessibleDOMNodeID": true,
+
+ // Defined for all top level accessibility browser tests.
+ "setE10sPrefs": true,
+ "unsetE10sPrefs": true,
+ "initPromise": true,
+ "shutdownPromise": true,
+ "forceGC": true,
+
+ // Defined for all e10s accessibility browser tests.
+ "addAccessibleTask": true,
+ "BrowserTestUtils": true,
+ "ContentTask": true,
+ "gBrowser": true,
+ "isDefunct": true,
+ "loadScripts": true,
+ "loadFrameScripts": true,
+ "Logger": true,
+ "MOCHITESTS_DIR": true,
+ "waitForEvent": true,
+ "waitForMultipleEvents": true,
+ "invokeSetAttribute": true,
+ "invokeSetStyle": true,
+ "invokeFocus": true,
+ "findAccessibleChildByID": true
+ },
+ "rules": {
+ "mozilla/no-aArgs": "warn",
+ "mozilla/no-cpows-in-tests": "warn",
+ "mozilla/reject-importGlobalProperties": "warn",
+ "mozilla/var-only-at-top-level": "warn",
+
+ "block-scoped-var": "error",
+ "brace-style": ["error", "1tbs"],
+ "camelcase": "error",
+ "comma-dangle": ["error", "never"],
+ "comma-spacing": "error",
+ "comma-style": ["error", "last"],
+ "complexity": ["error", 35],
+ "consistent-this": "off",
+ "curly": ["error", "multi-line"],
+ "default-case": "off",
+ "dot-location": ["error", "property"],
+ "dot-notation": "error",
+ "eol-last": "error",
+ "eqeqeq": "off",
+ "func-names": "off",
+ "func-style": "off",
+ "generator-star": "off",
+ "global-strict": "off",
+ "handle-callback-err": ["error", "er"],
+ "indent": ["error", 2, {"SwitchCase": 1}],
+ "key-spacing": ["error", {"beforeColon": false, "afterColon": true}],
+ "linebreak-style": "off",
+ "max-depth": "off",
+ "max-nested-callbacks": ["error", 4],
+ "max-params": "off",
+ "max-statements": "off",
+ "new-cap": ["error", {"capIsNew": false}],
+ "new-parens": "error",
+ "no-array-constructor": "error",
+ "no-bitwise": "off",
+ "no-caller": "error",
+ "no-catch-shadow": "error",
+ "no-comma-dangle": "off",
+ "no-cond-assign": "error",
+ "no-console": "off",
+ "no-constant-condition": "off",
+ "no-continue": "off",
+ "no-control-regex": "error",
+ "no-debugger": "error",
+ "no-delete-var": "error",
+ "no-div-regex": "off",
+ "no-dupe-args": "error",
+ "no-dupe-keys": "error",
+ "no-duplicate-case": "error",
+ "no-else-return": "error",
+ "no-empty": "error",
+ "no-empty-character-class": "error",
+ "no-eval": "error",
+ "no-ex-assign": "error",
+ "no-extend-native": "error",
+ "no-extra-bind": "error",
+ "no-extra-boolean-cast": "error",
+ "no-extra-parens": "off",
+ "no-extra-semi": "error",
+ "no-extra-strict": "off",
+ "no-fallthrough": "error",
+ "no-floating-decimal": "off",
+ "no-inline-comments": "off",
+ "no-lonely-if": "error",
+ "no-mixed-requires": "off",
+ "no-mixed-spaces-and-tabs": "error",
+ "no-multi-spaces": "error",
+ "no-multi-str": "error",
+ "no-multiple-empty-lines": ["error", {"max": 1}],
+ "no-native-reassign": "error",
+ "no-nested-ternary": "error",
+ "no-new-require": "off",
+ "no-octal": "error",
+ "no-param-reassign": "off",
+ "no-path-concat": "off",
+ "no-plusplus": "off",
+ "no-process-env": "off",
+ "no-process-exit": "off",
+ "no-proto": "error",
+ "no-redeclare": "error",
+ "no-regex-spaces": "error",
+ "no-reserved-keys": "off",
+ "no-restricted-modules": "off",
+ "no-return-assign": "error",
+ "no-script-url": "off",
+ "no-self-compare": "error",
+ "no-sequences": "error",
+ "no-shadow": "error",
+ "no-shadow-restricted-names": "error",
+ "no-space-before-semi": "off",
+ "no-spaced-func": "error",
+ "no-sparse-arrays": "error",
+ "no-sync": "off",
+ "no-ternary": "off",
+ "no-throw-literal": "error",
+ "no-trailing-spaces": "error",
+ "no-undef": "error",
+ "no-underscore-dangle": "off",
+ "no-undefined": "off",
+ "no-unneeded-ternary": "error",
+ "no-unreachable": "error",
+ "no-unused-vars": ["error", {"vars": "all", "args": "none"}],
+ "no-use-before-define": "off",
+ "no-var": "off",
+ "no-warning-comments": "off",
+ "no-with": "error",
+ "object-shorthand": "off",
+ "one-var": ["error", "never"],
+ "padded-blocks": ["error", "never"],
+ "quote-props": "off",
+ "radix": "error",
+ "semi": ["error", "always"],
+ "semi-spacing": ["error", {"before": false, "after": true}],
+ "sort-vars": "off",
+ "space-after-function-name": "off",
+ "keyword-spacing": "error",
+ "space-before-blocks": "error",
+ "space-before-function-parentheses": "off",
+ "space-before-function-paren": ["error", "never"],
+ "space-in-brackets": "off",
+ "space-in-parens": ["error", "never"],
+ "space-infix-ops": ["error", {"int32Hint": true}],
+ "space-unary-ops": ["error", { "words": true, "nonwords": false }],
+ "space-unary-word-ops": "off",
+ "spaced-comment": ["error", "always"],
+ "strict": ["error", "global"],
+ "use-isnan": "error",
+ "valid-jsdoc": "off",
+ "valid-typeof": "error",
+ "vars-on-top": "off",
+ "wrap-iife": "off",
+ "wrap-regex": "off",
+ "yoda": "error",
+
+ "guard-for-in": "off",
+ "newline-after-var": "off",
+ "no-alert": "off",
+ "no-eq-null": "off",
+ "no-func-assign": "off",
+ "no-implied-eval": "off",
+ "no-inner-declarations": "off",
+ "no-invalid-regexp": "off",
+ "no-irregular-whitespace": "off",
+ "no-iterator": "off",
+ "no-label-var": "off",
+ "no-labels": "error",
+ "no-lone-blocks": "off",
+ "no-loop-func": "off",
+ "no-negated-in-lhs": "off",
+ "no-new": "off",
+ "no-new-func": "off",
+ "no-new-object": "off",
+ "no-new-wrappers": "off",
+ "no-obj-calls": "off",
+ "no-octal-escape": "off",
+ "no-undef-init": "error",
+ "no-unexpected-multiline": "error",
+ "object-curly-spacing": "off",
+ "no-unused-expressions": "off",
+ "no-void": "off",
+ "no-wrap-func": "off",
+ "operator-assignment": "off",
+ "operator-linebreak": ["error", "after"]
+ }
+};
diff --git a/accessible/tests/browser/browser.ini b/accessible/tests/browser/browser.ini
new file mode 100644
index 000000000..402deda24
--- /dev/null
+++ b/accessible/tests/browser/browser.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+
+support-files =
+ head.js
+ shared-head.js
+
+[browser_shutdown_multi_reference.js]
+[browser_shutdown_parent_own_reference.js]
+skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_remote_no_reference.js]
+skip-if = !e10s # 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]
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 000000000..e0ba3ce6b
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_multi_reference.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';
+
+add_task(function* () {
+ info('Creating a service');
+ // Create a11y service.
+ let a11yInit = initPromise();
+ let accService1 = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield 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 canShutdonw 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.
+ let a11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise().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.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ // 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();
+ yield 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 000000000..9895bd2c7
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_parent_own_reference.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';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield 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.
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+
+ 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.
+ let contentA11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise(browser).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.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+
+ // Have some breathing room between a11y service shutdowns.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ 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.
+ let parentA11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed in parent');
+ // Force garbage collection that should trigger shutdown in both parent and
+ // content.
+ forceGC();
+ yield Promise.all([parentA11yShutdown, contentA11yShutdown]);
+
+ // Unsetting e10s related preferences.
+ yield 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 000000000..b066c2592
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_no_reference.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';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield Promise.all([parentA11yInit, contentA11yInit]);
+
+ info('Removing a service in parent and waiting for service to be shut ' +
+ 'down in content');
+ // Remove a11y service reference in the main process.
+ let parentA11yShutdown = shutdownPromise();
+ let contentA11yShutdown = shutdownPromise(browser);
+ accService = null;
+ ok(!accService, 'Service is removed in parent');
+ // Force garbage collection that should trigger shutdown in both main and
+ // content process.
+ forceGC();
+ yield Promise.all([parentA11yShutdown, contentA11yShutdown]);
+ });
+
+ // Unsetting e10s related preferences.
+ yield 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 000000000..aab497678
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_only.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in content');
+ // Create a11y service in the content process.
+ let a11yInit = initPromise(browser);
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+ yield a11yInit;
+
+ info('Removing a service in content');
+ // Remove a11y service reference from the content process.
+ let a11yShutdown = shutdownPromise(browser);
+ // Force garbage collection that should trigger shutdown.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+ yield a11yShutdown;
+
+ // Unsetting e10s related preferences.
+ yield 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 000000000..429737a81
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_own_reference.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';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield 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.
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+
+ info('Shutting down a service in parent and making sure the one in ' +
+ 'content stays alive');
+ let contentCanShutdown = false;
+ let parentA11yShutdown = shutdownPromise();
+ // 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.
+ let contentA11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise(browser).then(flag => contentCanShutdown ?
+ resolve() : reject('Accessible service was shut down incorrectly')));
+ // 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();
+ loadFrameScripts(browser, `Components.utils.forceGC();`);
+ yield parentA11yShutdown;
+
+ // Have some breathing room between a11y service shutdowns.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ info('Removing a service in content');
+ // Now allow a11y service to shutdown in content.
+ contentCanShutdown = true;
+ // Remove last reference to a11y service in content and force garbage
+ // collection that should trigger shutdown.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+ yield contentA11yShutdown;
+
+ // Unsetting e10s related preferences.
+ yield 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 000000000..be9c63d46
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_scope_lifecycle.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';
+
+add_task(function* () {
+ // Create a11y service inside of the function scope. Its reference should be
+ // released once the anonimous function is called.
+ let a11yInitThenShutdown = initPromise().then(shutdownPromise);
+
+ (function() {
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized');
+ })();
+
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield 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 000000000..53bd4daee
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_start_restart.js
@@ -0,0 +1,41 @@
+/* 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(function* () {
+ info('Creating a service');
+ // Create a11y service.
+ let a11yInit = initPromise();
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield a11yInit;
+ ok(accService, 'Service initialized');
+
+ info('Removing a service');
+ // Remove the only reference to an a11y service.
+ let a11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed');
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yShutdown;
+
+ info('Recreating a service');
+ // Re-create a11y service.
+ a11yInit = initPromise();
+ accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield a11yInit;
+ ok(accService, 'Service initialized again');
+
+ info('Removing a service again');
+ // Remove the only reference to an a11y service again.
+ a11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed again');
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yShutdown;
+});
diff --git a/accessible/tests/browser/e10s/browser.ini b/accessible/tests/browser/e10s/browser.ini
new file mode 100644
index 000000000..e0ca44dde
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -0,0 +1,51 @@
+[DEFAULT]
+skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
+support-files =
+ events.js
+ 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/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]
+[browser_caching_relations.js]
+[browser_caching_states.js]
+[browser_caching_value.js]
+
+# Events tests
+[browser_events_caretmove.js]
+[browser_events_hide.js]
+[browser_events_show.js]
+[browser_events_statechange.js]
+[browser_events_textchange.js]
+
+# Tree update tests
+[browser_treeupdate_ariadialog.js]
+[browser_treeupdate_ariaowns.js]
+[browser_treeupdate_canvas.js]
+[browser_treeupdate_cssoverflow.js]
+[browser_treeupdate_doc.js]
+[browser_treeupdate_gencontent.js]
+[browser_treeupdate_hidden.js]
+[browser_treeupdate_imagemap.js]
+skip-if = e10s # Bug 1318569
+[browser_treeupdate_list.js]
+[browser_treeupdate_list_editabledoc.js]
+[browser_treeupdate_listener.js]
+[browser_treeupdate_optgroup.js]
+[browser_treeupdate_removal.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 000000000..449ca9d91
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_attributes.js
@@ -0,0 +1,117 @@
+/* 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';
+
+/* global EVENT_FOCUS */
+
+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'
+};
+
+/**
+ * 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 {?Function*} an optional action that yields 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',
+ action: function*(browser) {
+ yield 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(`
+ <input id="textbox" value="hello">`,
+ 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) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, 'textbox', attr, value);
+ }
+ }
+
+ yield onUpdate;
+ testAttrs(textbox, expected);
+ testAbsentAttrs(textbox, unexpected);
+ }
+ }
+);
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 000000000..18ee58bd0
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_description.js
@@ -0,0 +1,164 @@
+/* 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';
+
+/* global EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_REORDER */
+
+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: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: '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: [{ eventType: EVENT_REORDER, id: 'body' }],
+ expected: ''
+}, {
+ desc: 'Description from @aria-describedby attribute when @alt and ' +
+ '@aria-describedby are not the same',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description2'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: '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: [{ eventType: EVENT_REORDER, id: 'body' }],
+ 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: [{ eventType: EVENT_NAME_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'No description with only @title attribute which is used as the name',
+ attrs: [{
+ attr: 'aria-describedby'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'Description from @title attribute when @alt and @atitle are not the ' +
+ 'same',
+ attrs: [{
+ attr: 'alt',
+ value: 'aria description'
+ }],
+ waitFor: [{ eventType: EVENT_REORDER, id: 'body' }],
+ 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: [{ eventType: EVENT_NAME_CHANGE, id: '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: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: '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: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: '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: [{ eventType: EVENT_NAME_CHANGE, id: '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: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: 'another description'
+}];
+
+/**
+ * Test caching of accessible object description
+ */
+addAccessibleTask(`
+ <p id="description">aria description</p>
+ <p id="description2">another description</p>
+ <img id="image" />`,
+ function*(browser, accDoc) {
+ let imgAcc = findAccessibleChildByID(accDoc, 'image');
+
+ for (let { desc, waitFor, attrs, expected } of tests) {
+ info(desc);
+ let onUpdate;
+ if (waitFor) {
+ onUpdate = waitForMultipleEvents(waitFor);
+ }
+ if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, 'image', attr, value);
+ }
+ }
+ yield onUpdate;
+ // When attribute change (alt) triggers reorder event, accessible will
+ // become defunct.
+ if (isDefunct(imgAcc)) {
+ imgAcc = findAccessibleChildByID(accDoc, 'image');
+ }
+ testDescr(imgAcc, expected);
+ }
+ }
+);
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 000000000..08e635014
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_name.js
@@ -0,0 +1,434 @@
+/* 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';
+
+/* global EVENT_REORDER, EVENT_TEXT_INSERTED */
+
+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:
+ * * recreated - subrtee is recreated and the test should only continue
+ * after a reorder event
+ * * textchanged - text is inserted into a subtree and the test should only
+ * continue after a text inserted event
+ */
+const ARIARule = [{ attr: 'aria-labelledby' }, { attr: 'aria-label' }];
+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', recreated: true }, { attr: 'title' }],
+ HTMLImgEmptyAlt: [...ARIARule, { attr: 'title' }, { attr: 'alt' }],
+ HTMLInputButton: [...HTMLControlHeadRule, { attr: 'value' },
+ { attr: 'title' }],
+ HTMLInputImage: [...HTMLControlHeadRule, { attr: 'alt', recreated: true },
+ { attr: 'value', recreated: true }, { attr: 'title' }],
+ HTMLInputImageNoValidSrc: [...HTMLControlHeadRule,
+ { attr: 'alt', recreated: true }, { attr: 'value', recreated: true }],
+ HTMLInputReset: [...HTMLControlHeadRule,
+ { attr: 'value', textchanged: true }],
+ HTMLInputSubmit: [...HTMLControlHeadRule,
+ { attr: 'value', textchanged: true }],
+ 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: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn">test4</label>
+ <button id="btn"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5">press me</button>`,
+ expected: ['test2 test3', 'test1', 'test4', 'press me', 'test5']
+}, {
+ id: 'btn',
+ ruleset: 'HTMLInputButton',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn">test4</label>
+ <input id="btn"
+ type="button"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from al"
+ src="no name from src"
+ data="no name from data"
+ title="name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value',
+ 'name from title']
+}, {
+ id: 'btn-submit',
+ ruleset: 'HTMLInputSubmit',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-submit">test4</label>
+ <input id="btn-submit"
+ type="submit"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from atl"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value']
+}, {
+ id: 'btn-reset',
+ ruleset: 'HTMLInputReset',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-reset">test4</label>
+ <input id="btn-reset"
+ type="reset"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from alt"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value']
+}, {
+ id: 'btn-image',
+ ruleset: 'HTMLInputImage',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-image">test4</label>
+ <input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"
+ data="no name from data"
+ title="name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from alt',
+ 'name from value', 'name from title']
+}, {
+ id: 'btn-image',
+ ruleset: 'HTMLInputImageNoValidSrc',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-image">test4</label>
+ <input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from alt',
+ 'name from value']
+}, {
+ id: 'opt',
+ ruleset: 'HTMLOption',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <select>
+ <option id="opt"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ label="test4"
+ title="test5">option1</option>
+ <option>option2</option>
+ </select>`,
+ expected: ['test2 test3', 'test1', 'test4', 'option1', 'test5']
+}, {
+ id: 'img',
+ ruleset: 'HTMLImg',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <img id="img"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ alt="Mozilla logo"
+ title="This is a logo"
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
+ expected: ['test2 test3', 'Logo of Mozilla', 'Mozilla logo', 'This is a logo']
+}, {
+ id: 'imgemptyalt',
+ ruleset: 'HTMLImgEmptyAlt',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <img id="imgemptyalt"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ title="This is a logo"
+ alt=""
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
+ expected: ['test2 test3', 'Logo of Mozilla', 'This is a logo', '']
+}, {
+ id: 'tc',
+ ruleset: 'HTMLElm',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="tc">test4</label>
+ <table>
+ <tr>
+ <td id="tc"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5">
+ <p>This is a paragraph</p>
+ <a href="#">This is a link</a>
+ <ul>
+ <li>This is a list</li>
+ </ul>
+ </td>
+ </tr>
+ </table>`,
+ expected: ['test2 test3', 'test1', 'test5']
+}, {
+ id: 'gc',
+ ruleset: 'HTMLARIAGridCell',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="gc">test4</label>
+ <table>
+ <tr>
+ <td id="gc"
+ role="gridcell"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="This is a paragraph This is a link This is a list">
+ <p>This is a paragraph</p>
+ <a href="#">This is a link</a>
+ <ul>
+ <li>Listitem1</li>
+ <li>Listitem2</li>
+ </ul>
+ </td>
+ </tr>
+ </table>`,
+ expected: ['test2 test3', 'test1', 'This is a paragraph',
+ 'This is a paragraph This is a link This is a list']
+}, {
+ id: 't',
+ ruleset: 'HTMLTable',
+ markup: `
+ <span id="l1">lby_tst6_1</span>
+ <span id="l2">lby_tst6_2</span>
+ <label for="t">label_tst6</label>
+ <table id="t"
+ aria-label="arialabel_tst6"
+ aria-labelledby="l1 l2"
+ summary="summary_tst6"
+ title="title_tst6">
+ <caption>caption_tst6</caption>
+ <tr>
+ <td>cell1</td>
+ <td>cell2</td>
+ </tr>
+ </table>`,
+ expected: ['lby_tst6_1 lby_tst6_2', 'arialabel_tst6', 'caption_tst6',
+ 'summary_tst6', 'title_tst6']
+}, {
+ id: 'btn',
+ ruleset: 'CSSContent',
+ markup: `
+ <style>
+ button::before {
+ content: "do not ";
+ }
+ </style>
+ <button id="btn">press me</button>`,
+ expected: ['do not press me', 'press me']
+}, {
+ // TODO: uncomment when Bug-1256382 is resoved.
+ // id: 'li',
+ // ruleset: 'CSSContent',
+ // markup: `
+ // <style>
+ // ul {
+ // list-style-type: decimal;
+ // }
+ // </style>
+ // <ul id="ul">
+ // <li id="li">Listitem</li>
+ // </ul>`,
+ // expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`]
+// }, {
+ id: 'a',
+ ruleset: 'HTMLLink',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <a id="a"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test4">test5</a>`,
+ expected: ['test2 test3', 'test1', 'test5', 'test4']
+}, {
+ id: 'a-img',
+ ruleset: 'HTMLLinkImage',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <a id="a-img"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test4"><img alt="test5"/></a>`,
+ expected: ['test2 test3', 'test1', 'test5', 'test4']
+}];
+
+/**
+ * Wait for an accessible event to happen and, in case given accessible is
+ * defunct, update it to one that is attached to the accessible event.
+ * @param {Promise} onEvent accessible event promise
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ */
+function* updateAccessibleIfNeeded(onEvent, target) {
+ let event = yield onEvent;
+ if (isDefunct(target.acc)) {
+ target.acc = findAccessibleChildByID(event.accessible, target.id);
+ }
+}
+
+/**
+ * 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
+ */
+function* testAttrRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent;
+ if (rule.recreated) {
+ onEvent = waitForEvent(EVENT_REORDER, target.parent);
+ } else if (rule.textchanged) {
+ onEvent = waitForEvent(EVENT_TEXT_INSERTED, target.id);
+ }
+ yield invokeSetAttribute(browser, target.id, rule.attr);
+ if (onEvent) {
+ yield updateAccessibleIfNeeded(onEvent, target);
+ }
+}
+
+/**
+ * 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
+ */
+function* testElmRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent = waitForEvent(EVENT_REORDER, rule.isSibling ?
+ target.parent : target.id);
+ yield ContentTask.spawn(browser, rule.elm, elm =>
+ content.document.querySelector(`${elm}`).remove());
+ yield updateAccessibleIfNeeded(onEvent, target);
+}
+
+/**
+ * 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
+ */
+function* testSubtreeRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent = waitForEvent(EVENT_REORDER, target.id);
+ yield ContentTask.spawn(browser, target.id, id => {
+ let elm = content.document.getElementById(id);
+ while (elm.firstChild) {
+ elm.removeChild(elm.firstChild);
+ }
+ });
+ yield updateAccessibleIfNeeded(onEvent, target);
+}
+
+/**
+ * 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
+ */
+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;
+ }
+ yield testFn(browser, target, rule, expected[i]);
+ }
+}
+
+markupTests.forEach(({ id, ruleset, markup, expected }) =>
+ addAccessibleTask(markup, function*(browser, accDoc) {
+ // 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 };
+ yield testNameRule(browser, target, rules[ruleset], expected);
+ }));
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 000000000..772aee96a
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_relations.js
@@ -0,0 +1,86 @@
+/* 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';
+
+/* global RELATION_LABELLED_BY, RELATION_LABEL_FOR, RELATION_DESCRIBED_BY,
+ RELATION_DESCRIPTION_FOR, RELATION_CONTROLLER_FOR,
+ RELATION_CONTROLLED_BY, RELATION_FLOWS_TO, RELATION_FLOWS_FROM */
+
+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]
+];
+
+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) {
+ yield 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(`
+ <div id="dependant1">label</div>
+ <div id="dependant2">label2</div>
+ <div role="checkbox" id="host"></div>`,
+ function* (browser, accDoc) {
+ for (let spec of attrRelationsSpec) {
+ yield testRelated(browser, accDoc, ...spec);
+ }
+ }
+);
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 000000000..69e4931ea
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_states.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';
+
+/* global EVENT_STATE_CHANGE, STATE_CHECKED, STATE_BUSY, STATE_REQUIRED,
+ STATE_INVALID, EXT_STATE_ENABLED */
+
+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]
+}];
+
+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) {
+ yield invokeSetAttribute(browser, id, attr, value);
+ }
+ yield onUpdate;
+ testStates(acc, ...expected);
+ }
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask(`
+ <input id="checkbox" type="checkbox">
+ <input id="file" type="file">
+ <input id="text">`,
+ function* (browser, accDoc) {
+ yield runStateTests(browser, accDoc, 'checkbox', attributeTests);
+ yield runStateTests(browser, accDoc, 'file', ariaTests);
+ yield runStateTests(browser, accDoc, 'text', extraStateTests);
+ }
+);
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 000000000..2669cbfab
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_value.js
@@ -0,0 +1,155 @@
+/* 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';
+
+/* global nsIAccessibleValue, EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE */
+
+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 {?Function*} an optional action that yields 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',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'select');
+ yield BrowserTestUtils.synthesizeKey('3', {}, browser);
+ },
+ 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!', 0, 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',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'range');
+ yield BrowserTestUtils.synthesizeKey('VK_LEFT', {}, browser);
+ },
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: '5'
+}];
+
+/**
+ * Test caching of accessible object values
+ */
+addAccessibleTask(`
+ <div id="slider" role="slider" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="7">slider</div>
+ <select id="select">
+ <option>1st</option>
+ <option>2nd</option>
+ <option>3rd</option>
+ </select>
+ <input id="combobox" role="combobox" aria-autocomplete="inline">
+ <progress id="progress" value="22" max="100"></progress>
+ <input type="range" id="range" min="0" max="10" value="6">`,
+ 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) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, id, attr, value);
+ }
+ }
+
+ yield onUpdate;
+ if (Array.isArray(expected)) {
+ acc.QueryInterface(nsIAccessibleValue);
+ testValue(acc, ...expected);
+ } else {
+ is(acc.value, expected, `Correct value for ${prettyName(acc)}`);
+ }
+ }
+ }
+);
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 000000000..506945f30
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_caretmove.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/. */
+
+/* global EVENT_TEXT_CARET_MOVED, nsIAccessibleCaretMoveEvent */
+
+'use strict';
+
+/**
+ * Test caret move event and its interface:
+ * - caretOffset
+ */
+addAccessibleTask('<input id="textbox" value="hello"/>', function*(browser) {
+ let onCaretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, 'textbox');
+ yield invokeFocus(browser, 'textbox');
+ let event = yield onCaretMoved;
+
+ let caretMovedEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent);
+ is(caretMovedEvent.caretOffset, 5,
+ 'Correct caret offset.');
+});
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 000000000..bb9ee3961
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_hide.js
@@ -0,0 +1,35 @@
+/* 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/. */
+
+/* global EVENT_HIDE */
+
+'use strict';
+
+/**
+ * Test hide event and its interface:
+ * - targetParent
+ * - targetNextSibling
+ * - targetPrevSibling
+ */
+addAccessibleTask(`
+ <div id="parent">
+ <div id="previous"></div>
+ <div id="to-hide"></div>
+ <div id="next"></div>
+ </div>`,
+ function*(browser, accDoc) {
+ let acc = findAccessibleChildByID(accDoc, 'to-hide');
+ let onHide = waitForEvent(EVENT_HIDE, acc);
+ yield invokeSetStyle(browser, 'to-hide', 'visibility', 'hidden');
+ let event = yield 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.');
+ }
+);
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 000000000..5003c14f7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_show.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+/* global EVENT_SHOW */
+
+'use strict';
+
+/**
+ * Test show event
+ */
+addAccessibleTask('<div id="div" style="visibility: hidden;"></div>',
+ function*(browser) {
+ let onShow = waitForEvent(EVENT_SHOW, 'div');
+ yield invokeSetStyle(browser, 'div', 'visibility', 'visible');
+ yield onShow;
+ });
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 000000000..7f353efa7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_statechange.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+/* global STATE_CHECKED, EXT_STATE_EDITABLE, nsIAccessibleStateChangeEvent,
+ EVENT_STATE_CHANGE */
+
+'use strict';
+
+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,
+ <html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Inner Iframe</title>
+ </head>
+ <body id='iframe'></body>
+ </html>`;
+
+/**
+ * Test state change event and its interface:
+ * - state
+ * - isExtraState
+ * - isEnabled
+ */
+addAccessibleTask(`
+ <iframe id="iframe" src="${iframeSrc}"></iframe>
+ <input id="checkbox" type="checkbox" />`, function*(browser) {
+ // Test state change
+ let onStateChange = waitForEvent(EVENT_STATE_CHANGE, 'checkbox');
+ // Set checked for a checkbox.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('checkbox').checked = true;
+ });
+ let event = yield 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.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('iframe').contentDocument.designMode = 'on';
+ });
+ event = yield onStateChange;
+
+ checkStateChangeEvent(event, EXT_STATE_EDITABLE, true, true);
+ testStates(event.accessible, 0, EXT_STATE_EDITABLE);
+});
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 000000000..a1aed52d1
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_textchange.js
@@ -0,0 +1,74 @@
+/* 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/. */
+
+/* global EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
+ nsIAccessibleTextChangeEvent */
+
+'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)}`);
+}
+
+function* changeText(browser, id, value, events) {
+ let onEvents = waitForMultipleEvents(events.map(({ isInserted }) => {
+ let eventType = isInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED;
+ return { id, eventType };
+ }));
+ // Change text in the subtree.
+ yield ContentTask.spawn(browser, [id, value], ([contentId, contentValue]) => {
+ content.document.getElementById(contentId).firstChild.textContent =
+ contentValue;
+ });
+ let resolvedEvents = yield onEvents;
+
+ events.forEach(({ isInserted, str, offset }, idx) =>
+ checkTextChangeEvent(resolvedEvents[idx],
+ id, str, offset, offset + str.length, isInserted, false));
+}
+
+function* removeTextFromInput(browser, id, value, start, end) {
+ let onTextRemoved = waitForEvent(EVENT_TEXT_REMOVED, id);
+ // Select text and delete it.
+ yield ContentTask.spawn(browser, [id, start, end], ([contentId, contentStart, contentEnd]) => {
+ let el = content.document.getElementById(contentId);
+ el.focus();
+ el.setSelectionRange(contentStart, contentEnd);
+ });
+ yield BrowserTestUtils.sendChar('VK_DELETE', browser);
+
+ let event = yield onTextRemoved;
+ checkTextChangeEvent(event, id, value, start, end, false, true);
+}
+
+/**
+ * Test text change event and its interface:
+ * - start
+ * - length
+ * - isInserted
+ * - modifiedText
+ * - isFromUserInput
+ */
+addAccessibleTask(`
+ <p id="p">abc</p>
+ <input id="input" value="input" />`, function*(browser) {
+ let events = [
+ { isInserted: false, str: 'abc', offset: 0 },
+ { isInserted: true, str: 'def', offset: 0 }
+ ];
+ yield changeText(browser, 'p', 'def', events);
+
+ events = [{ isInserted: true, str: 'DEF', offset: 2 }];
+ yield changeText(browser, 'p', 'deDEFf', events);
+
+ // Test isFromUserInput property.
+ yield removeTextFromInput(browser, 'input', 'n', 1, 2);
+});
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 000000000..a9544bc5c
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
@@ -0,0 +1,43 @@
+/* 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';
+
+/* global EVENT_SHOW, ROLE_DIALOG, ROLE_PUSHBUTTON, ROLE_TEXT_LEAF, ROLE_ENTRY,
+ ROLE_DOCUMENT */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+// Test ARIA Dialog
+addAccessibleTask('doc_treeupdate_ariadialog.html', function*(browser, accDoc) {
+ testAccessibleTree(accDoc, {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ });
+
+ // Make dialog visible and update its inner content.
+ let onShow = waitForEvent(EVENT_SHOW, 'dialog');
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('dialog').style.display = 'block';
+ });
+ yield onShow;
+
+ testAccessibleTree(accDoc, {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_DIALOG,
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ children: [ { role: ROLE_TEXT_LEAF } ]
+ },
+ {
+ role: ROLE_ENTRY
+ }
+ ]
+ }
+ ]
+ });
+});
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 000000000..998da32a1
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
@@ -0,0 +1,318 @@
+/* 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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+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);
+ yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
+ yield 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);
+ yield invokeSetAttribute(browser, id, 'aria-owns');
+ yield onReorder;
+
+ // children follow the DOM order
+ tree = {
+ SECTION: [
+ { PUSHBUTTON: [ ] },
+ { CHECKBUTTON: [
+ { SECTION: [] }
+ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Set ARIA owns ========================================= */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
+ yield 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);
+ yield invokeSetAttribute(browser, id, 'aria-owns',
+ 't1_button t1_subdiv t1_group');
+ yield 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);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let div = content.document.createElement('div');
+ div.setAttribute('id', 't1_child3');
+ div.setAttribute('role', 'radio');
+ content.document.getElementById(contentId).appendChild(div);
+ });
+ yield 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);
+ yield ContentTask.spawn(browser, {}, () =>
+ content.document.getElementById('t1_span').parentNode.removeChild(
+ content.document.getElementById('t1_span')));
+ yield 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);
+ yield invokeSetAttribute(browser, 't1_group', 'id');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] } // ARIA owned, t1_button
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Set ID ================================================ */
+ onReorder = waitForEvent(EVENT_REORDER, docID);
+ yield invokeSetAttribute(browser, 't1_grouptmp', 'id', 't1_group');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+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);
+ yield ContentTask.spawn(browser, {}, () =>
+ content.document.getElementById('t2_container2').removeChild(
+ content.document.getElementById('t2_container3')));
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* stealAndRecacheChildren(browser, accDoc) {
+ const id1 = 't3_container1';
+ const id2 = 't3_container2';
+ const acc1 = findAccessibleChildByID(accDoc, id1);
+ const acc2 = findAccessibleChildByID(accDoc, id2);
+
+ /* ================ Steal from other ARIA owns ============================ */
+ let onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield invokeSetAttribute(browser, id2, 'aria-owns', 't3_child');
+ yield onReorder;
+
+ let tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc1, tree);
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }
+ ]
+ };
+ testAccessibleTree(acc2, tree);
+
+ /* ================ Append element to recache children ==================== */
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield ContentTask.spawn(browser, id2, id => {
+ let div = content.document.createElement('div');
+ div.setAttribute('role', 'radio');
+ content.document.getElementById(id).appendChild(div);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc1, tree);
+
+ tree = {
+ SECTION: [
+ { RADIOBUTTON: [ ] },
+ { CHECKBUTTON: [ ] } // ARIA owned
+ ]
+ };
+ testAccessibleTree(acc2, tree);
+}
+
+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);
+ yield invokeSetStyle(browser, 't4_child1', 'display', 'block');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [] },
+ { RADIOBUTTON: [] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+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);
+ yield invokeSetAttribute(browser, id, 'aria-owns', val);
+ yield onReorder;
+
+ let tree = { SECTION: [ ] };
+ for (let role of roleList) {
+ let ch = {};
+ ch[role] = [];
+ tree.SECTION.push(ch);
+ }
+ testAccessibleTree(acc, tree);
+ }
+}
+
+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);
+ yield ContentTask.spawn(browser, id, contentId => {
+ content.document.getElementById(contentId).removeChild(
+ content.document.getElementById('t6_span'));
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { GROUPING: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+addAccessibleTask('doc_treeupdate_ariaowns.html', function*(browser, accDoc) {
+ yield testContainer1(browser, accDoc);
+ yield removeContainer(browser, accDoc);
+ yield stealAndRecacheChildren(browser, accDoc);
+ yield showHiddenElement(browser, accDoc);
+ yield rearrangeARIAOwns(browser, accDoc);
+ yield removeNotARIAOwnedEl(browser, accDoc);
+});
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 000000000..e4b3b70f7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js
@@ -0,0 +1,25 @@
+/* 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';
+
+/* global EVENT_SHOW */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <canvas id="canvas">
+ <div id="dialog" role="dialog" style="display: none;"></div>
+ </canvas>`, function*(browser, accDoc) {
+ let canvas = findAccessibleChildByID(accDoc, 'canvas');
+ let dialog = findAccessibleChildByID(accDoc, 'dialog');
+
+ testAccessibleTree(canvas, { CANVAS: [] });
+
+ let onShow = waitForEvent(EVENT_SHOW, 'dialog');
+ yield invokeSetStyle(browser, 'dialog', 'display', 'block');
+ yield onShow;
+
+ testAccessibleTree(dialog, { DIALOG: [] });
+});
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 000000000..d8b217380
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <div id="container"><div id="scrollarea" style="overflow:auto;"><input>
+ </div></div>
+ <div id="container2"><div id="scrollarea2" style="overflow:hidden;">
+ </div></div>`, function*(browser, accDoc) {
+ const id1 = 'container';
+ const id2 = 'container2';
+ const container = findAccessibleChildByID(accDoc, id1);
+ const container2 = findAccessibleChildByID(accDoc, id2);
+
+ /* ================= Change scroll range ================================== */
+ let tree = {
+ SECTION: [ {// container
+ SECTION: [ {// scroll area
+ ENTRY: [ ] // child content
+ } ]
+ } ]
+ };
+ testAccessibleTree(container, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id1);
+ yield ContentTask.spawn(browser, id1, id => {
+ let doc = content.document;
+ doc.getElementById('scrollarea').style.width = '20px';
+ doc.getElementById(id).appendChild(doc.createElement('input'));
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {// container
+ SECTION: [ {// scroll area
+ ENTRY: [ ] // child content
+ } ]
+ }, {
+ ENTRY: [ ] // inserted input
+ } ]
+ };
+ testAccessibleTree(container, tree);
+
+ /* ================= Change scrollbar styles ============================== */
+ tree = { SECTION: [ ] };
+ testAccessibleTree(container2, tree);
+
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield invokeSetStyle(browser, 'scrollarea2', 'overflow', 'auto');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [] } // scroll area
+ ]
+ };
+ testAccessibleTree(container2, tree);
+});
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 000000000..ccb1d1566
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_doc.js
@@ -0,0 +1,312 @@
+/* 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';
+
+/* global ROLE_PUSHBUTTON, ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_DOCUMENT,
+ nsIAccessibleDocument */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+const iframeSrc = `data:text/html,
+ <html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Inner Iframe</title>
+ </head>
+ <body id='inner-iframe'></body>
+ </html>`;
+
+addAccessibleTask(`
+ <iframe id="iframe" src="${iframeSrc}"></iframe>`, 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);
+ yield ContentTask.spawn(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);
+ });
+ yield 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);
+ yield ContentTask.spawn(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('<body id="${contentId}">hello</body>');
+ document.close();`;
+ docNode.body.appendChild(script);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Replace iframe body ================================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(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', 'button');
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Open iframe document ================================= */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(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('<body id="${contentId}"></body>');`;
+ docNode.body.appendChild(script);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Close iframe document ================================ */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Write and close document.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.write('Works?');
+ docNode.close();
+ });
+ yield 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);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Remove HTML element.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.removeChild(docNode.firstChild);
+ });
+ let event = yield 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);
+ yield ContentTask.spawn(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);
+ });
+ yield 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);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Remove body element.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.documentElement.removeChild(docNode.body);
+ });
+ event = yield 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);
+ yield ContentTask.spawn(browser, {}, () => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let inputNode = content.window.inputNode = docNode.createElement('input');
+ docNode.documentElement.appendChild(inputNode);
+ });
+ event = yield reorderEventPromise;
+
+ ok(event.accessible instanceof nsIAccessibleDocument,
+ 'Reorder should happen on the document');
+ tree = {
+ DOCUMENT: [
+ { ENTRY: [ ] }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ reorderEventPromise = waitForEvent(EVENT_REORDER, iframe);
+ yield ContentTask.spawn(browser, {}, () => {
+ let docEl =
+ content.document.getElementById('iframe').contentDocument.documentElement;
+ // Remove aftermath of this test before next test starts.
+ docEl.removeChild(docEl.firstChild);
+ });
+ // Make sure reorder event was fired and that the input was removed.
+ yield reorderEventPromise;
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Insert body to iframe document ======================= */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(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);
+ });
+ yield 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');
+ yield invokeSetAttribute(browser, 'iframe', 'src',
+ `data:text/html,<html><body id="${id}"><input></body></html>`);
+ event = yield 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);
+ yield ContentTask.spawn(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', 'button');
+ newBodyNode.id = contentId;
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+});
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 000000000..126419288
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <style>
+ .gentext:before {
+ content: "START"
+ }
+ .gentext:after {
+ content: "END"
+ }
+ </style>
+ <div id="container1"></div>
+ <div id="container2"><div id="container2_child">text</div></div>`,
+ 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
+ yield ContentTask.spawn(browser, id1, id => {
+ let node = content.document.createElement('div');
+ node.textContent = 'text';
+ node.setAttribute('class', 'gentext');
+ content.document.getElementById(id).appendChild(node);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [ // inserted node
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ]
+ };
+ testAccessibleTree(container1, tree);
+
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ // Add CSS generated content to an element in container2's subtree
+ yield invokeSetAttribute(browser, 'container2_child', 'class', 'gentext');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container2
+ { SECTION: [ // container2 child
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ]
+ };
+ testAccessibleTree(container2, tree);
+ });
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 000000000..00369ec05
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_hidden.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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* setHidden(browser, value) {
+ let onReorder = waitForEvent(EVENT_REORDER, 'container');
+ yield invokeSetAttribute(browser, 'child', 'hidden', value);
+ yield onReorder;
+}
+
+addAccessibleTask('<div id="container"><input id="child"></div>',
+ function*(browser, accDoc) {
+ let container = findAccessibleChildByID(accDoc, 'container');
+
+ testAccessibleTree(container, { SECTION: [ { ENTRY: [ ] } ] });
+
+ // Set @hidden attribute
+ yield setHidden(browser, 'true');
+ testAccessibleTree(container, { SECTION: [ ] });
+
+ // Remove @hidden attribute
+ yield setHidden(browser);
+ testAccessibleTree(container, { SECTION: [ { ENTRY: [ ] } ] });
+ });
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 000000000..d299c0acb
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
@@ -0,0 +1,176 @@
+/* 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';
+
+/* global EVENT_REORDER, ROLE_LINK */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+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);
+ yield ContentTask.spawn(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);
+ });
+ yield 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);
+ yield ContentTask.spawn(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);
+ });
+ yield 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);
+ yield ContentTask.spawn(browser, {}, () => {
+ let mapNode = content.document.getElementById('map');
+ mapNode.removeChild(mapNode.firstElementChild);
+ });
+ yield onReorder;
+
+ tree = {
+ IMAGE_MAP: [
+ { role: ROLE_LINK, name: 'b', children: [ ] },
+ { role: ROLE_LINK, name: 'c', children: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* testContainer(browser) {
+ const id = 'container';
+ /* ================= Remove name on map =================================== */
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, 'map', 'name');
+ let event = yield onReorder;
+ const acc = event.accessible;
+
+ let tree = {
+ SECTION: [
+ { GRAPHIC: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Restore name on map ================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, 'map', 'name', 'atoz_map');
+ // XXX: force repainting of the image (see bug 745788 for details).
+ yield BrowserTestUtils.synthesizeMouse('#imgmap', 10, 10,
+ { type: 'mousemove' }, browser);
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {
+ IMAGE_MAP: [
+ { LINK: [ ] },
+ { LINK: [ ] }
+ ]
+ } ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Remove map =========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ let mapNode = content.document.getElementById('map');
+ mapNode.parentNode.removeChild(mapNode);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { GRAPHIC: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Insert map =========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(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);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {
+ IMAGE_MAP: [
+ { LINK: [ ] }
+ ]
+ } ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Hide image map ======================================= */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetStyle(browser, 'imgmap', 'display', 'none');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+addAccessibleTask('doc_treeupdate_imagemap.html', function*(browser, accDoc) {
+ yield testImageMap(browser, accDoc);
+ yield testContainer(browser);
+});
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 000000000..023adf8e3
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_list.js
@@ -0,0 +1,43 @@
+/* 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';
+
+/* global ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_STATICTEXT, ROLE_LISTITEM */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* setDisplayAndWaitForReorder(browser, value) {
+ let onReorder = waitForEvent(EVENT_REORDER, 'ul');
+ yield invokeSetStyle(browser, 'li', 'display', value);
+ return yield onReorder;
+}
+
+addAccessibleTask(`
+ <ul id="ul">
+ <li id="li">item1</li>
+ </ul>`, function*(browser, accDoc) {
+ let li = findAccessibleChildByID(accDoc, 'li');
+ let bullet = li.firstChild;
+ let accTree = {
+ role: ROLE_LISTITEM,
+ children: [ {
+ role: ROLE_STATICTEXT,
+ children: []
+ }, {
+ role: ROLE_TEXT_LEAF,
+ children: []
+ } ]
+ };
+ testAccessibleTree(li, accTree);
+
+ yield setDisplayAndWaitForReorder(browser, 'none');
+
+ ok(isDefunct(li), 'Check that li is defunct.');
+ ok(isDefunct(bullet), 'Check that bullet is defunct.');
+
+ let event = yield setDisplayAndWaitForReorder(browser, 'list-item');
+
+ testAccessibleTree(findAccessibleChildByID(event.accessible, 'li'), accTree);
+});
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 000000000..7b01af87a
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
@@ -0,0 +1,39 @@
+/* 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';
+
+/* global ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_LISTITEM, ROLE_LIST,
+ ROLE_STATICTEXT */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<ol id="list"></ol>', function*(browser, accDoc) {
+ let list = findAccessibleChildByID(accDoc, 'list');
+
+ testAccessibleTree(list, {
+ role: ROLE_LIST,
+ children: [ ]
+ });
+
+ yield invokeSetAttribute(browser, 'body', 'contentEditable', 'true');
+ let onReorder = waitForEvent(EVENT_REORDER, 'list');
+ yield ContentTask.spawn(browser, {}, () => {
+ let li = content.document.createElement('li');
+ li.textContent = 'item';
+ content.document.getElementById('list').appendChild(li);
+ });
+ yield onReorder;
+
+ testAccessibleTree(list, {
+ role: ROLE_LIST,
+ children: [ {
+ role: ROLE_LISTITEM,
+ children: [
+ { role: ROLE_STATICTEXT, name: "1. ", children: [] },
+ { role: ROLE_TEXT_LEAF, children: [] }
+ ]
+ } ]
+ });
+});
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 000000000..7b7880312
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_listener.js
@@ -0,0 +1,29 @@
+/* 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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<span id="parent"><span id="child"></span></span>',
+ 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, 'body');
+ // Add an event listener to parent.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.window.dummyListener = () => {};
+ content.document.getElementById('parent').addEventListener(
+ 'click', content.window.dummyListener);
+ });
+ yield onReorder;
+
+ let tree = { TEXT: [] };
+ testAccessibleTree(findAccessibleChildByID(accDoc, 'parent'), tree);
+ });
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 000000000..45001afaa
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
@@ -0,0 +1,91 @@
+/* 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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<select id="select"></select>', function*(browser, accDoc) {
+ let select = findAccessibleChildByID(accDoc, 'select');
+
+ let onEvent = waitForEvent(EVENT_REORDER, 'select');
+ // Create a combobox with grouping and 2 standalone options
+ yield ContentTask.spawn(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 = yield 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
+ yield ContentTask.spawn(browser, {}, () => {
+ let contentSelect = content.document.getElementById('select');
+ contentSelect.removeChild(contentSelect.firstChild);
+ });
+ yield 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
+ yield ContentTask.spawn(browser, {}, () => {
+ let contentSelect = content.document.getElementById('select');
+ while (contentSelect.length) {
+ contentSelect.remove(0);
+ }
+ });
+ yield onEvent;
+
+ tree = {
+ COMBOBOX: [ {
+ COMBOBOX_LIST: [ ]
+ } ]
+ };
+ testAccessibleTree(select, tree);
+});
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 000000000..9892bbcd6
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_removal.js
@@ -0,0 +1,39 @@
+/* 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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('doc_treeupdate_removal.xhtml', 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, 'body');
+ yield ContentTask.spawn(browser, {}, () => content.document.getElementById(
+ 'the_displaynone').appendChild(content.document.getElementById(
+ 'the_table')));
+ yield 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).
+ yield ContentTask.spawn(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');
+});
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 000000000..9609f51ac
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_table.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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <table id="table">
+ <tr>
+ <td>cell1</td>
+ <td>cell2</td>
+ </tr>
+ </table>`, 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');
+ yield ContentTask.spawn(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);
+ });
+ yield onReorder;
+
+ tree = {
+ TABLE: [
+ { CAPTION: [ { TEXT_LEAF: [] } ] },
+ { ROW: [
+ { CELL: [ {TEXT_LEAF: [] }]},
+ { CELL: [ {TEXT_LEAF: [] }]}
+ ] }
+ ]
+ };
+ testAccessibleTree(table, tree);
+});
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 000000000..da45e2bc9
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
@@ -0,0 +1,35 @@
+/* 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';
+
+/* global EVENT_REORDER, ROLE_TEXT_CONTAINER ROLE_PARAGRAPH, ROLE_TEXT_LEAF */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* removeTextData(browser, accessible, id, role) {
+ let tree = {
+ role: role,
+ children: [ { role: ROLE_TEXT_LEAF, name: "text" } ]
+ };
+ testAccessibleTree(accessible, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ content.document.getElementById(contentId).firstChild.textContent = '';
+ });
+ yield onReorder;
+
+ tree = { role: role, children: [] };
+ testAccessibleTree(accessible, tree);
+}
+
+addAccessibleTask(`
+ <p id="p">text</p>
+ <pre id="pre">text</pre>`, function*(browser, accDoc) {
+ let p = findAccessibleChildByID(accDoc, 'p');
+ let pre = findAccessibleChildByID(accDoc, 'pre');
+ yield removeTextData(browser, p, 'p', ROLE_PARAGRAPH);
+ yield removeTextData(browser, pre, 'pre', ROLE_TEXT_CONTAINER);
+});
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 000000000..65a55c914
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js
@@ -0,0 +1,196 @@
+/* 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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* testTreeOnHide(browser, accDoc, containerID, id, before, after) {
+ let acc = findAccessibleChildByID(accDoc, containerID);
+ testAccessibleTree(acc, before);
+
+ let onReorder = waitForEvent(EVENT_REORDER, containerID);
+ yield invokeSetStyle(browser, id, 'visibility', 'hidden');
+ yield onReorder;
+
+ testAccessibleTree(acc, after);
+}
+
+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');
+ yield ContentTask.spawn(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';
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] },
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(accessible, tree);
+}
+
+function* test4(browser, accessible) {
+ let tree = {
+ SECTION: [
+ { TABLE: [
+ { ROW: [
+ { CELL: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(accessible, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, 't4_parent');
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ doc.getElementById('t4_container').style.color = 'red';
+ doc.getElementById('t4_child').style.visibility = 'visible';
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [{
+ TABLE: [{
+ ROW: [{
+ CELL: [{
+ SECTION: [{
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }]
+ };
+ testAccessibleTree(accessible, tree);
+}
+
+addAccessibleTask('doc_treeupdate_visibility.html', function*(browser, accDoc) {
+ let t3Container = findAccessibleChildByID(accDoc, 't3_container');
+ let t4Container = findAccessibleChildByID(accDoc, 't4_container');
+
+ yield testTreeOnHide(browser, accDoc, 't1_container', 't1_parent', {
+ SECTION: [{
+ SECTION: [{
+ SECTION: [ { TEXT_LEAF: [] } ]
+ }]
+ }]
+ }, {
+ SECTION: [ {
+ SECTION: [ { TEXT_LEAF: [] } ]
+ } ]
+ });
+
+ yield 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: []
+ }]
+ }]
+ });
+
+ yield test3(browser, t3Container);
+ yield test4(browser, t4Container);
+
+ yield testTreeOnHide(browser, accDoc, 't5_container', 't5_subcontainer', {
+ SECTION: [{ // container
+ SECTION: [{ // subcontainer
+ TABLE: [{
+ ROW: [{
+ CELL: [{
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }]
+ }]
+ }, {
+ SECTION: [{ // container
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }]
+ });
+
+ yield 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: []
+ }]
+ }]
+ });
+});
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 000000000..c9dbde691
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.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';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('doc_treeupdate_whitespace.html', 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
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ doc.getElementById('container1').removeChild(
+ doc.getElementById('img1'));
+ });
+ yield 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
+ yield ContentTask.spawn(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);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { LINK: [ { GRAPHIC: [ ] } ] },
+ { TEXT_LEAF: [ ] },
+ { LINK: [ { GRAPHIC: [ ] } ] }
+ ]
+ };
+ testAccessibleTree(container2Parent, tree);
+});
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 000000000..9d08854b9
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update ARIA Dialog Test</title>
+ </head>
+ <body id="body">
+ <div id="dialog" role="dialog" style="display: none;">
+ <table id="table" role="presentation"
+ style="display: block; position: fixed; top: 88px; left: 312.5px; z-index: 10010;">
+ <tbody>
+ <tr>
+ <td role="presentation">
+ <div role="presentation">
+ <a id="a" role="button">text</a>
+ </div>
+ <input id="input">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
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 000000000..38b5c333a
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
@@ -0,0 +1,44 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update ARIA Owns Test</title>
+ </head>
+ <body id="body">
+ <div id="t1_container" aria-owns="t1_checkbox t1_button">
+ <div role="button" id="t1_button"></div>
+ <div role="checkbox" id="t1_checkbox">
+ <span id="t1_span">
+ <div id="t1_subdiv"></div>
+ </span>
+ </div>
+ </div>
+ <div id="t1_group" role="group"></div>
+ <div id="t1_grouptmp" role="group"></div>
+
+ <div id="t2_container1" aria-owns="t2_owned"></div>
+ <div id="t2_container2">
+ <div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div>
+ </div>
+
+ <div id="t3_container1" aria-owns="t3_child"></div>
+ <div id="t3_child" role="checkbox"></div>
+ <div id="t3_container2"></div>
+
+ <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
+ <div id="t4_container2">
+ <div id="t4_child1" style="display:none" role="checkbox"></div>
+ <div id="t4_child2" role="radio"></div>
+ </div>
+
+ <div id="t5_container">
+ <div role="button" id="t5_button"></div>
+ <div role="checkbox" id="t5_checkbox"></div>
+ <div role="radio" id="t5_radio"></div>
+ </div>
+
+ <div id="t6_container" aria-owns="t6_fake">
+ <span id="t6_span">hey</span>
+ </div>
+ <div id="t6_fake" role="group"></div>
+ </body>
+</html>
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 000000000..4dd230fc2
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Imagemap Test</title>
+ </head>
+ <body id="body">
+ <map name="atoz_map" id="map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ </map>
+
+ <div id="container">
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="http://example.com/a11y/accessible/tests/mochitest/letters.gif"><!--
+ Important: no whitespace between the <img> and the </div>, so we
+ don't end up with textframes there, because those would be reflected
+ in our accessible tree in some cases.
+ --></div>
+ </body>
+</html>
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 000000000..9c59fb9d1
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Removal Test</title>
+ </head>
+ <body id="body">
+ <div id="the_displaynone" style="display: none;"></div>
+ <table id="the_table"></table>
+ <tr id="the_row"></tr>
+ </body>
+</html>
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 000000000..c33a2bc02
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_visibility.html
@@ -0,0 +1,78 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Visibility Test</title>
+ </head>
+ <body id="body">
+ <!-- hide parent while child stays visible -->
+ <div id="t1_container">
+ <div id="t1_parent">
+ <div id="t1_child" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- hide grandparent while its children stay visible -->
+ <div id="t2_container">
+ <div id="t2_grandparent">
+ <div>
+ <div id="t2_child" style="visibility: visible">text</div>
+ <div id="t2_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- change container style, hide parents while their children stay visible -->
+ <div id="t3_container">
+ <div id="t3_parent">
+ <div id="t3_child" style="visibility: visible">text</div>
+ </div>
+ <div id="t3_parent2">
+ <div id="t3_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- change container style, show child inside the table -->
+ <div id="t4_container">
+ <table>
+ <tr>
+ <td id="t4_parent">
+ <div id="t4_child" style="visibility: hidden;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <!-- hide subcontainer while child inside the table stays visible -->
+ <div id="t5_container">
+ <div id="t5_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <div id="t5_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <!-- hide subcontainer while its child and child inside the nested table stays visible -->
+ <div id="t6_container">
+ <div id="t6_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <table>
+ <tr>
+ <td>
+ <div id="t6_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <div id="t6_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+ </body>
+</html>
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 000000000..d1770d300
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Whitespace text accessible creation/desctruction</title>
+ </head>
+ <body id="body">
+ <div id="container1"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img id="img1" src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> </div>
+ <div id="container2-parent"> <a id="container2"></a> <a><img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"></a> </div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/events.js b/accessible/tests/browser/e10s/events.js
new file mode 100644
index 000000000..39dd743ef
--- /dev/null
+++ b/accessible/tests/browser/e10s/events.js
@@ -0,0 +1,127 @@
+/* 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';
+
+/* global nsIAccessibleEvent, nsIAccessibleDocument,
+ nsIAccessibleStateChangeEvent, nsIAccessibleTextChangeEvent */
+
+/* exported EVENT_REORDER, EVENT_SHOW, EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
+ EVENT_DOCUMENT_LOAD_COMPLETE, EVENT_HIDE, EVENT_TEXT_CARET_MOVED,
+ EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_STATE_CHANGE,
+ EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE, EVENT_FOCUS,
+ waitForEvent, waitForMultipleEvents */
+
+const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
+const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
+const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
+const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
+const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
+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_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE;
+const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
+const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
+const EVENT_TEXT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_TEXT_VALUE_CHANGE;
+const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
+
+/**
+ * Describe an event in string format.
+ * @param {nsIAccessibleEvent} event event to strigify
+ */
+function eventToString(event) {
+ let type = eventTypeToString(event.eventType);
+ let info = `Event type: ${type}`;
+
+ if (event instanceof nsIAccessibleStateChangeEvent) {
+ let stateStr = statesToString(event.isExtraState ? 0 : event.state,
+ event.isExtraState ? event.state : 0);
+ info += `, state: ${stateStr}, is enabled: ${event.isEnabled}`;
+ } else if (event instanceof nsIAccessibleTextChangeEvent) {
+ let tcType = event.isInserted ? 'inserted' : 'removed';
+ info += `, start: ${event.start}, length: ${event.length}, ${tcType} text: ${event.modifiedText}`;
+ }
+
+ info += `. Target: ${prettyName(event.accessible)}`;
+ return info;
+}
+
+/**
+ * A helper function that returns a promise that resolves when an accessible
+ * event of the given type with the given target (defined by its id or
+ * accessible) is observed.
+ * @param {String|nsIAccessible} expectedIdOrAcc expected content element id
+ * for the event
+ * @param {Number} eventType expected accessible event
+ * type
+ * @return {Promise} promise that resolves to an
+ * event
+ */
+function waitForEvent(eventType, expectedIdOrAcc) {
+ return new Promise(resolve => {
+ let eventObserver = {
+ observe(subject, topic, data) {
+ if (topic !== 'accessible-event') {
+ return;
+ }
+
+ let event = subject.QueryInterface(nsIAccessibleEvent);
+ if (Logger.enabled) {
+ // Avoid calling eventToString if the logger isn't enabled in order
+ // to avoid an intermittent crash (bug 1307645).
+ Logger.log(eventToString(event));
+ }
+
+ // If event type does not match expected type, skip the event.
+ if (event.eventType !== eventType) {
+ return;
+ }
+
+ let acc = event.accessible;
+ let id = getAccessibleDOMNodeID(acc);
+ let isID = typeof expectedIdOrAcc === 'string';
+ let actualIdOrAcc = isID ? id : acc;
+ // If event's accessible does not match expected DOMNode id or
+ // accessible, skip the event.
+ if (actualIdOrAcc === expectedIdOrAcc) {
+ if (isID) {
+ Logger.log(`Correct event DOMNode id: ${id}`);
+ } else {
+ Logger.log(`Correct event accessible: ${prettyName(acc)}`);
+ }
+ Logger.log(`Correct event type: ${eventTypeToString(eventType)}`);
+ ok(event.accessibleDocument instanceof nsIAccessibleDocument,
+ 'Accessible document present.');
+
+ Services.obs.removeObserver(this, 'accessible-event');
+ resolve(event);
+ }
+ }
+ };
+ Services.obs.addObserver(eventObserver, 'accessible-event', false);
+ });
+}
+
+/**
+ * A helper function that waits for a sequence of accessible events in
+ * specified order.
+ * @param {Array} events a list of events to wait (same format as
+ * waitForEvent arguments)
+ */
+function waitForMultipleEvents(events) {
+ // Next expected event index.
+ let currentIdx = 0;
+
+ return Promise.all(events.map(({ eventType, id }, idx) =>
+ // In addition to waiting for an event, attach an order checker for the
+ // event.
+ waitForEvent(eventType, id).then(resolvedEvent => {
+ // Verify that event happens in order and increment expected index.
+ is(idx, currentIdx++,
+ `Unexpected event order: ${eventToString(resolvedEvent)}`);
+ return resolvedEvent;
+ })
+ ));
+}
diff --git a/accessible/tests/browser/e10s/head.js b/accessible/tests/browser/e10s/head.js
new file mode 100644
index 000000000..5cc102697
--- /dev/null
+++ b/accessible/tests/browser/e10s/head.js
@@ -0,0 +1,84 @@
+/* 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';
+
+/* global EVENT_DOCUMENT_LOAD_COMPLETE, CURRENT_CONTENT_DIR, loadFrameScripts */
+
+/* exported addAccessibleTask */
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ 'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+ this);
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param {String} doc URL (relative to current directory) or
+ * data URL or markup snippet that is used
+ * to test content with
+ * @param {Function|Function*} task a generator or a function with tests to
+ * run
+ */
+function addAccessibleTask(doc, task) {
+ add_task(function*() {
+ let url;
+ if (doc.includes('doc_')) {
+ url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+ } else {
+ // Assume it's a markup snippet.
+ url = `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body id="body">${doc}</body>
+ </html>`;
+ }
+
+ registerCleanupFunction(() => {
+ let observers = Services.obs.enumerateObservers('accessible-event');
+ while (observers.hasMoreElements()) {
+ Services.obs.removeObserver(
+ observers.getNext().QueryInterface(Ci.nsIObserver),
+ 'accessible-event');
+ }
+ });
+
+ let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: url
+ }, function*(browser) {
+ registerCleanupFunction(() => {
+ if (browser) {
+ let tab = gBrowser.getTabForBrowser(browser);
+ if (tab && !tab.closing && tab.linkedBrowser) {
+ gBrowser.removeTab(tab);
+ }
+ }
+ });
+
+ yield SimpleTest.promiseFocus(browser);
+
+ loadFrameScripts(browser,
+ 'let { document, window, navigator } = content;',
+ { name: 'common.js', dir: MOCHITESTS_DIR });
+
+ Logger.log(
+ `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+ Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+ let event = yield onDocLoad;
+ yield task(browser, event.accessible);
+ });
+ });
+}
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
diff --git a/accessible/tests/browser/head.js b/accessible/tests/browser/head.js
new file mode 100644
index 000000000..8e8d82205
--- /dev/null
+++ b/accessible/tests/browser/head.js
@@ -0,0 +1,116 @@
+/* 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 initPromise, shutdownPromise,
+ setE10sPrefs, unsetE10sPrefs, forceGC */
+
+/**
+ * Set e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are set.
+ */
+function setE10sPrefs() {
+ return new Promise(resolve =>
+ SpecialPowers.pushPrefEnv({
+ set: [
+ ['browser.tabs.remote.autostart', true],
+ ['browser.tabs.remote.force-enable', true],
+ ['extensions.e10sBlocksEnabling', false]
+ ]
+ }, resolve));
+}
+
+/**
+ * Unset e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are unset.
+ */
+function unsetE10sPrefs() {
+ return new Promise(resolve => {
+ SpecialPowers.popPrefEnv(resolve);
+ });
+}
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ 'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+ this);
+
+/**
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired.
+ * @return {Promise} event promise evaluating to event's data
+ */
+function a11yInitOrShutdownPromise() {
+ return new Promise(resolve => {
+ let observe = (subject, topic, data) => {
+ Services.obs.removeObserver(observe, 'a11y-init-or-shutdown');
+ resolve(data);
+ };
+ Services.obs.addObserver(observe, 'a11y-init-or-shutdown', false);
+ });
+}
+
+/**
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired
+ * in content.
+ * @param {Object} browser current "tabbrowser" element
+ * @return {Promise} event promise evaluating to event's data
+ */
+function contentA11yInitOrShutdownPromise(browser) {
+ return ContentTask.spawn(browser, {}, a11yInitOrShutdownPromise);
+}
+
+/**
+ * A helper function that maps 'a11y-init-or-shutdown' event to a promise that
+ * resovles or rejects depending on whether accessibility service is expected to
+ * be initialized or shut down.
+ */
+function promiseOK(promise, expected) {
+ return promise.then(flag =>
+ flag === expected ? Promise.resolve() : Promise.reject());
+}
+
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * initialized with the correct flag.
+ * @param {?Object} contentBrowser optinal remove browser object that indicates
+ * that accessibility service is expected to be
+ * initialized in content process.
+ * @return {Promise} promise that resolves when the accessibility
+ * service initialized correctly.
+ */
+function initPromise(contentBrowser) {
+ let a11yInitPromise = contentBrowser ?
+ contentA11yInitOrShutdownPromise(contentBrowser) :
+ a11yInitOrShutdownPromise();
+ return promiseOK(a11yInitPromise, '1').then(
+ () => ok(true, 'Service initialized correctly'),
+ () => ok(false, 'Service shutdown incorrectly'));
+}
+
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * shut down with the correct flag.
+ * @param {?Object} contentBrowser optinal remove browser object that indicates
+ * that accessibility service is expected to be
+ * shut down in content process.
+ * @return {Promise} promise that resolves when the accessibility
+ * service shuts down correctly.
+ */
+function shutdownPromise(contentBrowser) {
+ let a11yShutdownPromise = contentBrowser ?
+ contentA11yInitOrShutdownPromise(contentBrowser) :
+ a11yInitOrShutdownPromise();
+ return promiseOK(a11yShutdownPromise, '0').then(
+ () => ok(true, 'Service shutdown correctly'),
+ () => ok(false, 'Service initialized incorrectly'));
+}
+
+/**
+ * Force garbage collection.
+ */
+function forceGC() {
+ Cu.forceCC();
+ Cu.forceGC();
+}
diff --git a/accessible/tests/browser/shared-head.js b/accessible/tests/browser/shared-head.js
new file mode 100644
index 000000000..83a9fa612
--- /dev/null
+++ b/accessible/tests/browser/shared-head.js
@@ -0,0 +1,229 @@
+/* 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 Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
+ invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
+ CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
+
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+/**
+ * Current browser test directory path used to load subscripts.
+ */
+const CURRENT_DIR =
+ 'chrome://mochitests/content/browser/accessible/tests/browser/';
+/**
+ * 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/browser/accessible/tests/browser/';
+
+/**
+ * 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}`);
+ }
+ }
+};
+
+/**
+ * Check if an accessible object has a defunct test.
+ * @param {nsIAccessible} accessible object to test defunct state for
+ * @return {Boolean} flag indicating defunct state
+ */
+function isDefunct(accessible) {
+ let defunct = false;
+ try {
+ let extState = {};
+ accessible.getState({}, extState);
+ defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
+ } catch (x) {
+ defunct = true;
+ } finally {
+ if (defunct) {
+ Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
+ }
+ }
+ return defunct;
+}
+
+/**
+ * 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 ContentTask.spawn(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).
+ * @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 ContentTask.spawn(browser, [id, style, value],
+ ([contentId, contentStyle, contentValue]) => {
+ let 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).
+ * @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 ContentTask.spawn(browser, id, contentId => {
+ let elm = content.document.getElementById(contentId);
+ if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
+ elm instanceof Ci.nsIDOMXULTextBoxElement) {
+ elm.selectionStart = elm.selectionEnd = elm.value.length;
+ }
+ elm.focus();
+ });
+}
+
+/**
+ * Traverses the accessible tree starting from a given accessible as a root and
+ * looks for an accessible that matches based on its DOMNode id.
+ * @param {nsIAccessible} accessible root accessible
+ * @param {String} id id to look up accessible for
+ * @return {nsIAccessible?} found accessible if any
+ */
+function findAccessibleChildByID(accessible, id) {
+ if (getAccessibleDOMNodeID(accessible) === id) {
+ return accessible;
+ }
+ for (let i = 0; i < accessible.children.length; ++i) {
+ let found = findAccessibleChildByID(accessible.getChildAt(i), id);
+ if (found) {
+ return found;
+ }
+ }
+}
+
+/**
+ * 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 frame scripts into test's content.
+ * @param {Object} browser browser element that content belongs to
+ * @param {Array} scripts a list of scripts to load into content
+ */
+function loadFrameScripts(browser, ...scripts) {
+ let mm = browser.messageManager;
+ for (let script of scripts) {
+ let frameScript;
+ if (typeof script === 'string') {
+ if (script.includes('.js')) {
+ // If script string includes a .js extention, assume it is a script
+ // path.
+ frameScript = `${CURRENT_DIR}${script}`;
+ } else {
+ // Otherwise it is a serealized script.
+ frameScript = `data:,${script}`;
+ }
+ } else {
+ // Script is a object that has { dir, name } format.
+ frameScript = `${script.dir}${script.name}`;
+ }
+ mm.loadFrameScript(frameScript, false, true);
+ }
+}
diff --git a/accessible/tests/crashtests/448064.xhtml b/accessible/tests/crashtests/448064.xhtml
new file mode 100644
index 000000000..64d6d851d
--- /dev/null
+++ b/accessible/tests/crashtests/448064.xhtml
@@ -0,0 +1,73 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+</head>
+<body>
+<div id="mw_b">
+<div id="mw_f">
+<div id="mw_g" style="display: none;"/>
+</div>
+</div>
+
+<div id="mw_c" style="display: none;">
+<div id="mw_d">
+<div id="mw_e"></div>
+</div>
+</div>
+
+<input id="mw_a"/>
+
+
+<script>
+function dumpAccessibleNode(aNode, level) {
+ var msg = "";
+
+ try {
+ msg += "name=\"" + aNode.name + "\" ";
+ } catch (e) {
+ msg += " noName ";
+ }
+
+ dump(msg + '\n');
+}
+
+
+function dumpAccessibleTree(aNode, level) {
+netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ level = level || 0;
+
+ dumpAccessibleNode(aNode, level);
+ try {
+ var child = aNode.firstChild;
+ while (child) {
+ dumpAccessibleTree(child, level + 1);
+ child = child.nextSibling;
+ }
+ } catch (e) {
+ dump("Error visiting child nodes: " + e + '\n');
+ }
+}
+
+function A(o) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var acc = Components.classes['@mozilla.org/accessibilityService;1']
+ .getService(Components.interfaces.nsIAccessibilityService);
+ return acc.getAccessibleFor(o);
+}
+
+function beginAccessible() {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ dumpAccessibleTree(A(document),0);
+}
+setTimeout(beginAccessible, 100);
+
+
+setTimeout(doe, 200);
+function doe() {
+ document.getElementById('mw_a').appendChild(document.getElementById('mw_b'));
+ document.getElementById('mw_c').appendChild(document.getElementById('mw_d'));
+ document.getElementById('mw_e').appendChild(document.getElementById('mw_f'));
+ document.getElementById('mw_g').appendChild(document.getElementById('mw_b'));
+}
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/crashtests/471493.xul b/accessible/tests/crashtests/471493.xul
new file mode 100644
index 000000000..2524d47cc
--- /dev/null
+++ b/accessible/tests/crashtests/471493.xul
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="bug 471493 'crash [@ nsPropertyTable::GetPropertyInternal]'"
+ onload="doTest();">
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ var accService = SpecialPowers.Cc["@mozilla.org/accessibilityService;1"].
+ getService(SpecialPowers.Ci.nsIAccessibilityService);
+
+ var treecol = document.getElementById("col");
+ var x = treecol.boxObject.screenX;
+ var y = treecol.boxObject.screenY;
+
+ var tree = document.getElementById("tree");
+ var treeAcc = accService.getAccessibleFor(tree);
+ treeAcc.getChildAtPoint(x + 1, y + 1);
+ }
+ ]]>
+ </script>
+
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="scol" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+
+</window>
+
diff --git a/accessible/tests/crashtests/crashtests.list b/accessible/tests/crashtests/crashtests.list
new file mode 100644
index 000000000..6718706aa
--- /dev/null
+++ b/accessible/tests/crashtests/crashtests.list
@@ -0,0 +1,3 @@
+# Disabled because they cause assertions/crashes in later crashtests, see bug 918246
+skip load 448064.xhtml
+skip load 471493.xul
diff --git a/accessible/tests/mochitest/a11y.ini b/accessible/tests/mochitest/a11y.ini
new file mode 100644
index 000000000..b197c7007
--- /dev/null
+++ b/accessible/tests/mochitest/a11y.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+support-files =
+ ../../../dom/media/test/bug461281.ogg
+ dumbfile.zip
+ formimage.png
+ letters.gif
+ moz.png
+ longdesc_src.html
+ *.js
+ treeview.css
+
+[test_aria_token_attrs.html]
+[test_bug420863.html]
+[test_descr.html]
+[test_nsIAccessibleDocument.html]
+[test_nsIAccessibleImage.html]
+[test_OuterDocAccessible.html]
diff --git a/accessible/tests/mochitest/actions.js b/accessible/tests/mochitest/actions.js
new file mode 100644
index 000000000..0c2765eeb
--- /dev/null
+++ b/accessible/tests/mochitest/actions.js
@@ -0,0 +1,187 @@
+////////////////////////////////////////////////////////////////////////////////
+// Event constants
+
+const MOUSEDOWN_EVENT = 1;
+const MOUSEUP_EVENT = 2;
+const CLICK_EVENT = 4;
+const COMMAND_EVENT = 8;
+const FOCUS_EVENT = 16;
+
+const CLICK_EVENTS = MOUSEDOWN_EVENT | MOUSEUP_EVENT | CLICK_EVENT;
+const XUL_EVENTS = CLICK_EVENTS | COMMAND_EVENT;
+
+////////////////////////////////////////////////////////////////////////////////
+// Public functions
+
+/**
+ * Test default accessible actions.
+ *
+ * Action tester interface is:
+ *
+ * var actionObj = {
+ * // identifier of accessible to perform an action on
+ * get ID() {},
+ *
+ * // index of the action
+ * get actionIndex() {},
+ *
+ * // name of the action
+ * get actionName() {},
+ *
+ * // DOM events (see constants defined above)
+ * get events() {},
+ *
+ * // [optional] identifier of target DOM events listeners are registered on,
+ * // used with 'events', if missing then 'ID' is used instead.
+ * get targetID() {},
+ *
+ * // [optional] perform checks when 'click' event is handled if 'events'
+ * // is used.
+ * checkOnClickEvent: function() {},
+ *
+ * // [optional] an array of invoker's checker objects (see eventQueue
+ * // constructor events.js)
+ * get eventSeq() {}
+ * };
+ *
+ *
+ * @param aArray [in] an array of action cheker objects
+ */
+function testActions(aArray)
+{
+ gActionsQueue = new eventQueue();
+
+ for (var idx = 0; idx < aArray.length; idx++) {
+
+ var actionObj = aArray[idx];
+ var accOrElmOrID = actionObj.ID;
+ var actionIndex = actionObj.actionIndex;
+ var actionName = actionObj.actionName;
+ var events = actionObj.events;
+ var accOrElmOrIDOfTarget = actionObj.targetID ?
+ actionObj.targetID : accOrElmOrID;
+
+ var eventSeq = new Array();
+ if (events) {
+ var elm = getNode(accOrElmOrIDOfTarget);
+ if (events & MOUSEDOWN_EVENT)
+ eventSeq.push(new checkerOfActionInvoker("mousedown", elm));
+
+ if (events & MOUSEUP_EVENT)
+ eventSeq.push(new checkerOfActionInvoker("mouseup", elm));
+
+ if (events & CLICK_EVENT)
+ eventSeq.push(new checkerOfActionInvoker("click", elm, actionObj));
+
+ if (events & COMMAND_EVENT)
+ eventSeq.push(new checkerOfActionInvoker("command", elm));
+
+ if (events & FOCUS_EVENT)
+ eventSeq.push(new focusChecker(elm));
+ }
+
+ if (actionObj.eventSeq)
+ eventSeq = eventSeq.concat(actionObj.eventSeq);
+
+ var invoker = new actionInvoker(accOrElmOrID, actionIndex, actionName,
+ eventSeq);
+ gActionsQueue.push(invoker);
+ }
+
+ gActionsQueue.invoke();
+}
+
+/**
+ * Test action names and descriptions.
+ */
+function testActionNames(aID, aActions)
+{
+ var actions = (typeof aActions == "string") ?
+ [ aActions ] : (aActions || []);
+
+ var acc = getAccessible(aID);
+ is(acc.actionCount, actions.length, "Wong number of actions.");
+ for (var i = 0; i < actions.length; i++ ) {
+ is(acc.getActionName(i), actions[i], "Wrong action name at " + i + " index.");
+ is(acc.getActionDescription(0), gActionDescrMap[actions[i]],
+ "Wrong action description at " + i + "index.");
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private
+
+var gActionsQueue = null;
+
+function actionInvoker(aAccOrElmOrId, aActionIndex, aActionName, aEventSeq)
+{
+ this.invoke = function actionInvoker_invoke()
+ {
+ var acc = getAccessible(aAccOrElmOrId);
+ if (!acc)
+ return INVOKER_ACTION_FAILED;
+
+ var isThereActions = acc.actionCount > 0;
+ ok(isThereActions,
+ "No actions on the accessible for " + prettyName(aAccOrElmOrId));
+
+ if (!isThereActions)
+ return INVOKER_ACTION_FAILED;
+
+ is(acc.getActionName(aActionIndex), aActionName,
+ "Wrong action name of the accessible for " + prettyName(aAccOrElmOrId));
+
+ try {
+ acc.doAction(aActionIndex);
+ }
+ catch (e){
+ ok(false, "doAction(" + aActionIndex + ") failed with: " + e.name);
+ return INVOKER_ACTION_FAILED;
+ }
+ }
+
+ this.eventSeq = aEventSeq;
+
+ this.getID = function actionInvoker_getID()
+ {
+ return "invoke an action " + aActionName + " at index " + aActionIndex +
+ " on " + prettyName(aAccOrElmOrId);
+ }
+}
+
+function checkerOfActionInvoker(aType, aTarget, aActionObj)
+{
+ this.type = aType;
+
+ this.target = aTarget;
+
+ this.phase = false;
+
+ this.getID = function getID()
+ {
+ return aType + " event handling";
+ }
+
+ this.check = function check(aEvent)
+ {
+ if (aActionObj && "checkOnClickEvent" in aActionObj)
+ aActionObj.checkOnClickEvent(aEvent);
+ }
+}
+
+var gActionDescrMap =
+{
+ jump: "Jump",
+ press: "Press",
+ check: "Check",
+ uncheck: "Uncheck",
+ select: "Select",
+ open: "Open",
+ close: "Close",
+ switch: "Switch",
+ click: "Click",
+ collapse: "Collapse",
+ expand: "Expand",
+ activate: "Activate",
+ cycle: "Cycle"
+};
diff --git a/accessible/tests/mochitest/actions/a11y.ini b/accessible/tests/mochitest/actions/a11y.ini
new file mode 100644
index 000000000..ca01f4104
--- /dev/null
+++ b/accessible/tests/mochitest/actions/a11y.ini
@@ -0,0 +1,18 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/dom/media/test/bug461281.ogg
+
+[test_anchors.html]
+[test_aria.html]
+[test_controls.html]
+[test_general.html]
+[test_general.xul]
+[test_keys.html]
+[test_keys_menu.xul]
+[test_link.html]
+[test_media.html]
+skip-if = buildapp == 'mulet'
+[test_select.html]
+[test_tree.xul]
+[test_treegrid.xul]
diff --git a/accessible/tests/mochitest/actions/test_anchors.html b/accessible/tests/mochitest/actions/test_anchors.html
new file mode 100644
index 000000000..5e8330683
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_anchors.html
@@ -0,0 +1,150 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing for HTML links that
+ scroll the page to named anchors</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Event checkers
+
+ function scrollingChecker(aAcc)
+ {
+ this.type = EVENT_SCROLLING_START;
+ this.target = aAcc;
+ this.getID = function scrollingChecker_getID()
+ {
+ return "scrolling start handling for " + prettyName(aAcc);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "anchor1",
+ actionName: "jump",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new scrollingChecker(getAccessible("bottom1"))
+ ]
+ },
+ { // jump again (test for bug 437607)
+ ID: "anchor1",
+ actionName: "jump",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new scrollingChecker(getAccessible("bottom1"))
+ ]
+ },
+ {
+ ID: "anchor2",
+ actionName: "jump",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new scrollingChecker(getAccessible("bottom2"))
+ ]
+ }
+ ];
+
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=506389"
+ title="Some same page links do not fire EVENT_SYSTEM_SCROLLINGSTART">
+ Mozilla Bug 506389
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=437607"
+ title="Clicking the 'Skip to main content' link once works, second time fails to initiate a V cursor jump">
+ Mozilla Bug 437607
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=519303"
+ title="Same page links to targets with content fires scrolling start accessible event on leaf text node">
+ Mozilla Bug 519303
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="debug"></div>
+
+ <h1>This is a test page for anchors</h1>
+ This is a top anchor<a name="Top">
+ </a><a id="anchor1" href="#bottom1">Link to anchor</a>
+ <a id="anchor2" href="#bottom2">Link to div</a>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br>This is some text in the middle<br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ This is some text.
+ This is a bottom anchor<a id="bottom1"></a>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <div id="bottom2">This is a div</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_aria.html b/accessible/tests/mochitest/actions/test_aria.html
new file mode 100644
index 000000000..c4eae812d
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_aria.html
@@ -0,0 +1,202 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "clickable",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "button",
+ actionName: "press",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_unchecked",
+ actionName: "check",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_checked",
+ actionName: "uncheck",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_mixed",
+ actionName: "cycle",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "combobox_collapsed",
+ actionName: "open",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "combobox_expanded",
+ actionName: "close",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "link",
+ actionName: "jump",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "menuitem",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "menuitemcheckbox",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "menuitemradio",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "option",
+ actionName: "select",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "radio",
+ actionName: "select",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "switch_unchecked",
+ actionName: "check",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "switch_checked",
+ actionName: "uncheck",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "tab",
+ actionName: "switch",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "textbox",
+ actionName: "activate",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "treeitem",
+ actionName: "activate",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "sortable",
+ actionName: "sort",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "expandable",
+ actionName: "expand",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "collapseable",
+ actionName: "collapse",
+ events: CLICK_EVENTS
+ }
+ ];
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410765"
+ title="nsIAccessible actions testing">
+ Mozilla Bug 410765
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="clickable" onclick="">Clickable text</div>
+
+ <div id="button" role="button">Button</div>
+
+ <div id="checkbox_unchecked" role="checkbox">Checkbox</div>
+
+ <div id="checkbox_checked" role="checkbox" aria-checked="true">Checkbox</div>
+
+ <div id="checkbox_mixed" role="checkbox" aria-checked="mixed">Checkbox</div>
+
+ <div id="combobox_collapsed" role="combobox">
+ <div id="option" role="option">Option of collapsed combobox</div>
+ </div>
+
+ <div id="combobox_expanded" role="combobox" aria-expanded="true">
+ <div role="option">Option of expanded combobox</div>
+ </div>
+
+ <div id="link" role="link">Link</div>
+
+ <div role="menu">
+ <div id="menuitem" role="menuitem">Menuitem</div>
+ <div id="menuitemcheckbox" role="menuitemcheckbox">Menuitem checkbox</div>
+ <div id="menuitemradio" role="menuitemradio">Menuitem radio</div>
+ </div>
+
+ <div role="radiogroup">
+ <div id="radio" role="radio">Radio</div>
+ </div>
+
+ <div id="switch_unchecked" role="switch">Switch</div>
+
+ <div id="switch_checked" role="switch" aria-checked="true">Switch</div>
+
+ <div role="tablist">
+ <div id="tab" role="tab">Tab</div>
+ </div>
+
+ <div id="textbox" role="textbox">Textbox</div>
+
+ <div role="tree">
+ <div id="treeitem" role="treeitem">Treeitem</div>
+ </div>
+
+ <div role="grid">
+ <div id="sortable" role="columnheader" aria-sort="ascending">
+ Columnheader
+ </div>
+ </div>
+
+ <div id="expandable" aria-expanded="false">collapsed</div>
+ <div id="collapseable" aria-expanded="true">expanded</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_controls.html b/accessible/tests/mochitest/actions/test_controls.html
new file mode 100644
index 000000000..d6109a82f
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_controls.html
@@ -0,0 +1,109 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing for inputs</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "button",
+ actionName: "press",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "input_button",
+ actionName: "press",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_unchecked",
+ actionName: "check",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_checked",
+ actionName: "uncheck",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "checkbox_mixed",
+ actionName: "cycle",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "radio",
+ actionName: "select",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "textarea",
+ actionName: "activate",
+ events: FOCUS_EVENT
+ },
+ {
+ ID: "textinput",
+ actionName: "activate",
+ events: FOCUS_EVENT
+ }
+
+ ];
+ document.getElementById("checkbox_mixed").indeterminate = true;
+
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=477975"
+ title="nsIAccessible actions testing">
+ Mozilla Bug 477975
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <button id="button">Button</button>
+
+ <input id="input_button" type="button" value="normal">
+
+ <input id="checkbox_unchecked" type="checkbox">Checkbox</input>
+
+ <input id="checkbox_checked" type="checkbox" checked="true">Checkbox</input>
+
+ <input id="checkbox_mixed" type="checkbox">Checkbox</input>
+
+ <fieldset>
+ <input id="radio" type="radio">Radio</input>
+ </fieldset>
+
+ <textarea id="textarea" placeholder="What's happening?"></textarea>
+
+ <input id="textinput" type="text">
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_general.html b/accessible/tests/mochitest/actions/test_general.html
new file mode 100644
index 000000000..5b9a18dab
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_general.html
@@ -0,0 +1,107 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing on HTML elements</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "li_clickable1",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "li_clickable2",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "li_clickable3",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "onclick_img",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "label1",
+ actionName: "click",
+ events: CLICK_EVENTS
+ }
+
+ ];
+
+ testActions(actionsArray);
+
+ is(getAccessible("label1").firstChild.actionCount, 1, "label text should have 1 action");
+
+ getAccessible("onclick_img").takeFocus();
+ is(getAccessible("link1").actionCount, 1, "links should have one action");
+ is(getAccessible("link2").actionCount, 1, "link with onclick handler should have 1 action");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=523789"
+ title="nsHTMLLiAccessible shouldn't be inherited from linkable accessible">
+ Mozilla Bug 523789
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=423409"
+ title="Expose click action if mouseup and mousedown are registered">
+ Mozilla Bug 423409
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=659620"
+ title="hang when trying to edit a page on wikimo with NVDA running">
+ Mozilla Bug 659620
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ul>
+ <li id="li_clickable1" onclick="">Clickable list item</li>
+ <li id="li_clickable2" onmousedown="">Clickable list item</li>
+ <li id="li_clickable3" onmouseup="">Clickable list item</li>
+ </ul>
+
+ <!-- linkable accessibles -->
+ <img id="onclick_img" onclick="" src="../moz.png">
+
+ <a id="link1" href="www">linkable textleaf accessible</a>
+ <div id="link2" onclick="">linkable textleaf accessible</div>
+
+ <div>
+ <label for="TextBox_t2" id="label1">
+ <span>Explicit</span>
+ </label>
+ <input name="in2" id="TextBox_t2" type="text" maxlength="17">
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_general.xul b/accessible/tests/mochitest/actions/test_general.xul
new file mode 100644
index 000000000..14d8bb0d0
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_general.xul
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../nsIAccessible_name.css"
+ type="text/css"?>
+
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessible actions testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../actions.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose"); // debug
+
+ if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 1);
+ } else {
+ SimpleTest.expectAssertions(0, 1);
+ }
+
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "menu",
+ actionName: "click",
+ events: CLICK_EVENTS,
+ // Wait for focus event, it guarantees us the submenu tree is created,
+ // that's necessary for next test.
+ eventSeq: [
+ new invokerChecker(EVENT_FOCUS, getNode("menu"))
+ ]
+ },
+ {
+ ID: "submenu",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "menuitem",
+ actionName: "click",
+ events: XUL_EVENTS
+ },
+ {
+ ID: "button",
+ actionName: "press",
+ events: XUL_EVENTS
+ },
+ {
+ ID: "buttonmenu",
+ actionName: "press",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "name_entry_label",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "labelWithPopup",
+ actionName: "click",
+ events: CLICK_EVENTS
+ }/*, // XXX: bug 490288
+ {
+ ID: "buttonmenu_item",
+ actionName: "click",
+ events: CLICK_EVENTS
+ }*/
+ ];
+
+ is(getAccessible("name_entry_label").firstChild.actionCount, 1, "label text should have 1 action");
+
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410765"
+ title="nsIAccessible actions testing">
+ Mozilla Bug 410765
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=504252"
+ title="Expose STATE_HASPOPUP on XUL elements that have an @popup attribute">
+ Mozilla Bug 504252
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar>
+ <menu label="menu" id="menu">
+ <menupopup>
+ <menuitem label="menu item" id="menuitem"/>
+ <menu label="submenu" id="submenu">
+ <menupopup>
+ <menuitem label="menu item"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <button label="button" id="button"/>
+
+ <button type="menu" id="buttonmenu" label="button">
+ <menupopup>
+ <menuitem label="item1" id="buttonmenu_item"/>
+ <menuitem label="item1"/>
+ </menupopup>
+ </button>
+
+ <label id="labelWithPopup" value="file name"
+ popup="fileContext"
+ tabindex="0"/>
+ <hbox>
+ <label id="name_entry_label" value="Name" control="name_entry"/>
+ <textbox id="name_entry"/>
+ </hbox>
+ </vbox>
+ </hbox>
+</window>
+
diff --git a/accessible/tests/mochitest/actions/test_keys.html b/accessible/tests/mochitest/actions/test_keys.html
new file mode 100644
index 000000000..2feebcadb
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_keys.html
@@ -0,0 +1,60 @@
+<html>
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <title>Keyboard shortcuts tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+
+ <script type="application/javascript">
+ function testAcessKey(aAccOrElmOrID, aKey)
+ {
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return;
+
+ is(acc.accessKey, aKey,
+ "Wrong keyboard shortcut on " + prettyName(aAccOrElmOrID));
+ }
+
+ function doTest()
+ {
+ testAcessKey("input1", "");
+ testAcessKey("input2", MAC ? "⌃⌥b" : "Alt+Shift+b");
+ testAcessKey("link", MAC ? "⌃⌥l" : "Alt+Shift+l");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=381599"
+ title="Inverse relations cache">
+ Mozilla Bug 381599
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <label accesskey="a">
+ <input id="input1"/>
+ </label>
+ <label accesskey="b" for="input2">
+ <input id="input2"/>
+ <a id="link" accesskey="l">link</a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_keys_menu.xul b/accessible/tests/mochitest/actions/test_keys_menu.xul
new file mode 100644
index 000000000..b63b6fab1
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_keys_menu.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL access keys and shortcut keys tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function openMenu(aMenuID, aMenuitemID)
+ {
+ this.menuNode = getNode(aMenuID),
+ this.menuitemNode = getNode(aMenuitemID),
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ // Show menu.
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ var menu = getAccessible(aMenuID);
+ is(menu.accessKey, (MAC ? "u" : "Alt+u"),
+ "Wrong accesskey on " + prettyName(this.menuitemNode));
+
+ var menuitem = getAccessible(aMenuitemID);
+ is(menuitem.accessKey, "p",
+ "Wrong accesskey on " + prettyName(this.menuitemNode));
+ is(menuitem.keyboardShortcut, (MAC ? "⌃l" : "Ctrl+l"),
+ "Wrong keyboard shortcut on " + prettyName(this.menuitemNode));
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "menuitem accesskey and shortcut test " +
+ prettyName(this.menuItemNode);
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new openMenu("menu", "menuitem"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=672092"
+ title="Reorganize access key and keyboard shortcut handling code">
+ Mozilla Bug 672092
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <keyset>
+ <key key="l" modifiers="control" id="key1"/>
+ </keyset>
+
+ <menubar>
+ <menu label="menu" id="menu" accesskey="u">
+ <menupopup>
+ <menuitem accesskey="p" key="key1" label="item1" id="menuitem"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/actions/test_link.html b/accessible/tests/mochitest/actions/test_link.html
new file mode 100644
index 000000000..dc96951d1
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_link.html
@@ -0,0 +1,147 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing on HTML links (HTML:a)</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ function getAnchorTargetDocumentAcc()
+ {
+ var thisTabDocAcc = getTabDocAccessible();
+ var thisDocTabPanelAcc = thisTabDocAcc.parent.parent;
+ var tabPanelsAcc = thisDocTabPanelAcc.parent;
+ var newDocTabPanelAcc = tabPanelsAcc.firstChild;
+ var nextAcc = newDocTabPanelAcc;
+
+ while (nextAcc = nextAcc.nextSibling) {
+ // Find the last accessible for a browser with about:mozilla loaded.
+ if (nextAcc.firstChild.DOMNode.currentURI.spec == "about:mozilla") {
+ newDocTabPanelAcc = nextAcc;
+ }
+ }
+
+ return newDocTabPanelAcc.firstChild.firstChild;
+ }
+
+ function linkChecker(aID)
+ {
+ this.type = EVENT_DOCUMENT_LOAD_COMPLETE;
+ this.__defineGetter__("target", getAnchorTargetDocumentAcc);
+
+ this.check = function linkChecker_check()
+ {
+ var anchorTargetWindow =
+ getAccessible(getAnchorTargetDocumentAcc(), [nsIAccessibleDocument]).
+ window;
+ anchorTargetWindow.close();
+ }
+
+ this.getID = function linkChecker_getID()
+ {
+ return "link '" + aID + "' states check ";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,eventTree,verbose");
+
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "link1",
+ actionName: "jump",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new linkChecker("link1")
+ ]
+ },
+ {
+ ID: "img1",
+ targetID: "link1",
+ actionName: "jump",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new linkChecker("link1")
+ ]
+ },
+ {
+ ID: "link2",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "img2",
+ targetID: "link2",
+ actionName: "jump",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "link3",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "img3",
+ targetID: "link3",
+ actionName: "jump",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "link4",
+ actionName: "click",
+ events: CLICK_EVENTS
+ },
+ {
+ ID: "img4",
+ targetID: "link4",
+ actionName: "jump",
+ events: CLICK_EVENTS
+ }
+ ];
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=423409"
+ title="Expose click action if mouseup and mousedown are registered">
+ Mozilla Bug 423409
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <a href="about:mozilla" id="link1" target="_blank">
+ <img src="../moz.png" id="img1">
+ </a>
+ <a id="link2" onmousedown="">
+ <img src="../moz.png" id="img2">
+ </a>
+ <a id="link3" onclick="">
+ <img src="../moz.png" id="img3">
+ </a>
+ <a id="link4" onmouseup="">
+ <img src="../moz.png" id="img4">
+ </a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_media.html b/accessible/tests/mochitest/actions/test_media.html
new file mode 100644
index 000000000..beb014ebc
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_media.html
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=483573
+-->
+<head>
+ <title>HTML5 audio/video tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+
+ // gA11yEventDumpID = "eventDump";
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function focusChecker(aAcc)
+ {
+ this.type = EVENT_FOCUS;
+ this.target = aAcc;
+ this.getID = function focusChecker_getID()
+ {
+ return "focus handling";
+ }
+ this.check = function focusChecker_check(aEvent)
+ {
+ testStates(this.target, STATE_FOCUSED);
+ }
+ }
+
+ function nameChecker(aAcc, aName)
+ {
+ this.type = EVENT_NAME_CHANGE;
+ this.target = aAcc;
+ this.getID = function nameChecker_getID()
+ {
+ return "name change handling";
+ },
+ this.check = function nameChecker_check(aEvent)
+ {
+ is(aEvent.accessible.name, aName,
+ "Wrong name of " + prettyName(aEvent.accessible) + " on focus");
+ }
+ }
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // test actions of audio controls
+
+ todo(false, "Focus test are disabled until bug 494175 is fixed.");
+
+ var audioElm = getAccessible("audio");
+ var playBtn = audioElm.firstChild;
+ var scrubber = playBtn.nextSibling.nextSibling.nextSibling;
+ var muteBtn = audioElm.lastChild.previousSibling;
+
+ var actions = [
+ {
+ ID: muteBtn,
+ actionName: "press",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ // new focusChecker(muteBtn),
+ new nameChecker(muteBtn, "Unmute"),
+ ]
+ },
+ // {
+ // ID: scrubber,
+ // actionName: "activate",
+ // events: null,
+ // eventSeq: [
+ // new focusChecker(scrubber)
+ // ]
+ // },
+ {
+ ID: playBtn,
+ actionName: "press",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ // new focusChecker(playBtn),
+ new nameChecker(playBtn, "Pause"),
+ ]
+ }
+ ];
+
+ testActions(actions); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Expose HTML5 video and audio elements' embedded controls through accessibility APIs"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <audio id="audio" src="../bug461281.ogg"
+ controls="true"></audio>
+
+ <div id="eventDump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_select.html b/accessible/tests/mochitest/actions/test_select.html
new file mode 100644
index 000000000..1a6c89619
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_select.html
@@ -0,0 +1,105 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing for HTML select</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debugging
+ function doTest()
+ {
+ var actionsArray = [
+ {
+ ID: "lb_apple",
+ actionIndex: 0,
+ actionName: "select",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new focusChecker("lb_apple")
+ ]
+ },
+ {
+ ID: "combobox",
+ actionIndex: 0,
+ actionName: "open",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new focusChecker("cb_orange")
+ ]
+ },
+ {
+ ID: "cb_apple",
+ actionIndex: 0,
+ actionName: "select",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new focusChecker("combobox")
+ ]
+ },
+ {
+ ID: "combobox",
+ actionIndex: 0,
+ actionName: "open",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new focusChecker("cb_apple")
+ ]
+ },
+ {
+ ID: "combobox",
+ actionIndex: 0,
+ actionName: "close",
+ events: CLICK_EVENTS,
+ eventSeq: [
+ new focusChecker("combobox")
+ ]
+ }
+ ];
+
+ testActions(actionsArray);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="listbox" size="2">
+ <option id="lb_orange">orange</option>
+ <option id="lb_apple">apple</option>
+ </select>
+
+ <select id="combobox">
+ <option id="cb_orange">orange</option>
+ <option id="cb_apple">apple</option>
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/actions/test_tree.xul b/accessible/tests/mochitest/actions/test_tree.xul
new file mode 100644
index 000000000..3958ed2fb
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_tree.xul
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree actions tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../actions.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Accessible tree testers
+
+ function stateFocusChecker(aAcc, aStates)
+ {
+ this.__proto__ = new focusChecker(aAcc);
+
+ this.check = function focusChecker_check(aEvent)
+ {
+ var states = aStates ? aStates : 0;
+ testStates(this.target, STATE_FOCUSED | STATE_SELECTED | states);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+ //gA11yEventDumpToConsole = true; // debug
+
+ function doTest()
+ {
+ var treeNode = getNode("tree");
+
+ var treeBodyNode = treeNode.boxObject.treeBody;
+
+ var tree = getAccessible(treeNode);
+ var expandedTreeItem = tree.getChildAt(2);
+ var collapsedTreeItem = tree.getChildAt(5);
+
+ var actions = [
+ {
+ ID: expandedTreeItem,
+ actionName: "activate",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ eventSeq: [
+ new stateFocusChecker(expandedTreeItem, STATE_EXPANDED)
+ ]
+ },
+ {
+ ID: collapsedTreeItem,
+ actionName: "expand",
+ actionIndex: 1,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ checkOnClickEvent: function check(aEvent)
+ {
+ testStates(this.ID, STATE_EXPANDED);
+ }
+ },
+ {
+ ID: collapsedTreeItem,
+ actionName: "collapse",
+ actionIndex: 1,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ checkOnClickEvent: function check(aEvent)
+ {
+ testStates(this.ID, STATE_COLLAPSED);
+ }
+ }
+ ];
+
+ testActions(actions); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTreeTreeView());
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1" minheight="100px">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/actions/test_treegrid.xul b/accessible/tests/mochitest/actions/test_treegrid.xul
new file mode 100644
index 000000000..0bfb3d662
--- /dev/null
+++ b/accessible/tests/mochitest/actions/test_treegrid.xul
@@ -0,0 +1,197 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree actions tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../actions.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Accessible tree testers
+
+ function focusChecker(aAcc, aStates)
+ {
+ this.type = EVENT_FOCUS;
+ this.target = aAcc;
+ this.getID = function focusChecker_getID()
+ {
+ return "focus handling";
+ }
+ this.check = function focusChecker_check(aEvent)
+ {
+ var states = aStates ? aStates : 0;
+ testStates(this.target, STATE_FOCUSED | STATE_SELECTED | states);
+ }
+ }
+
+ function stateChangeChecker(aAcc, aIsEnabled)
+ {
+ this.type = EVENT_STATE_CHANGE;
+ this.target = aAcc;
+ this.getID = function stateChangeChecker_getID()
+ {
+ return "state change handling";
+ }
+ this.check = function stateChangeChecker_check(aEvent)
+ {
+ if (aIsEnabled)
+ testStates(this.target, STATE_CHECKED);
+ else
+ testStates(this.target, STATE_CHECKABLE, 0, STATE_CHECKED);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTestActions()
+ {
+ var treeNode = getNode("tabletree");
+
+ var treeBodyNode = treeNode.boxObject.treeBody;
+ treeNode.focus();
+
+ var tree = getAccessible(treeNode);
+
+ var expandedTreeItem = tree.getChildAt(2);
+ var collapsedTreeItem = tree.getChildAt(5);
+ var cycleCell = expandedTreeItem.getChildAt(0);
+ var checkableCell = expandedTreeItem.getChildAt(3);
+
+ var actions = [
+ {
+ ID: expandedTreeItem,
+ actionName: "activate",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ eventSeq: [
+ new focusChecker(expandedTreeItem, STATE_EXPANDED)
+ ]
+ },
+ {
+ ID: collapsedTreeItem,
+ actionName: "expand",
+ actionIndex: 1,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ check: function check(aEvent)
+ {
+ testStates(this.ID, STATE_EXPANDED);
+ }
+ },
+ {
+ ID: collapsedTreeItem,
+ actionName: "collapse",
+ actionIndex: 1,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ check: function check(aEvent)
+ {
+ testStates(this.ID, STATE_COLLAPSED);
+ }
+ },
+ {
+ ID: cycleCell,
+ actionName: "cycle",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode
+ },
+ {
+ ID: checkableCell,
+ actionName: "uncheck",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ eventSeq: [
+ new stateChangeChecker(checkableCell, false)
+ ]
+ },
+ {
+ ID: checkableCell,
+ actionName: "check",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ targetID: treeBodyNode,
+ eventSeq: [
+ new stateChangeChecker(checkableCell, true)
+ ]
+ }
+ ];
+
+ testActions(actions); // Will call SimpleTest.finish();
+ }
+
+ // gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ var treeNode = getNode("tabletree");
+ waitForEvent(EVENT_REORDER, treeNode, doTestActions);
+ treeNode.view = new nsTreeTreeView();
+ }
+
+ function test1()
+ {
+ getNode("tabletree").view.setCellValue(0, boxObj.columns.firstColumn, "false");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tabletree" flex="1" editable="true">
+ <treecols>
+ <treecol id="tabletree_col1" cycler="true" label="cycler"/>
+ <treecol id="tabletree_col2" flex="1" primary="true" label="column1"/>
+ <treecol id="tabletree_col3" flex="1" label="column2"/>
+ <treecol id="tabletree_col4" flex="1" label="checker"
+ type="checkbox" editable="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ <button oncommand="test1();" label="uncheck"/>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/aom/a11y.ini b/accessible/tests/mochitest/aom/a11y.ini
new file mode 100644
index 000000000..03085c1de
--- /dev/null
+++ b/accessible/tests/mochitest/aom/a11y.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_general.html]
diff --git a/accessible/tests/mochitest/aom/test_general.html b/accessible/tests/mochitest/aom/test_general.html
new file mode 100644
index 000000000..5812ac55f
--- /dev/null
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Accessibility API: generic</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script>
+ 'use strict';
+
+ SimpleTest.waitForExplicitFinish();
+ const finish = SimpleTest.finish.bind(SimpleTest);
+ enablePref()
+ .then(createIframe)
+ .then(checkImplementation)
+ .catch(err => {
+ dump(`${err}: ${err.stack}`);
+ finish();
+ });
+
+ function enablePref() {
+ const ops = {
+ "set": [
+ [ "accessibility.AOM.enabled", true ],
+ ],
+ };
+ return SpecialPowers.pushPrefEnv(ops);
+ }
+
+ // WebIDL conditional annotations for an interface are evaluated once per
+ // global, so we need to create an iframe to see the effects of calling
+ // enablePref().
+ function createIframe() {
+ return new Promise((resolve) => {
+ let iframe = document.createElement("iframe");
+ iframe.src = "about:blank";
+ iframe.onload = () => resolve(iframe.contentDocument);
+ document.body.appendChild(iframe);
+ });
+ }
+
+ // Check that the WebIDL is as expected.
+ function checkImplementation(ifrDoc) {
+ let anode = ifrDoc.accessibleNode;
+ ok(anode, "DOM document has accessible node");
+
+ is(anode.role, 'document', 'correct role of a document accessible node');
+ is(anode.DOMNode, ifrDoc, 'correct DOM Node of a document accessible node');
+
+ finish();
+ }
+ </script>
+</head>
diff --git a/accessible/tests/mochitest/attributes.js b/accessible/tests/mochitest/attributes.js
new file mode 100644
index 000000000..b2ac78cba
--- /dev/null
+++ b/accessible/tests/mochitest/attributes.js
@@ -0,0 +1,382 @@
+////////////////////////////////////////////////////////////////////////////////
+// Object attributes.
+
+/**
+ * Test object attributes.
+ *
+ * @param aAccOrElmOrID [in] the accessible identifier
+ * @param aAttrs [in] the map of expected object attributes
+ * (name/value pairs)
+ * @param aSkipUnexpectedAttrs [in] points this function doesn't fail if
+ * unexpected attribute is encountered
+ */
+function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs)
+{
+ testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs);
+}
+
+/**
+ * Test object attributes that must not be present.
+ *
+ * @param aAccOrElmOrID [in] the accessible identifier
+ * @param aAbsentAttrs [in] map of attributes that should not be
+ * present (name/value pairs)
+ */
+function testAbsentAttrs(aAccOrElmOrID, aAbsentAttrs)
+{
+ testAttrsInternal(aAccOrElmOrID, {}, true, aAbsentAttrs);
+}
+
+/**
+ * Test CSS based object attributes.
+ */
+function testCSSAttrs(aID)
+{
+ var node = document.getElementById(aID);
+ var computedStyle = document.defaultView.getComputedStyle(node, "");
+
+ var attrs = {
+ "display": computedStyle.display,
+ "text-align": computedStyle.textAlign,
+ "text-indent": computedStyle.textIndent,
+ "margin-left": computedStyle.marginLeft,
+ "margin-right": computedStyle.marginRight,
+ "margin-top": computedStyle.marginTop,
+ "margin-bottom": computedStyle.marginBottom
+ };
+ testAttrs(aID, attrs, true);
+}
+
+/**
+ * Test the accessible that it doesn't have CSS-based object attributes.
+ */
+function testAbsentCSSAttrs(aID)
+{
+ var attrs = {
+ "display": "",
+ "text-align": "",
+ "text-indent": "",
+ "margin-left": "",
+ "margin-right": "",
+ "margin-top": "",
+ "margin-bottom": ""
+ };
+ testAbsentAttrs(aID, attrs);
+}
+
+/**
+ * Test group object attributes (posinset, setsize and level) and
+ * nsIAccessible::groupPosition() method.
+ *
+ * @param aAccOrElmOrID [in] the ID, DOM node or accessible
+ * @param aPosInSet [in] the value of 'posinset' attribute
+ * @param aSetSize [in] the value of 'setsize' attribute
+ * @param aLevel [in, optional] the value of 'level' attribute
+ */
+function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel)
+{
+ var acc = getAccessible(aAccOrElmOrID);
+ var levelObj = {}, posInSetObj = {}, setSizeObj = {};
+ acc.groupPosition(levelObj, setSizeObj, posInSetObj);
+
+ if (aPosInSet && aSetSize) {
+ is(posInSetObj.value, aPosInSet,
+ "Wrong group position (posinset) for " + prettyName(aAccOrElmOrID));
+ is(setSizeObj.value, aSetSize,
+ "Wrong size of the group (setsize) for " + prettyName(aAccOrElmOrID));
+
+ var attrs = {
+ "posinset": String(aPosInSet),
+ "setsize": String(aSetSize)
+ };
+ testAttrs(aAccOrElmOrID, attrs, true);
+ }
+
+ if (aLevel) {
+ is(levelObj.value, aLevel,
+ "Wrong group level for " + prettyName(aAccOrElmOrID));
+
+ var attrs = { "level" : String(aLevel) };
+ testAttrs(aAccOrElmOrID, attrs, true);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Text attributes.
+
+/**
+ * Test text attributes.
+ *
+ * @param aID [in] the ID of DOM element having text
+ * accessible
+ * @param aOffset [in] the offset inside text accessible to fetch
+ * text attributes
+ * @param aAttrs [in] the map of expected text attributes
+ * (name/value pairs) exposed at the offset
+ * @param aDefAttrs [in] the map of expected text attributes
+ * (name/value pairs) exposed on hyper text
+ * accessible
+ * @param aStartOffset [in] expected start offset where text attributes
+ * are applied
+ * @param aEndOffset [in] expected end offset where text attribute
+ * are applied
+ * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
+ * unexpected attribute is encountered
+ */
+function testTextAttrs(aID, aOffset, aAttrs, aDefAttrs,
+ aStartOffset, aEndOffset, aSkipUnexpectedAttrs)
+{
+ var accessible = getAccessible(aID, [nsIAccessibleText]);
+ if (!accessible)
+ return;
+
+ var startOffset = { value: -1 };
+ var endOffset = { value: -1 };
+
+ // do not include attributes exposed on hyper text accessbile
+ var attrs = getTextAttributes(aID, accessible, false, aOffset,
+ startOffset, endOffset);
+
+ if (!attrs)
+ return;
+
+ var errorMsg = " for " + aID + " at offset " + aOffset;
+
+ is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg);
+ is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg);
+
+ compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
+
+ // include attributes exposed on hyper text accessbile
+ var expectedAttrs = {};
+ for (var name in aAttrs)
+ expectedAttrs[name] = aAttrs[name];
+
+ for (var name in aDefAttrs) {
+ if (!(name in expectedAttrs))
+ expectedAttrs[name] = aDefAttrs[name];
+ }
+
+ attrs = getTextAttributes(aID, accessible, true, aOffset,
+ startOffset, endOffset);
+
+ if (!attrs)
+ return;
+
+ compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs);
+}
+
+/**
+ * Test default text attributes.
+ *
+ * @param aID [in] the ID of DOM element having text
+ * accessible
+ * @param aDefAttrs [in] the map of expected text attributes
+ * (name/value pairs)
+ * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
+ * unexpected attribute is encountered
+ */
+function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs)
+{
+ var accessible = getAccessible(aID, [nsIAccessibleText]);
+ if (!accessible)
+ return;
+
+ var defAttrs = null;
+ try{
+ defAttrs = accessible.defaultTextAttributes;
+ } catch (e) {
+ }
+
+ if (!defAttrs) {
+ ok(false, "Can't get default text attributes for " + aID);
+ return;
+ }
+
+ var errorMsg = ". Getting default text attributes for " + aID;
+ compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
+}
+
+/**
+ * Test text attributes for wrong offset.
+ */
+function testTextAttrsWrongOffset(aID, aOffset)
+{
+ var res = false;
+ try {
+ var s = {}, e = {};
+ var acc = getAccessible(ID, [nsIAccessibleText]);
+ acc.getTextAttributes(false, 157, s, e);
+ } catch (e) {
+ res = true;
+ }
+
+ ok(res,
+ "text attributes are calculated successfully at wrong offset " + aOffset + " for " + prettyName(aID));
+}
+
+const kNormalFontWeight =
+ function equalsToNormal(aWeight) { return aWeight <= 400 ; }
+
+const kBoldFontWeight =
+ function equalsToBold(aWeight) { return aWeight > 400; }
+
+// The pt font size of the input element can vary by Linux distro.
+const kInputFontSize = WIN ?
+ "10pt" : (MAC ? "8pt" : function() { return true; });
+
+const kAbsentFontFamily =
+ function(aFontFamily) { return aFontFamily != "sans-serif"; }
+const kInputFontFamily =
+ function(aFontFamily) { return aFontFamily != "sans-serif"; }
+
+const kMonospaceFontFamily =
+ function(aFontFamily) { return aFontFamily != "monospace"; }
+const kSansSerifFontFamily =
+ function(aFontFamily) { return aFontFamily != "sans-serif"; }
+const kSerifFontFamily =
+ function(aFontFamily) { return aFontFamily != "serif"; }
+
+const kCursiveFontFamily = LINUX ? "DejaVu Serif" : "Comic Sans MS";
+
+/**
+ * Return used font from the given computed style.
+ */
+function fontFamily(aComputedStyle)
+{
+ var name = aComputedStyle.fontFamily;
+ switch (name) {
+ case "monospace":
+ return kMonospaceFontFamily;
+ case "sans-serif":
+ return kSansSerifFontFamily;
+ case "serif":
+ return kSerifFontFamily;
+ default:
+ return name;
+ }
+}
+
+/**
+ * Build an object of default text attributes expected for the given accessible.
+ *
+ * @param aID [in] identifier of accessible
+ * @param aFontSize [in] font size
+ * @param aFontWeight [in, optional] kBoldFontWeight or kNormalFontWeight,
+ * default value is kNormalFontWeight
+ */
+function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily)
+{
+ var elm = getNode(aID);
+ var computedStyle = document.defaultView.getComputedStyle(elm, "");
+ var bgColor = computedStyle.backgroundColor == "transparent" ?
+ "rgb(255, 255, 255)" : computedStyle.backgroundColor;
+
+ var defAttrs = {
+ "font-style": computedStyle.fontStyle,
+ "font-size": aFontSize,
+ "background-color": bgColor,
+ "font-weight": aFontWeight ? aFontWeight : kNormalFontWeight,
+ "color": computedStyle.color,
+ "font-family": aFontFamily ? aFontFamily : fontFamily(computedStyle),
+ "text-position": computedStyle.verticalAlign
+ };
+
+ return defAttrs;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private.
+
+function getTextAttributes(aID, aAccessible, aIncludeDefAttrs, aOffset,
+ aStartOffset, aEndOffset)
+{
+ // This function expects the passed in accessible to already be queried for
+ // nsIAccessibleText.
+ var attrs = null;
+ try {
+ attrs = aAccessible.getTextAttributes(aIncludeDefAttrs, aOffset,
+ aStartOffset, aEndOffset);
+ } catch (e) {
+ }
+
+ if (attrs)
+ return attrs;
+
+ ok(false, "Can't get text attributes for " + aID);
+ return null;
+}
+
+function testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs,
+ aAbsentAttrs)
+{
+ var accessible = getAccessible(aAccOrElmOrID);
+ if (!accessible)
+ return;
+
+ var attrs = null;
+ try {
+ attrs = accessible.attributes;
+ } catch (e) { }
+
+ if (!attrs) {
+ ok(false, "Can't get object attributes for " + prettyName(aAccOrElmOrID));
+ return;
+ }
+
+ var errorMsg = " for " + prettyName(aAccOrElmOrID);
+ compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs, aAbsentAttrs);
+}
+
+function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs,
+ aAbsentAttrs)
+{
+ // Check if all obtained attributes are expected and have expected value.
+ var enumerate = aAttrs.enumerate();
+ while (enumerate.hasMoreElements()) {
+ var prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
+
+ if (!(prop.key in aExpectedAttrs)) {
+ if (!aSkipUnexpectedAttrs)
+ ok(false, "Unexpected attribute '" + prop.key + "' having '" +
+ prop.value + "'" + aErrorMsg);
+ } else {
+ var msg = "Attribute '" + prop.key + "' has wrong value" + aErrorMsg;
+ var expectedValue = aExpectedAttrs[prop.key];
+
+ if (typeof expectedValue == "function")
+ ok(expectedValue(prop.value), msg);
+ else
+ is(prop.value, expectedValue, msg);
+ }
+ }
+
+ // Check if all expected attributes are presented.
+ for (var name in aExpectedAttrs) {
+ var value = "";
+ try {
+ value = aAttrs.getStringProperty(name);
+ } catch(e) { }
+
+ if (!value)
+ ok(false,
+ "There is no expected attribute '" + name + "' " + aErrorMsg);
+ }
+
+ // Check if all unexpected attributes are absent.
+ if (aAbsentAttrs) {
+ for (var name in aAbsentAttrs) {
+ var wasFound = false;
+
+ var enumerate = aAttrs.enumerate();
+ while (enumerate.hasMoreElements()) {
+ var prop = enumerate.getNext().QueryInterface(nsIPropertyElement);
+ if (prop.key == name)
+ wasFound = true;
+ }
+ }
+
+ ok(!wasFound,
+ "There is an unexpected attribute '" + name + "' " + aErrorMsg);
+ }
+}
diff --git a/accessible/tests/mochitest/attributes/a11y.ini b/accessible/tests/mochitest/attributes/a11y.ini
new file mode 100644
index 000000000..ad53ecd27
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/a11y.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_obj.html]
+[test_obj_css.html]
+[test_obj_css.xul]
+[test_obj_group.html]
+[test_obj_group.xul]
+[test_obj_group_tree.xul]
+[test_tag.html]
+[test_xml-roles.html]
diff --git a/accessible/tests/mochitest/attributes/test_obj.html b/accessible/tests/mochitest/attributes/test_obj.html
new file mode 100644
index 000000000..9e147e1d1
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj.html
@@ -0,0 +1,278 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=475006
+https://bugzilla.mozilla.org/show_bug.cgi?id=391829
+https://bugzilla.mozilla.org/show_bug.cgi?id=581952
+https://bugzilla.mozilla.org/show_bug.cgi?id=558036
+-->
+<head>
+ <title>Group attributes tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // aria
+ testAttrs("atomic", {"atomic" : "true", "container-atomic" : "true"}, true);
+ testAttrs(getNode("atomic").firstChild, {"container-atomic" : "true"}, true);
+ testAbsentAttrs("atomic_false", {"atomic" : "false", "container-atomic" : "false"});
+ testAbsentAttrs(getNode("atomic_false").firstChild, {"container-atomic" : "false"});
+
+ testAttrs("autocomplete", {"autocomplete" : "true"}, true);
+ testAttrs("checkbox", {"checkable" : "true"}, true);
+ testAttrs("checkedCheckbox", {"checkable" : "true"}, true);
+ testAttrs("checkedMenuitem", {"checkable" : "true"}, true);
+ testAttrs("checkedOption", {"checkable" : "true"}, true);
+ testAttrs("checkedRadio", {"checkable" : "true"}, true);
+ testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
+ testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
+ testAttrs("grabbed", {"grabbed" : "true"}, true);
+ testAbsentAttrs("haspopup", { "haspopup": "false" });
+ testAttrs("hidden", {"hidden" : "true"}, true);
+ testAbsentAttrs("hidden_false", { "hidden": "false" });
+ testAbsentAttrs("modal", {"modal" : "true"});
+ testAttrs("sortAscending", {"sort" : "ascending"}, true);
+ testAttrs("sortDescending", {"sort" : "descending"}, true);
+ testAttrs("sortNone", {"sort" : "none"}, true);
+ testAttrs("sortOther", {"sort" : "other"}, true);
+ testAttrs("roledescr", {"roledescription" : "spreadshit"}, true);
+ testAttrs("currentPage", {"current" : "page"}, true);
+
+ // inherited attributes by subdocuments
+ var subdoc = getAccessible("iframe").firstChild;
+ testAttrs(subdoc, {"busy" : "true"}, true);
+
+ // live object attribute
+
+ // HTML
+ testAttrs("output", {"live" : "polite"}, true);
+
+ // ARIA
+ testAttrs("live", {"live" : "polite"}, true);
+ testAttrs("live2", {"live" : "polite"}, true);
+ testAbsentAttrs("live3", {"live" : ""});
+ testAttrs("log", {"live" : "polite"}, true);
+ testAttrs("logAssertive", {"live" : "assertive"}, true);
+ testAttrs("marquee", {"live" : "off"}, true);
+ testAttrs("status", {"live" : "polite"}, true);
+ testAttrs("timer", {"live" : "off"}, true);
+ testAbsentAttrs("tablist", {"live" : "polite"});
+
+ // container-live object attribute
+ testAttrs("liveChild", {"container-live" : "polite"}, true);
+ testAttrs("live2Child", {"container-live" : "polite"}, true);
+ testAttrs("logChild", {"container-live" : "polite"}, true);
+ testAttrs("logAssertiveChild", {"container-live" : "assertive"}, true);
+ testAttrs("marqueeChild", {"container-live" : "off"}, true);
+ testAttrs("statusChild", {"container-live" : "polite"}, true);
+ testAttrs("timerChild", {"container-live" : "off"}, true);
+ testAbsentAttrs("tablistChild", {"container-live" : "polite"});
+
+ // container-live-role object attribute
+ testAttrs("log", {"container-live-role" : "log"}, true);
+ testAttrs("logAssertive", {"container-live-role" : "log"}, true);
+ testAttrs("marquee", {"container-live-role" : "marquee"}, true);
+ testAttrs("status", {"container-live-role" : "status"}, true);
+ testAttrs("timer", {"container-live-role" : "timer"}, true);
+ testAttrs("logChild", {"container-live-role" : "log"}, true);
+ testAttrs("logAssertive", {"container-live-role" : "log"}, true);
+ testAttrs("logAssertiveChild", {"container-live-role" : "log"}, true);
+ testAttrs("marqueeChild", {"container-live-role" : "marquee"}, true);
+ testAttrs("statusChild", {"container-live-role" : "status"}, true);
+ testAttrs("timerChild", {"container-live-role" : "timer"}, true);
+ testAbsentAttrs("tablistChild", {"container-live-role" : "tablist"});
+
+ // absent aria-label and aria-labelledby object attribute
+ testAbsentAttrs("label", {"label" : "foo"});
+ testAbsentAttrs("labelledby", {"labelledby" : "label"});
+
+ // container that has no default live attribute
+ testAttrs("liveGroup", {"live" : "polite"}, true);
+ testAttrs("liveGroupChild", {"container-live" : "polite"}, true);
+ testAttrs("liveGroup", {"container-live-role" : "group"}, true);
+ testAttrs("liveGroupChild", {"container-live-role" : "group"}, true);
+
+ // text input type
+ testAbsentAttrs("button", { "text-input-type": "button"});
+ testAbsentAttrs("checkbox", { "text-input-type": "checkbox"});
+ testAbsentAttrs("radio", { "text-input-type": "radio"});
+ testAttrs("email", {"text-input-type" : "email"}, true);
+ testAttrs("search", {"text-input-type" : "search"}, true);
+ testAttrs("tel", {"text-input-type" : "tel"}, true);
+ testAttrs("url", {"text-input-type" : "url"}, true);
+
+ // ARIA
+ testAttrs("searchbox", {"text-input-type" : "search"}, true);
+
+ // html
+ testAttrs("radio", {"checkable" : "true"}, true);
+ testAttrs("checkbox", {"checkable" : "true"}, true);
+ testAttrs("draggable", {"draggable" : "true"}, true);
+ testAttrs("th1", { "abbr": "SS#" }, true);
+ testAttrs("th2", { "abbr": "SS#" }, true);
+ testAttrs("th2", { "axis": "social" }, true);
+
+ // don't barf on an empty abbr element.
+ testAbsentAttrs("th3", { "abbr": "" }, true);
+
+ // application accessible
+ if (WIN) {
+ var gfxInfo = Components.classes["@mozilla.org/gfx/info;1"].
+ getService(Components.interfaces.nsIGfxInfo);
+ var attrs = {
+ "D2D": (gfxInfo.D2DEnabled ? "true" : "false")
+ }
+ testAttrs(getApplicationAccessible(), attrs, false);
+ }
+
+ // no object attributes
+ testAbsentAttrs(getAccessible("listitem").firstChild, { "tag": "" });
+
+ // experimental aria
+ testAttrs("experimental", {"blah" : "true"}, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=475006"
+ title="Extend nsARIAMap to capture ARIA attribute characteristics">
+ Mozilla Bug 475006
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=391829"
+ title="Add support for container-live-role to object attributes">
+ Mozilla Bug 391829
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=581952"
+ title="Make explicit that aria-label is not an object attribute">
+ Mozilla Bug 475006
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+ title="make HTML <output> accessible">
+ Mozilla Bug 558036
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=896400"
+ title="Tablist should no longer be an implicit live region">
+ Mozilla Bug 896400
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=563862"
+ title="Expand support for nsIAccessibleEvent::OBJECT_ATTRIBUTE_CHANGE">
+ Mozilla Bug 563862
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=819303"
+ title="crash in nsTextEquivUtils::AppendTextEquivFromTextContent">
+ Mozilla Bug 819303
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=838407"
+ title="aria-hidden false value shouldn't be exposed via object attributes">
+ Mozilla Bug 838407
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
+ title="ARIA 1.1: Support role 'searchbox'">
+ Mozilla Bug 1121518
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- aria -->
+ <div id="atomic" aria-atomic="true">live region</div>
+ <div id="atomic_false" aria-atomic="false">live region</div>
+ <div id="autocomplete" role="textbox" aria-autocomplete="true"></div>
+ <div id="checkbox" role="checkbox"></div>
+ <div id="checkedCheckbox" role="checkbox" aria-checked="true"></div>
+ <div id="checkedMenuitem" role="menuitem" aria-checked="true"></div>
+ <div id="checkedOption" role="option" aria-checked="true"></div>
+ <div id="checkedRadio" role="radio" aria-checked="true"></div>
+ <div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
+ <div id="dropeffect" aria-dropeffect="copy"></div>
+ <div id="grabbed" aria-grabbed="true"></div>
+ <div id="haspopup" aria-haspopup="true"></div>
+ <div id="hidden" aria-hidden="true"></div>
+ <div id="hidden_false" aria-hidden="false"></div>
+ <div id="modal" aria-modal="true"></div>
+ <div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
+ <div id="sortDescending" role="columnheader" aria-sort="descending"></div>
+ <div id="sortNone" role="columnheader" aria-sort="none"></div>
+ <div id="sortOther" role="columnheader" aria-sort="other"></div>
+ <div id="roledescr" aria-roledescription="spreadshit"></div>
+ <div id="currentPage" aria-current="page"></div>
+
+ <!-- inherited from iframe -->
+ <iframe id="iframe" src="data:text/html,<html><body></body></html>"
+ aria-busy="true"></iframe>
+
+ <!-- html -->
+ <output id="output"></output>
+
+ <!-- back to aria -->
+ <div id="live" aria-live="polite">excuse <div id="liveChild">me</div></div>
+ <div id="live2" role="marquee" aria-live="polite">excuse <div id="live2Child">me</div></div>
+ <div id="live3" role="region">excuse</div>
+ <div id="log" role="log">excuse <div id="logChild">me</div></div>
+ <div id="logAssertive" role="log" aria-live="assertive">excuse <div id="logAssertiveChild">me</div></div>
+ <div id="marquee" role="marquee">excuse <div id="marqueeChild">me</div></div>
+ <div id="status" role="status">excuse <div id="statusChild">me</div></div>
+ <div id="tablist" role="tablist">tablist <div id="tablistChild">tab</div></div>
+ <div id="timer" role="timer">excuse <div id="timerChild">me</div></div>
+
+ <!-- aria-label[ledby] should not be an object attribute -->
+ <div id="label" role="checkbox" aria-label="foo"></div>
+ <div id="labelledby" role="checkbox" aria-labelledby="label"></div>
+
+ <!-- unusual live case -->
+ <div id="liveGroup" role="group" aria-live="polite">
+ excuse <div id="liveGroupChild">me</div>
+ </div>
+
+ <!-- text input type -->
+ <input id="button" type="button"/>
+ <input id="email" type="email"/>
+ <input id="search" type="search"/>
+ <input id="tel" type="tel"/>
+ <input id="url" type="url"/>
+ <div id="searchbox" role="searchbox"></div>
+
+ <!-- html -->
+ <input id="radio" type="radio"/>
+ <input id="checkbox" type="checkbox"/>
+ <div id="draggable" draggable="true">Draggable div</div>
+ <table>
+ <tr>
+ <th id="th1"><abbr title="Social Security Number">SS#</abbr></th>
+ <th id="th2" abbr="SS#" axis="social">Social Security Number</th>
+ <th id="th3"><abbr></abbr></th>
+ </tr>
+ </table>
+
+ <ul>
+ <li id="listitem">item
+ </ul>
+
+ <!-- experimental aria -->
+ <div id="experimental" aria-blah="true">Fake beer</div>
+</body>
+</html>
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 000000000..6bf34543f
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj_css.html
@@ -0,0 +1,231 @@
+<html>
+<head>
+ <title>CSS-like attributes tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ var gQueue = null;
+
+ function removeElm(aID)
+ {
+ this.node = getNode(aID);
+ this.accessible = getAccessible(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.accessible)
+ ];
+
+ this.invoke = function removeElm_invoke()
+ {
+ this.node.parentNode.removeChild(this.node);
+ }
+
+ this.check = function removeElm_check()
+ {
+ testAbsentCSSAttrs(this.accessible);
+ }
+
+ this.getID = function removeElm_getID()
+ {
+ return "test CSS-based attributes on removed accessible";
+ }
+ }
+
+ function doTest()
+ {
+ // CSS display
+ testCSSAttrs("display_block");
+ testCSSAttrs("display_inline");
+ testCSSAttrs("display_inline-block");
+ testCSSAttrs("display_list-item");
+ testCSSAttrs("display_table");
+ testCSSAttrs("display_inline-table");
+ testCSSAttrs("display_table-row-group");
+ testCSSAttrs("display_table-column");
+ testCSSAttrs("display_table-column-group");
+ testCSSAttrs("display_table-header-group");
+ testCSSAttrs("display_table-footer-group");
+ testCSSAttrs("display_table-row");
+ testCSSAttrs("display_table-cell");
+ testCSSAttrs("display_table-caption");
+
+ // CSS text-align
+ testCSSAttrs("text-align_left");
+ testCSSAttrs("text-align_right");
+ testCSSAttrs("text-align_center");
+ testCSSAttrs("text-align_justify");
+ testCSSAttrs("text-align_inherit");
+
+ // CSS text-indent
+ testCSSAttrs("text-indent_em");
+ testCSSAttrs("text-indent_ex");
+ testCSSAttrs("text-indent_in");
+ testCSSAttrs("text-indent_cm");
+ testCSSAttrs("text-indent_mm");
+ testCSSAttrs("text-indent_pt");
+ testCSSAttrs("text-indent_pc");
+ testCSSAttrs("text-indent_px");
+ testCSSAttrs("text-indent_percent");
+ testCSSAttrs("text-indent_inherit");
+
+ // CSS margin
+ testCSSAttrs("margin_em");
+ testCSSAttrs("margin_ex");
+ testCSSAttrs("margin_in");
+ testCSSAttrs("margin_cm");
+ testCSSAttrs("margin_mm");
+ testCSSAttrs("margin_pt");
+ testCSSAttrs("margin_pc");
+ testCSSAttrs("margin_px");
+ testCSSAttrs("margin_percent");
+ testCSSAttrs("margin_auto");
+ testCSSAttrs("margin_inherit");
+
+ testCSSAttrs("margin-left");
+ testCSSAttrs("margin-right");
+ testCSSAttrs("margin-top");
+ testCSSAttrs("margin-bottom");
+
+ // Elements
+ testCSSAttrs("span");
+ testCSSAttrs("div");
+ testCSSAttrs("p");
+ testCSSAttrs("input");
+ testCSSAttrs("table");
+ testCSSAttrs("tr");
+ testCSSAttrs("td");
+
+ // no CSS-based object attributes
+ testAbsentCSSAttrs(getAccessible("listitem").firstChild);
+
+ gQueue = new eventQueue();
+ gQueue.push(new removeElm("div"));
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=439566"
+ title="Include the css display property as an IAccessible2 object attribute">
+ Mozilla Bug 439566
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=460932"
+ title="text-indent and text-align should really be object attribute">
+ Mozilla Bug 460932
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=689540"
+ title="Expose IA2 margin- object attributes">
+ Mozilla Bug 689540
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=714579"
+ title="Don't use GetComputedStyle for object attribute calculation">
+ Mozilla Bug 714579
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=729831"
+ title="Don't expose CSS-based object attributes on not in tree accessible and accessible having no DOM element">
+ Mozilla Bug 729831
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="display_block" role="img"
+ style="display: block;">display: block</div>
+ <div id="display_inline" role="img"
+ style="display: inline;">display: inline</div>
+ <div id="display_inline-block" role="img"
+ style="display: inline-block;">display: inline-block</div>
+ <div id="display_list-item" role="img"
+ style="display: list-item;">display: list-item</div>
+ <div id="display_table" role="img"
+ style="display: table;">display: table</div>
+ <div id="display_inline-table" role="img"
+ style="display: inline-table;">display: inline-table</div>
+ <div id="display_table-row-group" role="img"
+ style="display: table-row-group;">display: table-row-group</div>
+ <div id="display_table-column" role="img"
+ style="display: table-column;">display: table-column</div>
+ <div id="display_table-column-group" role="img"
+ style="display: table-column-group;">display: table-column-group</div>
+ <div id="display_table-header-group" role="img"
+ style="display: table-header-group;">display: table-header-group</div>
+ <div id="display_table-footer-group" role="img"
+ style="display: table-footer-group;">display: table-footer-group</div>
+ <div id="display_table-row" role="img"
+ style="display: table-row;">display: table-row</div>
+ <div id="display_table-cell" role="img"
+ style="display: table-cell;">display: table-cell</div>
+ <div id="display_table-caption" role="img"
+ style="display: table-caption;">display: table-caption</div>
+
+ <p id="text-align_left" style="text-align: left;">text-align: left</p>
+ <p id="text-align_right" style="text-align: right;">text-align: right</p>
+ <p id="text-align_center" style="text-align: center;">text-align: center</p>
+ <p id="text-align_justify" style="text-align: justify;">text-align: justify</p>
+ <p id="text-align_inherit" style="text-align: inherit;">text-align: inherit</p>
+
+ <p id="text-indent_em" style="text-indent: 0.5em;">text-indent: 0.5em</p>
+ <p id="text-indent_ex" style="text-indent: 1ex;">text-indent: 1ex</p>
+ <p id="text-indent_in" style="text-indent: 0.5in;">text-indent: 0.5in</p>
+ <p id="text-indent_cm" style="text-indent: 2cm;">text-indent: 2cm</p>
+ <p id="text-indent_mm" style="text-indent: 10mm;">text-indent: 10mm</p>
+ <p id="text-indent_pt" style="text-indent: 30pt;">text-indent: 30pt</p>
+ <p id="text-indent_pc" style="text-indent: 2pc;">text-indent: 2pc</p>
+ <p id="text-indent_px" style="text-indent: 5px;">text-indent: 5px</p>
+ <p id="text-indent_percent" style="text-indent: 10%;">text-indent: 10%</p>
+ <p id="text-indent_inherit" style="text-indent: inherit;">text-indent: inherit</p>
+
+ <p id="margin_em" style="margin: 0.5em;">margin: 0.5em</p>
+ <p id="margin_ex" style="margin: 1ex;">margin: 1ex</p>
+ <p id="margin_in" style="margin: 0.5in;">margin: 0.5in</p>
+ <p id="margin_cm" style="margin: 2cm;">margin: 2cm</p>
+ <p id="margin_mm" style="margin: 10mm;">margin: 10mm</p>
+ <p id="margin_pt" style="margin: 30pt;">margin: 30pt</p>
+ <p id="margin_pc" style="margin: 2pc;">margin: 2pc</p>
+ <p id="margin_px" style="margin: 5px;">margin: 5px</p>
+ <p id="margin_percent" style="margin: 10%;">margin: 10%</p>
+ <p id="margin_auto" style="margin: auto;">margin: auto</p>
+ <p id="margin_inherit" style="margin: inherit;">margin: inherit</p>
+
+ <p id="margin-left" style="margin-left: 11px;">margin-left: 11px</p>
+ <p id="margin-right" style="margin-right: 21px;">margin-right</p>
+ <p id="margin-top" style="margin-top: 31px;">margin-top: 31px</p>
+ <p id="margin-bottom" style="margin-bottom: 41px;">margin-bottom: 41px</p>
+
+ <span id="span" role="group">It's span</span>
+ <div id="div">It's div</div>
+ <p id="p">It's paragraph"</p>
+ <input id="input"/>
+ <table id="table" style="margin: 2px; text-align: center; text-indent: 10%;">
+ <tr id="tr" role="group">
+ <td id="td">td</td>
+ </tr>
+ </table>
+
+ <ul>
+ <li id="listitem">item
+ </ul>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/attributes/test_obj_css.xul b/accessible/tests/mochitest/attributes/test_obj_css.xul
new file mode 100644
index 000000000..a5e93571e
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj_css.xul
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility CSS-based Object Attributes Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../attributes.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // CSS display
+ testCSSAttrs("display_mozbox");
+ testCSSAttrs("display_mozinlinebox");
+ testCSSAttrs("display_mozgrid");
+ testCSSAttrs("display_mozinlinegrid");
+ testCSSAttrs("display_mozgridgroup");
+ testCSSAttrs("display_mozgridline");
+ testCSSAttrs("display_mozstack");
+ testCSSAttrs("display_mozinlinestack");
+ testCSSAttrs("display_mozdeck");
+ testCSSAttrs("display_mozpopup");
+ testCSSAttrs("display_mozgroupbox");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=714579"
+ title="Don't use GetComputedStyle for object attribute calculation">
+ Mozilla Bug 714579
+ </a><br/>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="display_mozbox" style="display: -moz-box;" role="img"/>
+ <vbox id="display_mozinlinebox" style="display: -moz-inline-box;" role="img"/>
+ <vbox id="display_mozgrid" style="display: -moz-grid;" role="img"/>
+ <vbox id="display_mozinlinegrid" style="display: -moz-inline-grid;" role="img"/>
+ <vbox id="display_mozgridgroup" style="display: -moz-grid-group;" role="img"/>
+ <vbox id="display_mozgridline" style="display: -moz-grid-line;" role="img"/>
+ <vbox id="display_mozstack" style="display: -moz-stack;" role="img"/>
+ <vbox id="display_mozinlinestack" style="display: -moz-inline-stack;" role="img"/>
+ <vbox id="display_mozdeck" style="display: -moz-deck;" role="img"/>
+ <vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/>
+ <vbox id="display_mozgroupbox" style="display: -moz-groupbox;" role="img"/>
+
+ </hbox>
+</window>
+
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 000000000..d5fe89471
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj_group.html
@@ -0,0 +1,469 @@
+<html>
+
+<head>
+ <title>Group attributes tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // HTML select with no size attribute.
+ testGroupAttrs("opt1-nosize", 1, 4);
+ testGroupAttrs("opt2-nosize", 2, 4);
+ testGroupAttrs("opt3-nosize", 3, 4);
+ testGroupAttrs("opt4-nosize", 4, 4);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML select
+ testGroupAttrs("opt1", 1, 2);
+ testGroupAttrs("opt2", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML select with options
+ // XXX bug 469123
+ //testGroupAttrs("select2_optgroup", 1, 3, 1);
+ //testGroupAttrs("select2_opt3", 2, 3, 1);
+ //testGroupAttrs("select2_opt4", 3, 3, 1);
+ //testGroupAttrs("select2_opt1", 1, 2, 2);
+ //testGroupAttrs("select2_opt2", 2, 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML input@type="radio" within form
+ testGroupAttrs("radio1", 1, 2);
+ testGroupAttrs("radio2", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML input@type="radio" within document
+ testGroupAttrs("radio3", 1, 2);
+ testGroupAttrs("radio4", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Hidden HTML input@type="radio"
+ testGroupAttrs("radio5", 1, 1);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML ul/ol
+ testGroupAttrs("li1", 1, 3);
+ testGroupAttrs("li2", 2, 3);
+ testGroupAttrs("li3", 3, 3);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML ul/ol (nested lists)
+
+ testGroupAttrs("li4", 1, 3, 1);
+ testGroupAttrs("li5", 2, 3, 1);
+ testGroupAttrs("li6", 3, 3, 1);
+
+ testGroupAttrs("n_li4", 1, 3, 2);
+ testGroupAttrs("n_li5", 2, 3, 2);
+ testGroupAttrs("n_li6", 3, 3, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA list
+ testGroupAttrs("li7", 1, 3);
+ testGroupAttrs("li8", 2, 3);
+ testGroupAttrs("li9", 3, 3);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA list (nested lists: list -> listitem -> list -> listitem)
+ testGroupAttrs("li10", 1, 3, 1);
+ testGroupAttrs("li11", 2, 3, 1);
+ testGroupAttrs("li12", 3, 3, 1);
+
+ testGroupAttrs("n_li10", 1, 3, 2);
+ testGroupAttrs("n_li11", 2, 3, 2);
+ testGroupAttrs("n_li12", 3, 3, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA list (nested lists: list -> listitem -> group -> listitem)
+ testGroupAttrs("lgt_li1", 1, 2, 1);
+ testGroupAttrs("lgt_li1_nli1", 1, 2, 2);
+ testGroupAttrs("lgt_li1_nli2", 2, 2, 2);
+ testGroupAttrs("lgt_li2", 2, 2, 1);
+ testGroupAttrs("lgt_li2_nli1", 1, 2, 2);
+ testGroupAttrs("lgt_li2_nli2", 2, 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA menu (menuitem, separator, menuitemradio and menuitemcheckbox)
+ testGroupAttrs("menu_item1", 1, 2);
+ testGroupAttrs("menu_item2", 2, 2);
+ testGroupAttrs("menu_item1.1", 1, 2);
+ testGroupAttrs("menu_item1.2", 2, 2);
+ testGroupAttrs("menu_item1.3", 1, 3);
+ testGroupAttrs("menu_item1.4", 2, 3);
+ testGroupAttrs("menu_item1.5", 3, 3);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA tab
+ testGroupAttrs("tab_1", 1, 3);
+ testGroupAttrs("tab_2", 2, 3);
+ testGroupAttrs("tab_3", 3, 3);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA radio
+ testGroupAttrs("r1", 1, 3);
+ testGroupAttrs("r2", 2, 3);
+ testGroupAttrs("r3", 3, 3);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA tree
+ testGroupAttrs("ti1", 1, 3, 1);
+ testGroupAttrs("ti2", 1, 2, 2);
+ testGroupAttrs("ti3", 2, 2, 2);
+ testGroupAttrs("ti4", 2, 3, 1);
+ testGroupAttrs("ti5", 1, 3, 2);
+ testGroupAttrs("ti6", 2, 3, 2);
+ testGroupAttrs("ti7", 3, 3, 2);
+ testGroupAttrs("ti8", 3, 3, 1);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA tree (tree -> treeitem -> group -> treeitem)
+ testGroupAttrs("tree2_ti1", 1, 2, 1);
+ testGroupAttrs("tree2_ti1a", 1, 2, 2);
+ testGroupAttrs("tree2_ti1b", 2, 2, 2);
+ testGroupAttrs("tree2_ti2", 2, 2, 1);
+ testGroupAttrs("tree2_ti2a", 1, 2, 2);
+ testGroupAttrs("tree2_ti2b", 2, 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA tree (tree -> treeitem, group -> treeitem)
+ testGroupAttrs("tree3_ti1", 1, 2, 1);
+ testGroupAttrs("tree3_ti1a", 1, 2, 2);
+ testGroupAttrs("tree3_ti1b", 2, 2, 2);
+ testGroupAttrs("tree3_ti2", 2, 2, 1);
+ testGroupAttrs("tree3_ti2a", 1, 2, 2);
+ testGroupAttrs("tree3_ti2b", 2, 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid
+ testGroupAttrs("grid_row1", 1, 2);
+ testAbsentAttrs("grid_cell1", {"posinset":"", "setsize":""});
+ testAbsentAttrs("grid_cell2", {"posinset":"", "setsize":""});
+
+ testGroupAttrs("grid_row2", 2, 2);
+ testAbsentAttrs("grid_cell3", {"posinset":"", "setsize":""});
+ testAbsentAttrs("grid_cell4", {"posinset":"", "setsize":""});
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA treegrid
+ testGroupAttrs("treegrid_row1", 1, 2, 1);
+ testAbsentAttrs("treegrid_cell1", {"posinset":"", "setsize":""});
+ testAbsentAttrs("treegrid_cell2", {"posinset":"", "setsize":""});
+
+ testGroupAttrs("treegrid_row2", 1, 1, 2);
+ testAbsentAttrs("treegrid_cell3", {"posinset":"", "setsize":""});
+ testAbsentAttrs("treegrid_cell4", {"posinset":"", "setsize":""});
+
+ testGroupAttrs("treegrid_row3", 2, 2, 1);
+ testAbsentAttrs("treegrid_cell5", {"posinset":"", "setsize":""});
+ testAbsentAttrs("treegrid_cell6", {"posinset":"", "setsize":""});
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML headings
+ testGroupAttrs("h1", 0, 0, 1);
+ testGroupAttrs("h2", 0, 0, 2);
+ testGroupAttrs("h3", 0, 0, 3);
+ testGroupAttrs("h4", 0, 0, 4);
+ testGroupAttrs("h5", 0, 0, 5);
+ testGroupAttrs("h6", 0, 0, 6);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA combobox
+ testGroupAttrs("combo1_opt1", 1, 4);
+ testGroupAttrs("combo1_opt2", 2, 4);
+ testGroupAttrs("combo1_opt3", 3, 4);
+ testGroupAttrs("combo1_opt4", 4, 4);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA table
+ testGroupAttrs("table_cell", 3, 4);
+ testGroupAttrs("table_row", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA list constructed by ARIA owns
+ testGroupAttrs("t1_li1", 1, 3);
+ testGroupAttrs("t1_li2", 2, 3);
+ testGroupAttrs("t1_li3", 3, 3);
+
+ // Test that group position information updates after deleting node.
+ testGroupAttrs("tree4_ti1", 1, 2, 1);
+ testGroupAttrs("tree4_ti2", 2, 2, 1);
+ var tree4element = document.getElementById("tree4_ti1");
+ var tree4acc = getAccessible("tree4");
+ tree4element.parentNode.removeChild(tree4element);
+ waitForEvent(EVENT_REORDER, tree4acc, function() {
+ testGroupAttrs("tree4_ti2", 1, 1, 1);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=468418"
+ title="Expose level for nested lists in HTML">
+ Mozilla Bug 468418
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=844023"
+ title="group info might not be properly updated when flat trees mutate">
+ Bug 844023
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=864224"
+ title="Support nested ARIA listitems structured by role='group'">
+ Bug 864224
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=907682"
+ title=" HTML:option group position is not correct when select is collapsed">
+ Mozilla Bug 907682
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select>
+ <option id="opt1-nosize">option1</option>
+ <option id="opt2-nosize">option2</option>
+ <option id="opt3-nosize">option3</option>
+ <option id="opt4-nosize">option4</option>
+ </select>
+
+ <select size="4">
+ <option id="opt1">option1</option>
+ <option id="opt2">option2</option>
+ </select>
+
+ <select size="4">
+ <optgroup id="select2_optgroup" label="group">
+ <option id="select2_opt1">option1</option>
+ <option id="select2_opt2">option2</option>
+ </optgroup>
+ <option id="select2_opt3">option3</option>
+ <option id="select2_opt4">option4</option>
+ </select>
+
+ <form>
+ <input type="radio" id="radio1" name="group1"/>
+ <input type="radio" id="radio2" name="group1"/>
+ </form>
+
+ <input type="radio" id="radio3" name="group2"/>
+ <input type="radio" id="radio4" name="group2"/>
+
+ <ul>
+ <li id="li1">Oranges</li>
+ <li id="li2">Apples</li>
+ <li id="li3">Bananas</li>
+ </ul>
+
+ <ol>
+ <li id="li4">Oranges</li>
+ <li id="li5">Apples</li>
+ <li id="li6">Bananas
+ <ul>
+ <li id="n_li4">Oranges</li>
+ <li id="n_li5">Apples</li>
+ <li id="n_li6">Bananas</li>
+ </ul>
+ </li>
+ </ol>
+
+ <span role="list">
+ <span role="listitem" id="li7">Oranges</span>
+ <span role="listitem" id="li8">Apples</span>
+ <span role="listitem" id="li9">Bananas</span>
+ </span>
+
+ <span role="list">
+ <span role="listitem" id="li10">Oranges</span>
+ <span role="listitem" id="li11">Apples</span>
+ <span role="listitem" id="li12">Bananas
+ <span role="list">
+ <span role="listitem" id="n_li10">Oranges</span>
+ <span role="listitem" id="n_li11">Apples</span>
+ <span role="listitem" id="n_li12">Bananas</span>
+ </span>
+ </span>
+ </span>
+
+ <div role="list">
+ <div role="listitem" id="lgt_li1">Item 1
+ <div role="group">
+ <div role="listitem" id="lgt_li1_nli1">Item 1A</div>
+ <div role="listitem" id="lgt_li1_nli2">Item 1B</div>
+ </div>
+ </div>
+ <div role="listitem" id="lgt_li2">Item 2
+ <div role="group">
+ <div role="listitem" id="lgt_li2_nli1">Item 2A</div>
+ <div role="listitem" id="lgt_li2_nli2">Item 2B</div>
+ </div>
+ </div>
+ </div>
+
+ <ul role="menubar">
+ <li role="menuitem" aria-haspopup="true" id="menu_item1">File
+ <ul role="menu">
+ <li role="menuitem" id="menu_item1.1">New</li>
+ <li role="menuitem" id="menu_item1.2">Open…</li>
+ <li role="separator">-----</li>
+ <li role="menuitem" id="menu_item1.3">Item</li>
+ <li role="menuitemradio" id="menu_item1.4">Radio</li>
+ <li role="menuitemcheckbox" id="menu_item1.5">Checkbox</li>
+ </ul>
+ </li>
+ <li role="menuitem" aria-haspopup="false" id="menu_item2">Help</li>
+ </ul>
+
+ <ul id="tablist_1" role="tablist">
+ <li id="tab_1" role="tab">Crust</li>
+ <li id="tab_2" role="tab">Veges</li>
+ <li id="tab_3" role="tab">Carnivore</li>
+ </ul>
+
+ <ul id="rg1" role="radiogroup">
+ <li id="r1" role="radio" aria-checked="false">Thai</li>
+ <li id="r2" role="radio" aria-checked="false">Subway</li>
+ <li id="r3" role="radio" aria-checked="false">Jimmy Johns</li>
+ </ul>
+
+ <table role="tree">
+ <tr role="presentation">
+ <td role="treeitem" aria-expanded="true" aria-level="1"
+ id="ti1">vegetables</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="2" id="ti2">cucumber</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="2" id="ti3">carrot</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-expanded="false" aria-level="1"
+ id="ti4">cars</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="2" id="ti5">mercedes</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="2" id="ti6">BMW</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="2" id="ti7">Audi</td>
+ </tr>
+ <tr role="presentation">
+ <td role="treeitem" aria-level="1" id="ti8">people</td>
+ </tr>
+ </table>
+
+ <ul role="tree">
+ <li role="treeitem" id="tree2_ti1">Item 1
+ <ul role="group">
+ <li role="treeitem" id="tree2_ti1a">Item 1A</li>
+ <li role="treeitem" id="tree2_ti1b">Item 1B</li>
+ </ul>
+ </li>
+ <li role="treeitem" id="tree2_ti2">Item 2
+ <ul role="group">
+ <li role="treeitem" id="tree2_ti2a">Item 2A</li>
+ <li role="treeitem" id="tree2_ti2b">Item 2B</li>
+ </ul>
+ </li>
+ </div>
+
+ <div role="tree">
+ <div role="treeitem" id="tree3_ti1">Item 1</div>
+ <div role="group">
+ <li role="treeitem" id="tree3_ti1a">Item 1A</li>
+ <li role="treeitem" id="tree3_ti1b">Item 1B</li>
+ </div>
+ <div role="treeitem" id="tree3_ti2">Item 2</div>
+ <div role="group">
+ <div role="treeitem" id="tree3_ti2a">Item 2A</div>
+ <div role="treeitem" id="tree3_ti2b">Item 2B</div>
+ </div>
+ </div>
+
+ <!-- IMPORTANT: Need to have no whitespace between elements in this tree. -->
+ <div role="tree" id="tree4"><div role="treeitem"
+ id="tree4_ti1">Item 1</div><div role="treeitem"
+ id="tree4_ti2">Item 2</div></div>
+
+ <table role="grid">
+ <tr role="row" id="grid_row1">
+ <td role="gridcell" id="grid_cell1">cell1</td>
+ <td role="gridcell" id="grid_cell2">cell2</td>
+ </tr>
+ <tr role="row" id="grid_row2">
+ <td role="gridcell" id="grid_cell3">cell3</td>
+ <td role="gridcell" id="grid_cell4">cell4</td>
+ </tr>
+ </table>
+
+ <div role="treegrid">
+ <div role="row" aria-level="1" id="treegrid_row1">
+ <div role="gridcell" id="treegrid_cell1">cell1</div>
+ <div role="gridcell" id="treegrid_cell2">cell2</div>
+ </div>
+ <div role="row" aria-level="2" id="treegrid_row2">
+ <div role="gridcell" id="treegrid_cell3">cell1</div>
+ <div role="gridcell" id="treegrid_cell4">cell2</div>
+ </div>
+ <div role="row" id="treegrid_row3">
+ <div role="gridcell" id="treegrid_cell5">cell1</div>
+ <div role="gridcell" id="treegrid_cell6">cell2</div>
+ </div>
+ </div>
+
+ <h1 id="h1">heading1</h1>
+ <h2 id="h2">heading2</h2>
+ <h3 id="h3">heading3</h3>
+ <h4 id="h4">heading4</h4>
+ <h5 id="h5">heading5</h5>
+ <h6 id="h6">heading6</h6>
+
+ <ul id="combo1" role="combobox">Password
+ <li id="combo1_opt1" role="option">Xyzzy</li>
+ <li id="combo1_opt2" role="option">Plughs</li>
+ <li id="combo1_opt3" role="option">Shazaam</li>
+ <li id="combo1_opt4" role="option">JoeSentMe</li>
+ </ul>
+
+ <form>
+ <input type="radio" style="display: none;" name="group3">
+ <input type="radio" id="radio5" name="group3">
+ </form>
+
+ <div role="table" aria-colcount="4" aria-rowcount="2">
+ <div role="row" id="table_row" aria-rowindex="2">
+ <div role="cell" id="table_cell" aria-colindex="3">cell</div>
+ </div>
+ </div>
+
+ <div role="list" aria-owns="t1_li1 t1_li2 t1_li3">
+ <div role="listitem" id="t1_li2">Apples</div>
+ <div role="listitem" id="t1_li1">Oranges</div>
+ </span>
+ <div role="listitem" id="t1_li3">Bananas</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/attributes/test_obj_group.xul b/accessible/tests/mochitest/attributes/test_obj_group.xul
new file mode 100644
index 000000000..c6943ae37
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj_group.xul
@@ -0,0 +1,216 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Group Attributes ('level', 'setsize', 'posinset') Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../attributes.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function openMenu(aID)
+ {
+ this.menuNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ testGroupAttrs("menu_item1.1", 1, 1);
+ testGroupAttrs("menu_item1.2", 1, 3);
+ testGroupAttrs("menu_item1.4", 2, 3);
+ testGroupAttrs("menu_item2", 3, 3);
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "open menu " + prettyName(aID);
+ }
+ }
+
+ function openSubMenu(aID)
+ {
+ this.menuNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openSubMenu_invoke()
+ {
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openSubMenu_finalCheck()
+ {
+ testGroupAttrs("menu_item2.1", 1, 2, 1);
+ testGroupAttrs("menu_item2.2", 2, 2, 1);
+ }
+
+ this.getID = function openSubMenu_getID()
+ {
+ return "open submenu " + prettyName(aID);
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // xul:listbox (bug 417317)
+ testGroupAttrs("listitem1", 1, 4);
+ testGroupAttrs("listitem2", 2, 4);
+ testGroupAttrs("listitem3", 3, 4);
+ testGroupAttrs("listitem4", 4, 4);
+
+ //////////////////////////////////////////////////////////////////////////
+ // xul:tab
+ testGroupAttrs("tab1", 1, 2);
+ testGroupAttrs("tab2", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // xul:radio
+ testGroupAttrs("radio1", 1, 2);
+ testGroupAttrs("radio2", 2, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // xul:menulist
+ testGroupAttrs("menulist1.1", 1);
+ testGroupAttrs("menulist1.2", 2);
+ testGroupAttrs("menulist1.3", 3);
+ testGroupAttrs("menulist1.4", 4);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA menu (bug 441888)
+ testGroupAttrs("aria-menuitem", 1, 3);
+ testGroupAttrs("aria-menuitemcheckbox", 2, 3);
+ testGroupAttrs("aria-menuitemradio", 3, 3);
+ testGroupAttrs("aria-menuitem2", 1, 1);
+
+ //////////////////////////////////////////////////////////////////////////
+ // xul:menu (bug 443881)
+ gQueue = new eventQueue();
+ gQueue.push(new openMenu("menu_item1"));
+ gQueue.push(new openSubMenu("menu_item2"));
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=417317"
+ title="Certain types of LISTITEM accessibles no longer get attributes set like 'x of y', regression from fix for bug 389926">
+ Mozilla Bug 417317
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=443881"
+ title="take into account separators in xul menus when group attributes are calculating">
+ Mozilla Bug 443881
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441888"
+ title="ARIA checked menu items are not included in the total list of menu items">
+ Mozilla Bug 441888
+ </a><br/>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <listbox>
+ <listitem label="listitem1" id="listitem1"/>
+ <listitem label="listitem2" id="listitem2" type="checkbox"/>
+ <listitem label="listitem3" id="listitem3" type="checkbox"/>
+ <listitem label="listitem4" id="listitem4"/>
+ </listbox>
+
+ <menubar>
+ <menu label="item1" id="menu_item1">
+ <menupopup>
+ <menuitem label="item1.1" id="menu_item1.1"/>
+ <menuseparator/>
+ <menuitem label="item1.2" id="menu_item1.2"/>
+ <menuitem label="item1.3" hidden="true"/>
+ <menuitem label="item1.4" id="menu_item1.4"/>
+ <menu label="item2" id="menu_item2">
+ <menupopup>
+ <menuitem label="item2.1" id="menu_item2.1"/>
+ <menuitem label="item2.2" id="menu_item2.2"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <tabbox>
+ <tabs>
+ <tab id="tab1" label="tab1"/>
+ <tab id="tab2" label="tab3"/>
+ </tabs>
+ <tabpanels>
+ <tabpanel/>
+ <tabpanel/>
+ </tabpanels>
+ </tabbox>
+
+ <radiogroup>
+ <radio id="radio1" label="radio1"/>
+ <radio id="radio2" label="radio2"/>
+ </radiogroup>
+
+ <menulist id="menulist1" label="Vehicle">
+ <menupopup>
+ <menuitem id="menulist1.1" label="Car"/>
+ <menuitem id="menulist1.2" label="Taxi"/>
+ <menuitem id="menulist1.3" label="Bus" selected="true"/>
+ <menuitem id="menulist1.4" label="Train"/>
+ </menupopup>
+ </menulist>
+
+ <vbox>
+ <description role="menuitem" id="aria-menuitem"
+ value="conventional menuitem"/>
+ <description role="menuitemcheckbox" id="aria-menuitemcheckbox"
+ value="conventional checkbox menuitem"/>
+ <description role="menuitem" hidden="true"/>
+ <description role="menuitemradio" id="aria-menuitemradio"
+ value="conventional radio menuitem"/>
+ <description role="separator"
+ value="conventional separator"/>
+ <description role="menuitem" id="aria-menuitem2"
+ value="conventional menuitem"/>
+ </vbox>
+
+ </vbox>
+ </hbox>
+</window>
+
diff --git a/accessible/tests/mochitest/attributes/test_obj_group_tree.xul b/accessible/tests/mochitest/attributes/test_obj_group_tree.xul
new file mode 100644
index 000000000..6b8461ef7
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_obj_group_tree.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree attributes tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../attributes.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ var treeNode = getNode("tree");
+
+ var tree = getAccessible(treeNode);
+ var treeitem1 = tree.firstChild.nextSibling;
+ testGroupAttrs(treeitem1, 1, 4, 1);
+
+ var treeitem2 = treeitem1.nextSibling;
+ testGroupAttrs(treeitem2, 2, 4, 1);
+
+ var treeitem3 = treeitem2.nextSibling;
+ testGroupAttrs(treeitem3, 1, 2, 2);
+
+ var treeitem4 = treeitem3.nextSibling;
+ testGroupAttrs(treeitem4, 2, 2, 2);
+
+ var treeitem5 = treeitem4.nextSibling;
+ testGroupAttrs(treeitem5, 3, 4, 1);
+
+ var treeitem6 = treeitem5.nextSibling;
+ testGroupAttrs(treeitem6, 4, 4, 1);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTreeTreeView());
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/attributes/test_tag.html b/accessible/tests/mochitest/attributes/test_tag.html
new file mode 100644
index 000000000..e43147b24
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_tag.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML landmark tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // And some AT may look for this
+ testAttrs("nav", {"tag" : "nav"}, true);
+ testAttrs("header", {"tag" : "header"}, true);
+ testAttrs("footer", {"tag" : "footer"}, true);
+ testAttrs("article", {"tag" : "article"}, true);
+ testAttrs("aside", {"tag" : "aside"}, true);
+ testAttrs("section", {"tag" : "section"}, true);
+ testAttrs("main", {"tag" : "article"}, true);
+ testAttrs("form", {"tag" : "article"}, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Provide mappings for html5 <nav> <header> <footer> <article>"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">
+ Bug 593368
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+ title="Map <article> like we do aria role article">
+ Bug 613502
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=610650"
+ title="Change implementation of HTML5 landmark elements to conform">
+ Bug 610650
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=614310"
+ title="Map section to pane (like role=region)">
+ Mozilla Bug 614310
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=734982"
+ title="Map ARIA role FORM">
+ Bug 734982
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <nav id="nav">a nav</nav>
+ <header id="header">a header</header>
+ <footer id="footer">a footer</footer>
+ <aside id="aside">by the way I am an aside</aside>
+ <section id="section">a section</section>
+
+ <article id="article">an article</article>
+ <article id="main" role="main">a main area</article>
+ <article id="form" role="form">a form area</article>
+
+</body>
+</html>
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 000000000..9b3d5aa5a
--- /dev/null
+++ b/accessible/tests/mochitest/attributes/test_xml-roles.html
@@ -0,0 +1,251 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>XML roles tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // Some AT may look for this
+ testAttrs("nav", {"xml-roles" : "navigation"}, true);
+ testAttrs("header", {"xml-roles" : "banner"}, true);
+ testAbsentAttrs("article_header", {"xml-roles" : "banner"});
+ testAbsentAttrs("section_header", {"xml-roles" : "banner"});
+ testAttrs("footer", {"xml-roles" : "contentinfo"}, true);
+ testAbsentAttrs("article_footer", {"xml-roles" : "contentinfo"});
+ testAbsentAttrs("section_footer", {"xml-roles" : "contentinfo"});
+ testAttrs("aside", {"xml-roles" : "complementary"}, true);
+ testAttrs("section", {"xml-roles" : "region"}, true);
+ testAttrs("main", {"xml-roles" : "main"}, true); // // ARIA override
+ testAttrs("form", {"xml-roles" : "form"}, true);
+ testAttrs("feed", {"xml-roles" : "feed"}, true);
+ testAttrs("article", {"xml-roles" : "article"}, true);
+ testAttrs("main_element", {"xml-roles" : "main"}, true);
+
+ testAttrs("search", {"xml-roles" : "searchbox"}, true);
+
+ testAttrs("open-1", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-2", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-3", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-4", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-5", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-6", {"xml-roles" : "open-fence"}, true);
+ testAttrs("open-7", {"xml-roles" : "open-fence"}, true);
+
+ testAttrs("sep-1", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-2", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-3", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-4", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-5", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-6", {"xml-roles" : "separator"}, true);
+ testAttrs("sep-7", {"xml-roles" : "separator"}, true);
+
+ testAttrs("close-1", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-2", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-3", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-4", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-5", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-6", {"xml-roles" : "close-fence"}, true);
+ testAttrs("close-7", {"xml-roles" : "close-fence"}, true);
+
+ testAttrs("num", {"xml-roles" : "numerator"}, true);
+ testAttrs("den", {"xml-roles" : "denominator"}, true);
+
+ testAttrs("sub-1", {"xml-roles" : "subscript"}, true);
+ testAttrs("sub-2", {"xml-roles" : "subscript"}, true);
+ testAttrs("sub-3", {"xml-roles" : "subscript"}, true);
+ testAttrs("sup-1", {"xml-roles" : "superscript"}, true);
+ testAttrs("sup-2", {"xml-roles" : "superscript"}, true);
+ testAttrs("sup-3", {"xml-roles" : "superscript"}, true);
+ testAttrs("sup-4", {"xml-roles" : "superscript"}, true);
+ testAttrs("presub-1", {"xml-roles" : "presubscript"}, true);
+ testAttrs("presub-2", {"xml-roles" : "presubscript"}, true);
+ testAttrs("presup-1", {"xml-roles" : "presuperscript"}, true);
+
+ testAttrs("under-1", {"xml-roles" : "underscript"}, true);
+ testAttrs("under-2", {"xml-roles" : "underscript"}, true);
+ testAttrs("over-1", {"xml-roles" : "overscript"}, true);
+ testAttrs("over-2", {"xml-roles" : "overscript"}, true);
+
+ testAttrs("root-index-1", {"xml-roles" : "root-index"}, true);
+
+ testAttrs("base-1", {"xml-roles" : "base"}, true);
+ testAttrs("base-2", {"xml-roles" : "base"}, true);
+ testAttrs("base-3", {"xml-roles" : "base"}, true);
+ testAttrs("base-4", {"xml-roles" : "base"}, true);
+ testAttrs("base-5", {"xml-roles" : "base"}, true);
+ testAttrs("base-6", {"xml-roles" : "base"}, true);
+ testAttrs("base-7", {"xml-roles" : "base"}, true);
+ testAttrs("base-8", {"xml-roles" : "base"}, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Provide mappings for html5 <nav> <header> <footer> <article>"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">
+ Bug 593368
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+ title="Map <article> like we do aria role article">
+ Bug 613502
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=610650"
+ title="Change implementation of HTML5 landmark elements to conform">
+ Bug 610650
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=614310"
+ title="Map section to pane (like role=region)">
+ Mozilla Bug 614310
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=734982"
+ title="Map ARIA role FORM">
+ Bug 734982
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761891"
+ title="HTML5 article element should expose xml-roles:article object attribute">
+ Bug 761891
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=849624"
+ title="modify HTML5 header and footer accessibility API mapping">
+ Bug 849624
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
+ title="ARIA 1.1: Support role 'searchbox'">
+ Bug 1121518
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <nav id="nav">a nav</nav>
+ <header id="header">a header</header>
+ <footer id="footer">a footer</footer>
+ <article id="article_with_header_and_footer">
+ <header id="article_header">a header within an article</header>
+ <footer id="article_footer">a footer within an article</footer>
+ </article>
+ <section id="section_with_header_and_footer">
+ <header id="section_header">a header within an section</header>
+ <footer id="section_footer">a footer within an section</footer>
+ </section>
+ <aside id="aside">by the way I am an aside</aside>
+ <section id="section">a section</section>
+ <article id="main" role="main">a main area</article>
+ <article id="form" role="form">a form area</article>
+ <div id="feed" role="feed">a feed</div>
+ <article id="article">article</article>
+ <main id="main_element">another main area</main>
+
+ <input id="search" type="search"/>
+
+ <!-- open-fence, separator, close-fence -->
+ <math><mo id="open-1">(</mo><mi>x</mi><mo id="sep-1">,</mo><mi>y</mi><mo id="close-1">)</mo></math>
+ <math><mrow><mo id="open-2">(</mo><mi>x</mi><mo id="sep-2">,</mo><mi>y</mi><mo id="close-2">)</mo></mrow></math>
+ <math><mstyle><mo id="open-3">(</mo><mi>x</mi><mo id="sep-3">,</mo><mi>y</mi><mo id="close-3">)</mo></mstyle></math>
+ <math><msqrt><mo id="open-4">(</mo><mi>x</mi><mo id="sep-4">,</mo><mi>y</mi><mo id="close-4">)</mo></msqrt></math>
+ <math><menclose><mo id="open-5">(</mo><mi>x</mi><mo id="sep-5">,</mo><mi>y</mi><mo id="close-5">)</mo></menclose></math>
+ <math><merror><mo id="open-6">(</mo><mi>x</mi><mo id="sep-6">,</mo><mi>y</mi><mo id="close-6">)</mo></merror></math>
+ <math><mtable><mtr><mtd><mo id="open-7">(</mo><mi>x</mi><mo id="sep-7">,</mo><mi>y</mi><mo id="close-7">)</mo></mtd></mtr></mtable></math>
+
+ <!-- numerator, denominator -->
+ <math>
+ <mfrac>
+ <mi id="num">a</mi>
+ <mi id="den">b</mi>
+ </mfrac>
+ </math>
+
+ <!-- subscript, superscript, presubscript, presuperscript -->
+ <math>
+ <msub>
+ <mi id="base-1">a</mi>
+ <mi id="sub-1">b</mi>
+ </msub>
+ </math>
+ <math>
+ <msup>
+ <mi id="base-2">a</mi>
+ <mi id="sup-1">b</mi>
+ </msup>
+ </math>
+ <math>
+ <msubsup>
+ <mi id="base-3">a</mi>
+ <mi id="sub-2">b</mi>
+ <mi id="sup-2">c</mi>
+ </msubsup>
+ </math>
+ <math>
+ <mmultiscripts>
+ <mi id="base-4">a</mi>
+ <mi id="sub-3">b</mi>
+ <mi id="sup-3">c</mi>
+ <none/>
+ <mi id="sup-4">d</mi>
+ <mprescripts/>
+ <mi id="presub-1">e</mi>
+ <none/>
+ <mi id="presub-2">f</mi>
+ <mi id="presup-1">g</mi>
+ </mmultiscripts>
+ </math>
+
+ <!-- underscript, overscript -->
+ <math>
+ <munder>
+ <mi id="base-5">a</mi>
+ <mi id="under-1">b</mi>
+ </munder>
+ </math>
+ <math>
+ <mover>
+ <mi id="base-6">a</mi>
+ <mi id="over-1">b</mi>
+ </mover>
+ </math>
+ <math>
+ <munderover>
+ <mi id="base-7">a</mi>
+ <mi id="under-2">b</mi>
+ <mi id="over-2">c</mi>
+ </munderover>
+ </math>
+
+ <!-- root-index -->
+ <math>
+ <mroot>
+ <mi id="base-8">a</mi>
+ <mi id="root-index-1">b</mi>
+ </mroot>
+ </math>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/autocomplete.js b/accessible/tests/mochitest/autocomplete.js
new file mode 100644
index 000000000..4de881b4d
--- /dev/null
+++ b/accessible/tests/mochitest/autocomplete.js
@@ -0,0 +1,221 @@
+
+const nsISupports = Components.interfaces.nsISupports;
+const nsIAutoCompleteResult = Components.interfaces.nsIAutoCompleteResult;
+const nsIAutoCompleteSearch = Components.interfaces.nsIAutoCompleteSearch;
+const nsIFactory = Components.interfaces.nsIFactory;
+const nsIUUIDGenerator = Components.interfaces.nsIUUIDGenerator;
+const nsIComponentRegistrar = Components.interfaces.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 = Components.classes["@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: function(aSearchString)
+ {
+ var values = [], comments = [];
+ for (var idx = 0; idx < this.values.length; idx++) {
+ if (this.values[idx].indexOf(aSearchString) != -1) {
+ 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: function(aSearchString, aSearchParam, aPreviousResult,
+ aListener)
+ {
+ var result = this.allResults.getAutoCompleteResultFor(aSearchString);
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid)
+ {
+ if (iid.equals(nsISupports) ||
+ iid.equals(nsIFactory) ||
+ iid.equals(nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(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: function(aIndex)
+ {
+ return this.values[aIndex];
+ },
+
+ getLabelAt: function(aIndex)
+ {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex)
+ {
+ return this.comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex)
+ {
+ return null;
+ },
+
+ getImageAt: function(aIndex)
+ {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex)
+ {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(nsISupports) ||
+ iid.equals(nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // 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 000000000..d60bd46a5
--- /dev/null
+++ b/accessible/tests/mochitest/bounds/a11y.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_list.html]
+[test_select.html]
+[test_zoom.html]
+[test_zoom_text.html]
diff --git a/accessible/tests/mochitest/bounds/test_list.html b/accessible/tests/mochitest/bounds/test_list.html
new file mode 100644
index 000000000..38a79689b
--- /dev/null
+++ b/accessible/tests/mochitest/bounds/test_list.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Accessible boundaries when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Inside list
+ var li = getAccessible("insidelist_item");
+ testBounds(li);
+
+ var [xLI, yLI, widthLI, heightLI] = getBounds(li);
+ var bullet = li.firstChild;
+ var [x, y, width, height] = getBounds(bullet);
+ is(x, xLI,
+ "Bullet x should match to list item x");
+ ok(y >= yLI,
+ "Bullet y= " + y + " should be not less than list item y=" + yLI);
+ ok(width < widthLI,
+ "Bullet width should be lesser list item width");
+ ok(height <= heightLI,
+ "Bullet height= " + height + " should be not greater than list item height=" + heightLI);
+
+ // Outside list
+ li = getAccessible("outsidelist_item");
+ var [xLIElm, yLIElm, widthLIElm, heightLIElm] = getBoundsForDOMElm(li);
+ [xLI, yLI, widthLI, heightLI] = getBounds(li);
+
+ ok(xLI < xLIElm,
+ "Outside list item x=" + xLI + " should be lesser than list item element x=" + xLIElm);
+ is(yLI, yLIElm,
+ "Outside list item y should match to list item element y");
+ ok(widthLI > widthLIElm,
+ "Outside list item width=" + widthLI + " should be greater than list item element width=" + widthLIElm);
+ is(heightLI, heightLIElm,
+ "Outside list item height should match to list item element height");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754627"
+ title="GetBounds on bullet return wrong values">
+ Mozilla Bug 754627
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ul style="list-style-position: inside;">
+ <li id="insidelist_item">item</li>
+ </ul>
+
+ <ul style="list-style-position: outside;">
+ <li id="outsidelist_item">item</li>
+ </ul>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/bounds/test_select.html b/accessible/tests/mochitest/bounds/test_select.html
new file mode 100644
index 000000000..395dad1b6
--- /dev/null
+++ b/accessible/tests/mochitest/bounds/test_select.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Accessible boundaries when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function openComboboxNCheckBounds(aID)
+ {
+ this.combobox = getAccessible(aID);
+ this.comboboxList = this.combobox.firstChild;
+ this.comboboxOption = this.comboboxList.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.comboboxOption)
+ ];
+
+ this.invoke = function openComboboxNCheckBounds_invoke()
+ {
+ getNode(aID).focus();
+ synthesizeKey("VK_DOWN", { altKey: true });
+ }
+
+ this.finalCheck = function openComboboxNCheckBounds_invoke()
+ {
+ testBounds(this.comboboxOption);
+ }
+
+ this.getID = function openComboboxNCheckBounds_getID()
+ {
+ return "open combobox and test boundaries";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ // Combobox
+ testBounds("combobox");
+
+ // Option boundaries matches to combobox boundaries when collapsed.
+ var selectBounds = getBoundsForDOMElm("combobox");
+ testBounds("option1", selectBounds);
+
+ // Open combobox and test option boundaries.
+ gQueue = new eventQueue();
+ gQueue.push(new openComboboxNCheckBounds("combobox"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="combobox">
+ <option id="option1">item1</option>
+ <option>item2</option>
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/bounds/test_zoom.html b/accessible/tests/mochitest/bounds/test_zoom.html
new file mode 100644
index 000000000..fc2dee482
--- /dev/null
+++ b/accessible/tests/mochitest/bounds/test_zoom.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Accessible boundaries when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose");
+ function doPreTest()
+ {
+ var tabDocument = currentTabDocument();
+ var imgMap = tabDocument.getElementById("imgmap");
+ waitForImageMap(imgMap, doTest);
+ }
+
+ function doTest()
+ {
+ // Bug 746176: Failure of this whole test file on OS X.
+ if (MAC) {
+ todo(false, "Fix bug 746176 on Mac");
+ closeBrowserWindow();
+ SimpleTest.finish();
+ return;
+ }
+
+ var tabDocument = currentTabDocument();
+ var p1 = tabDocument.getElementById("p1");
+ var p2 = tabDocument.getElementById("p2");
+
+ var imgMap = tabDocument.getElementById("imgmap");
+ var imgMapAcc = getAccessible(imgMap);
+ var area = imgMapAcc.firstChild;
+
+ testBounds(p1);
+ testBounds(p2);
+ testBounds(area);
+
+ zoomDocument(tabDocument, 2.0);
+
+ testBounds(p1);
+ testBounds(p2);
+ testBounds(area);
+
+ closeBrowserWindow();
+ SimpleTest.finish();
+ }
+
+ var url = "data:text/html,<html><body>";
+ url += "<p id='p1'>para 1</p><p id='p2'>para 2</p>";
+ url += "<map name='atoz_map' id='map'>";
+ url += " <area id='area1' href='http%3A%2F%2Fmozilla.org'";
+ url += " coords=17,0,30,14' alt='mozilla.org' shape='rect'>";
+ url += "</map>";
+ url += "<img id='imgmap' width='447' height='15'";
+ url += " usemap='%23atoz_map'";
+ url += " src='chrome%3A%2F%2Fmochitests%2Fcontent%2Fa11y%2Faccessible%2Fletters.gif'>";
+ url += "</body></html>";
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doPreTest,
+ url,
+ { left: 0, top: 0, width: 600, height: 600 });
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=650241"
+ title="Location returned by accessibles incorrect when page zoomed">
+ Mozilla Bug 650241
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/bounds/test_zoom_text.html b/accessible/tests/mochitest/bounds/test_zoom_text.html
new file mode 100644
index 000000000..1637f344e
--- /dev/null
+++ b/accessible/tests/mochitest/bounds/test_zoom_text.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>The text range boundary when page is zoomed</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ function testTextNode(aDoc, aContainerID)
+ {
+ var hyperTextNode = aDoc.getElementById(aContainerID);
+ var textNode = hyperTextNode.firstChild;
+
+ var [x, y, width, height] = getBounds(textNode);
+ testTextBounds(hyperTextNode, 0, -1, [x, y, width, height],
+ COORDTYPE_SCREEN_RELATIVE);
+ }
+
+ function doTest()
+ {
+ var tabDocument = currentTabDocument();
+ testTextNode(tabDocument, "p1");
+ testTextNode(tabDocument, "p2");
+
+ zoomDocument(tabDocument, 2.0);
+
+ testTextNode(tabDocument, "p1");
+
+ zoomDocument(tabDocument, 1.0);
+
+ closeBrowserWindow();
+ SimpleTest.finish();
+ }
+
+ var url = "data:text/html,<html>" +
+ "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>" +
+ "</meta><body>" +
+ "<p id='p1' style='font-family: monospace;'>Tilimilitryamdiya</p>" +
+ "<p id='p2'>ل</p>"
+ "</body></html>";
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest,
+ url,
+ { left: 0, top: 0, width: 600, height: 600 });
+
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727942"
+ title="Text range boundaries are incorrect when page is zoomed">
+ Mozilla Bug 727942
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/browser.js b/accessible/tests/mochitest/browser.js
new file mode 100644
index 000000000..f84f545d8
--- /dev/null
+++ b/accessible/tests/mochitest/browser.js
@@ -0,0 +1,153 @@
+/**
+ * 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
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var gBrowserContext =
+{
+ browserWnd: null,
+ testFunc: null,
+ startURL: ""
+};
+
+function openBrowserWindowIntl()
+{
+ var params = "chrome,all,dialog=no";
+ 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.openDialog(Services.prefs.getCharPref("browser.chromeURL"),
+ "_blank", params,
+ gBrowserContext.startURL);
+
+ whenDelayedStartupFinished(browserWindow(), function () {
+ addA11yLoadEvent(startBrowserTests, browserWindow());
+ });
+}
+
+function startBrowserTests()
+{
+ if (gBrowserContext.startURL) // 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", false);
+}
diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js
new file mode 100644
index 000000000..1e48fa067
--- /dev/null
+++ b/accessible/tests/mochitest/common.js
@@ -0,0 +1,952 @@
+////////////////////////////////////////////////////////////////////////////////
+// Interfaces
+
+const nsIAccessibilityService = Components.interfaces.nsIAccessibilityService;
+
+const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
+const nsIAccessibleStateChangeEvent =
+ Components.interfaces.nsIAccessibleStateChangeEvent;
+const nsIAccessibleCaretMoveEvent =
+ Components.interfaces.nsIAccessibleCaretMoveEvent;
+const nsIAccessibleTextChangeEvent =
+ Components.interfaces.nsIAccessibleTextChangeEvent;
+const nsIAccessibleVirtualCursorChangeEvent =
+ Components.interfaces.nsIAccessibleVirtualCursorChangeEvent;
+const nsIAccessibleObjectAttributeChangedEvent =
+ Components.interfaces.nsIAccessibleObjectAttributeChangedEvent;
+
+const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
+const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
+const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType;
+const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType;
+
+const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation;
+const nsIAccessibleTextRange = Components.interfaces.nsIAccessibleTextRange;
+
+const nsIAccessible = Components.interfaces.nsIAccessible;
+
+const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
+const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
+
+const nsIAccessibleText = Components.interfaces.nsIAccessibleText;
+const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText;
+
+const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
+const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
+
+const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
+const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot;
+const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable;
+const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
+const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
+const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule;
+const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue;
+
+const nsIObserverService = Components.interfaces.nsIObserverService;
+
+const nsIDOMDocument = Components.interfaces.nsIDOMDocument;
+const nsIDOMEvent = Components.interfaces.nsIDOMEvent;
+const nsIDOMHTMLDocument = Components.interfaces.nsIDOMHTMLDocument;
+const nsIDOMNode = Components.interfaces.nsIDOMNode;
+const nsIDOMHTMLElement = Components.interfaces.nsIDOMHTMLElement;
+const nsIDOMWindow = Components.interfaces.nsIDOMWindow;
+const nsIDOMXULElement = Components.interfaces.nsIDOMXULElement;
+
+const nsIPropertyElement = Components.interfaces.nsIPropertyElement;
+
+////////////////////////////////////////////////////////////////////////////////
+// OS detect
+
+const MAC = (navigator.platform.indexOf("Mac") != -1);
+const LINUX = (navigator.platform.indexOf("Linux") != -1);
+const SOLARIS = (navigator.platform.indexOf("SunOS") != -1);
+const WIN = (navigator.platform.indexOf("Win") != -1);
+
+////////////////////////////////////////////////////////////////////////////////
+// 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.
+ */
+Components.utils.import('resource://gre/modules/Services.jsm');
+
+/**
+ * nsIAccessibilityService service.
+ */
+var gAccService = Components.classes["@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)
+ return waitForDocLoad();
+
+ window.setTimeout(aFunc, 0);
+ },
+ 0
+ );
+ }
+
+ 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) + "'");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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 (aAccOrNodeOrID instanceof nsIDOMNode)
+ 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 (aAccOrElmOrID instanceof nsIDOMNode) {
+ 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) ?
+ true : false;
+}
+
+/**
+ * Return an accessible that contains the DOM node for the given identifier.
+ */
+function getContainerAccessible(aAccOrElmOrID)
+{
+ var node = getNode(aAccOrElmOrID);
+ if (!node)
+ return null;
+
+ 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
+ */
+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 (var 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) {
+ var attrs = accTree[prop][prevOffset];
+ testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, +offset, true);
+ }
+ prevOffset = +offset;
+ }
+
+ if (prevOffset != -1) {
+ var charCount = getAccessible(acc, [nsIAccessibleText]).characterCount;
+ var 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 (var 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 (var i = 0; i < childCount; i++) {
+ var 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++) {
+ var 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) ? true : false;
+}
+
+/**
+ * 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 == Components.results.NS_ERROR_FAILURE)
+ }
+ ok(success, "firstChild" + msg);
+
+ // lastChild
+ success = false;
+ try {
+ aAcc.lastChild;
+ } catch (e) {
+ success = (e.result == Components.results.NS_ERROR_FAILURE)
+ }
+ ok(success, "lastChild" + msg);
+
+ // childCount
+ success = false;
+ try {
+ aAcc.childCount;
+ } catch (e) {
+ success = (e.result == Components.results.NS_ERROR_FAILURE)
+ }
+ ok(success, "childCount" + msg);
+
+ // children
+ success = false;
+ try {
+ aAcc.children;
+ } catch (e) {
+ success = (e.result == Components.results.NS_ERROR_FAILURE)
+ }
+ ok(success, "children" + msg);
+
+ // nextSibling
+ success = false;
+ try {
+ aAcc.nextSibling;
+ } catch (e) {
+ success = (e.result == Components.results.NS_ERROR_FAILURE);
+ }
+ ok(success, "nextSibling" + msg);
+
+ // previousSibling
+ success = false;
+ try {
+ aAcc.previousSibling;
+ } catch (e) {
+ success = (e.result == Components.results.NS_ERROR_FAILURE);
+ }
+ ok(success, "previousSibling" + msg);
+
+ // parent
+ success = false;
+ try {
+ aAcc.parent;
+ } catch (e) {
+ success = (e.result == Components.results.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() {
+ const Ci = Components.interfaces;
+ return window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsILoadContext);
+}
+
+/**
+ * Return text from clipboard.
+ */
+function getTextFromClipboard()
+{
+ var clip = Components.classes["@mozilla.org/widget/clipboard;1"].
+ getService(Components.interfaces.nsIClipboard);
+ if (!clip)
+ return "";
+
+ var trans = Components.classes["@mozilla.org/widget/transferable;1"].
+ createInstance(Components.interfaces.nsITransferable);
+ trans.init(getLoadContext());
+ if (!trans)
+ return "";
+
+ trans.addDataFlavor("text/unicode");
+ clip.getData(trans, clip.kGlobalClipboard);
+
+ var str = new Object();
+ var strLength = new Object();
+ trans.getTransferData("text/unicode", str, strLength);
+
+ if (str)
+ str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
+ if (str)
+ return str.data.substring(0, strLength.value / 2);
+
+ 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 pretty name for identifier, it may be ID, DOM node or accessible.
+ */
+function prettyName(aIdentifier)
+{
+ if (aIdentifier instanceof Array) {
+ var 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);
+ var 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 (aIdentifier instanceof nsIDOMNode)
+ 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.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindow);
+}
+
+/** 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 == nsIDOMNode.DOCUMENT_NODE) {
+ tag = "document";
+ } else {
+ tag = aNode.localName;
+ if (aNode.nodeType == nsIDOMNode.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 000000000..15cb0ecb3
--- /dev/null
+++ b/accessible/tests/mochitest/dumbfile.zip
Binary files differ
diff --git a/accessible/tests/mochitest/editabletext/a11y.ini b/accessible/tests/mochitest/editabletext/a11y.ini
new file mode 100644
index 000000000..68466fdf2
--- /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 000000000..2fb1851bf
--- /dev/null
+++ b/accessible/tests/mochitest/editabletext/editabletext.js
@@ -0,0 +1,353 @@
+/**
+ * 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 = new Array();
+
+ 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)
+ {
+ // 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: Array.slice(arguments, 1)
+ };
+ 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(aID);
+ var removeTripple = oldValue ?
+ [ aSkipStartOffset, aSkipStartOffset + oldValue.length, oldValue ] : null;
+
+ this.generateTest(aID, removeTripple, insertTripple, setTextContentsInvoke,
+ getValueChecker(aID, 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(aID, null, [resPos, resPos + aStr.length, aStr],
+ insertTextInvoke, getValueChecker(aID, 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(aID, null, null, copyTextInvoke,
+ getClipboardChecker(aID, 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(aID, null, [aStartPos, aEndPos, getTextFromClipboard],
+ copyNPasteInvoke, getValueChecker(aID, 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(aID, [resStartPos, resEndPos, getTextFromClipboard], null,
+ cutTextInvoke, getValueChecker(aID, 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(aID, [aStartPos, aEndPos, getTextFromClipboard],
+ [aPos, -1, getTextFromClipboard],
+ cutNPasteTextInvoke, getValueChecker(aID, 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(aID, null, [aPos, -1, getTextFromClipboard],
+ pasteTextInvoke, getValueChecker(aID, aResStr), testID);
+ }
+
+ /**
+ * deleteText test.
+ */
+ this.deleteText = function deleteText(aStartPos, aEndPos, aResStr)
+ {
+ var testID = "deleteText from " + aStartPos + " to " + aEndPos +
+ " for " + prettyName(aID);
+
+ var oldValue = getValue(aID).substring(aStartPos, aEndPos);
+ var removeTripple = oldValue ? [aStartPos, aEndPos, oldValue] : null;
+
+ function deleteTextInvoke()
+ {
+ var acc = getAccessible(aID, [nsIAccessibleEditableText]);
+ acc.deleteText(aStartPos, aEndPos);
+ }
+
+ this.generateTest(aID, removeTripple, null, deleteTextInvoke,
+ getValueChecker(aID, aResStr), testID);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Implementation details.
+
+ function getValue(aID)
+ {
+ var value = "";
+ var elm = getNode(aID);
+ if (elm instanceof Components.interfaces.nsIDOMNSEditableElement)
+ return elm.value;
+
+ if (elm instanceof Components.interfaces.nsIDOMHTMLDocument)
+ return elm.body.textContent;
+
+ return elm.textContent;
+ }
+
+ /**
+ * Common checkers.
+ */
+ function getValueChecker(aID, aValue)
+ {
+ var checker = {
+ check: function valueChecker_check()
+ {
+ is(getValue(aID), aValue, "Wrong value " + aValue);
+ }
+ };
+ return checker;
+ }
+
+ function getClipboardChecker(aID, aText)
+ {
+ var checker = {
+ check: function clipboardChecker_check()
+ {
+ is(getTextFromClipboard(), aText, "Wrong text in clipboard.");
+ }
+ };
+ return checker;
+ }
+
+ function getValueNClipboardChecker(aID, aValue, aText)
+ {
+ var valueChecker = getValueChecker(aID, aValue);
+ var clipboardChecker = getClipboardChecker(aID, aText);
+
+ var checker = {
+ check: function()
+ {
+ valueChecker.check();
+ clipboardChecker.check();
+ }
+ };
+ 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(aID, 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) {
+ var checker = new textChangeChecker(aID, aRemoveTriple[0],
+ aRemoveTriple[1], aRemoveTriple[2],
+ false);
+ invoker.eventSeq.push(checker);
+ }
+
+ if (aInsertTriple) {
+ var 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 000000000..0c0b19696
--- /dev/null
+++ b/accessible/tests/mochitest/editabletext/test_1.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=452161
+-->
+<head>
+ <title>nsIAccessibleEditableText chrome tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="editabletext.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose"); // debug
+
+ function addTestEditable(aID, aTestRun, aBeforeContent, aAfterContent)
+ {
+ var et = new editableTextTest(aID);
+ var startOffset = aBeforeContent ? aBeforeContent.length : 0;
+ // XXX afterContent currently is not used
+
+ //////////////////////////////////////////////////////////////////////////
+ // setTextContents
+ et.scheduleTest(et.setTextContents, "hello", startOffset);
+ et.scheduleTest(et.setTextContents, "olleh", startOffset);
+ et.scheduleTest(et.setTextContents, "", startOffset);
+
+ //////////////////////////////////////////////////////////////////////////
+ // insertText
+ et.scheduleTest(et.insertText, "hello", startOffset, "hello");
+ et.scheduleTest(et.insertText, "ma ", startOffset, "ma hello");
+ et.scheduleTest(et.insertText, "ma", startOffset + 2, "mama hello");
+ et.scheduleTest(et.insertText, " hello", startOffset + 10, "mama hello hello");
+
+ // XXX: bug 452584
+
+ //////////////////////////////////////////////////////////////////////////
+ // deleteText
+// et.deleteText(0, 5, "hello hello");
+// et.deleteText(5, 6, "hellohello");
+// et.deleteText(5, 10, "hello");
+// et.deleteText(0, 5, "");
+
+ //////////////////////////////////////////////////////////////////////////
+ // copyNPasteText
+// et.copyNPasteText(0, 0, 0, "");
+// et.insertText("hello", 0, "hello");
+// et.copyNPasteText(0, 1, 0, "hhello");
+// et.copyNPasteText(5, 6, 6, "hhelloo");
+// et.copyNPasteText(3, 4, 1, "hehelloo");
+
+ //////////////////////////////////////////////////////////////////////////
+// // cutNPasteText
+// et.cutNPasteText(0, 1, 1, "ehhelloo");
+// et.cutNPasteText(1, 2, 0, "hehelloo");
+// et.cutNPasteText(7, 8, 8, "hehelloo");
+
+ aTestRun.add(et);
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function runTest()
+ {
+ var testRun = new editableTextTestRun();
+
+ addTestEditable("input", testRun);
+ addTestEditable("div", testRun);
+ addTestEditable("divb", testRun, "pseudo element", "");
+ addTestEditable("diva", testRun, "", "pseudo element");
+ addTestEditable("divba", testRun, "before", "after");
+ addTestEditable(getNode("frame").contentDocument, testRun);
+
+ testRun.run(); // Will call SimpleTest.finish();
+ }
+
+ function doTest()
+ {
+ // Prepare tested elements.
+
+ // Design mode on/off triggers an editable state change event on
+ // the document accessible.
+ var frame = getNode("frame");
+ waitForEvent(EVENT_STATE_CHANGE, frame.contentDocument, runTest);
+ frame.contentDocument.designMode = "on";
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+ <style>
+ #divb::before,
+ #diva::after {
+ content: "pseudo element";
+ }
+ #divba::before {
+ content: "before";
+ }
+ #divba::after {
+ content: "after";
+ }
+ </style>
+</head>
+<body>
+
+ <a target="_blank"
+ title="nsIAccessibleEditableText chrome tests"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=452161">
+ Bug 452161
+ </a>
+ <a target="_blank"
+ title="Cache rendered text on a11y side"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660">
+ Bug 626660
+ </a>
+ <a target="_blank"
+ title="Pseudo element support test"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1105611">
+ Bug 1105611
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input"/>
+
+ <div id="div" contenteditable="true"></div>
+ <div id="divb" contenteditable="true"></div>
+ <div id="diva" contenteditable="true"></div>
+ <div id="divba" contenteditable="true"></div>
+
+ <iframe id="frame"/>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/editabletext/test_2.html b/accessible/tests/mochitest/editabletext/test_2.html
new file mode 100644
index 000000000..5d96ebf35
--- /dev/null
+++ b/accessible/tests/mochitest/editabletext/test_2.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleEditableText chrome tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="editabletext.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var et = new editableTextTest("input");
+
+ // 'ee' insertion/removal at 1 or 2 offset of 'hello'/'heeello' string
+ // reports 'ee' text was inserted/removed at 2 offset.
+ et.scheduleTest(et.insertText, "ee", 1, "heeello", 2);
+ et.scheduleTest(et.copyText, 1, 3, "ee");
+ et.scheduleTest(et.cutText, 1, 3, "hello", 2, 4);
+ et.scheduleTest(et.insertText, "ee", 2, "heeello", 2);
+ et.scheduleTest(et.cutText, 2, 4, "hello", 2, 4);
+
+ et.scheduleTest(et.deleteText, 1, 3, "hlo");
+ et.scheduleTest(et.pasteText, 1, "heelo");
+
+ var testRun = new editableTextTestRun();
+ testRun.add(et);
+ testRun.run(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115">
+ Mozilla Bug 524115
+ </a>
+ <a target="_blank"
+ title="Cache rendered text on a11y side"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660">
+ Mozilla Bug 626660
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input" value="hello"/>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/a11y.ini b/accessible/tests/mochitest/elm/a11y.ini
new file mode 100644
index 000000000..76fb4402b
--- /dev/null
+++ b/accessible/tests/mochitest/elm/a11y.ini
@@ -0,0 +1,16 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+ !/dom/media/test/bug461281.ogg
+
+[test_HTMLSpec.html]
+skip-if = buildapp == 'mulet'
+[test_figure.html]
+[test_listbox.xul]
+[test_MathMLSpec.html]
+[test_nsApplicationAcc.html]
+[test_plugin.html]
+skip-if = buildapp == 'mulet'
+[test_canvas.html]
+[test_shadowroot.html]
diff --git a/accessible/tests/mochitest/elm/test_HTMLSpec.html b/accessible/tests/mochitest/elm/test_HTMLSpec.html
new file mode 100644
index 000000000..f5f48ec56
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -0,0 +1,1671 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML a11y spec tests</title>
+ <link id="link" rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:a@href
+
+ var obj = {
+ role: ROLE_LINK,
+ states: STATE_LINKED,
+ actions: "jump",
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText, nsIAccessibleHyperLink ],
+ children: [ // all kids inherits linked state and jump action
+ {
+ role: ROLE_TEXT_LEAF,
+ states: STATE_LINKED,
+ actions: "jump"
+ }
+ ]
+ };
+ testElm("a_href", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:a no @href
+
+ obj = {
+ todo_role: ROLE_TEXT_CONTAINER,
+ absentStates: STATE_LINKED,
+ actions: null,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ absentStates: STATE_LINKED,
+ actions: null
+ }
+ ]
+ };
+ testElm("a_nohref", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:abbr contained by HTML:td
+
+ obj = {
+ role: ROLE_CELL,
+ attributes: { abbr: "WWW" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [
+ {
+ role: ROLE_TEXT,
+ children: [ { role: ROLE_TEXT_LEAF } ]
+ }
+ ]
+ };
+ testElm("td_abbr", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:address
+
+ obj = {
+ role: ROLE_TEXT_CONTAINER,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ };
+ testElm("address", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:area@href
+
+ obj = {
+ role: ROLE_LINK,
+ states: STATE_LINKED,
+ actions: "jump",
+ interfaces: [ nsIAccessibleHyperLink ],
+ children: []
+ };
+ testElm(getAccessible("imgmap").firstChild, obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:area no @href
+
+ obj = {
+ todo_role: "ROLE_SHAPE",
+ absentStates: STATE_LINKED,
+ children: []
+ };
+ testElm(getAccessible("imgmap").lastChild, obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:article
+ obj = {
+ role: ROLE_DOCUMENT,
+ states: STATE_READONLY,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ };
+ testElm("article", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:aside
+ obj = {
+ role: ROLE_NOTE,
+ attributes: { "xml-roles": "complementary" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("aside", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:audio
+ role: ROLE_GROUPING
+ };
+ testElm("audio", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:b contained by paragraph
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-weight": kBoldFontWeight }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:b text
+ ]
+ }
+ testElm("b_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:bdi contained by paragraph
+ role: ROLE_PARAGRAPH,
+ todo_textAttrs: {
+ 0: { },
+ 5: { "writing-mode": "rl" },
+ 8: { }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF }, // HTML:bdi text
+ { role: ROLE_TEXT_LEAF } // plain text
+ ]
+ }
+ testElm("bdi_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:bdo contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ todo_textAttrs: {
+ 0: { },
+ 6: { "writing-mode": "rl" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ ]
+ }
+ testElm("bdo_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:blockquote
+
+ obj = {
+ role: ROLE_SECTION,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [ { role: ROLE_PARAGRAPH } ]
+ };
+ testElm("blockquote", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:br contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ children: [ { role: ROLE_WHITESPACE } ]
+ };
+ testElm("br_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:button
+ role: ROLE_PUSHBUTTON,
+ absentStates: STATE_DEFAULT,
+ actions: "press",
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("button", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:button@type="submit" (default button)
+
+ obj = {
+ role: ROLE_PUSHBUTTON,
+ states: STATE_DEFAULT,
+ actions: "press"
+ };
+ testElm("button_default", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:canvas
+
+ obj = {
+ role: ROLE_CANVAS
+ };
+ testElm("canvas", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:caption under table
+
+ obj = {
+ role: ROLE_TABLE,
+ relations: {
+ RELATION_LABELLED_BY: "caption"
+ },
+ interfaces: nsIAccessibleTable,
+ children: [
+ {
+ role: ROLE_CAPTION,
+ relations: {
+ RELATION_LABEL_FOR: "table"
+ },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ },
+ { // td inside thead
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_COLUMNHEADER,
+ interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+ },
+ { role: ROLE_COLUMNHEADER }
+ ]
+ },
+ { // td inside tbody
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_ROWHEADER,
+ interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+ },
+ {
+ role: ROLE_CELL,
+ interfaces: [ nsIAccessibleTableCell, nsIAccessibleText, nsIAccessibleHyperText ]
+ }
+ ]
+ },
+ { // td inside tfoot
+ role: ROLE_ROW
+ }
+ ]
+ };
+ testElm("table", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:cite contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-style": "italic" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:cite text
+ ]
+ };
+ testElm("cite_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:code contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-family": kMonospaceFontFamily }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:code text
+ ]
+ };
+ testElm("code_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:col and HTML:colgroup under table
+
+ obj =
+ { TABLE : [
+ { ROW :[
+ { role: ROLE_CELL },
+ { role: ROLE_CELL },
+ { role: ROLE_CELL }
+ ] }
+ ] };
+ testElm("colNcolgroup_table", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:data contained by paragraph
+
+ obj =
+ { PARAGRAPH: [
+ { TEXT_LEAF: [] } // HTML:data text
+ ] };
+ testElm("data_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:datalist associated with input
+
+ todo(false, "datalist and summary tree hierarchy test missed");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:dd, HTML:dl, HTML:dd
+
+ obj = {
+ role: ROLE_DEFINITION_LIST,
+ states: STATE_READONLY,
+ children: [ // dl
+ {
+ role: ROLE_TERM,
+ states: STATE_READONLY,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [ // dt
+ { role: ROLE_TEXT_LEAF }
+ ]
+ },
+ {
+ role: ROLE_DEFINITION,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [ // dd
+ { role: ROLE_TEXT_LEAF }
+ ]
+ }
+ ]
+ };
+ testElm("dl", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:del contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-line-through-style": "solid" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:del text
+ ]
+ };
+ testElm("del_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:details with open state
+
+ obj = {
+ role: ROLE_DETAILS,
+ children: [
+ {
+ role: ROLE_SUMMARY,
+ states: STATE_EXPANDED,
+ actions: "collapse"
+ },
+ { role: ROLE_PARAGRAPH }
+ ]
+ };
+ testElm("details", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:details with closed (default) state
+
+ obj = {
+ role: ROLE_DETAILS,
+ children: [
+ {
+ role: ROLE_SUMMARY,
+ states: STATE_COLLAPSED,
+ actions: "expand"
+ }
+ ]
+ };
+ testElm("details_closed", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:dfn contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { "font-style": "italic" },
+ 12: { }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // HTML:dfn text
+ { role: ROLE_TEXT_LEAF } // plain text
+ ]
+ };
+ testElm("dfn_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:dialog
+
+ todo(isAccessible("dialog"), "dialog element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:div
+
+ obj = {
+ role: ROLE_SECTION,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [
+ { role: ROLE_TEXT_LEAF } // plain text
+ ]
+ };
+ testElm("div", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:em in a paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-style": "italic" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:em text
+ ]
+ };
+ testElm("em_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:embed (windowless and windowed plugins)
+
+ if (WIN) {
+ obj = {
+ role: ROLE_EMBEDDED_OBJECT,
+ states: STATE_UNAVAILABLE
+ };
+
+ testElm("embed_plugin_windowless", obj);
+
+ obj = {
+ role: ROLE_EMBEDDED_OBJECT,
+ absentStates: STATE_UNAVAILABLE
+ };
+ testElm("embed_plugin_windowed", obj);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:fieldset and HTML:legend
+
+ obj = {
+ role: ROLE_GROUPING,
+ relations: {
+ RELATION_LABELLED_BY: "legend"
+ },
+ children: [
+ {
+ role: ROLE_LABEL,
+ relations: {
+ RELATION_LABEL_FOR: "fieldset"
+ },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ },
+ {
+ role: ROLE_ENTRY
+ }
+ ]
+ };
+ testElm("fieldset", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:figure and HTML:figcaption
+
+ obj = {
+ role: ROLE_FIGURE,
+ attributes: { "xml-roles": "figure" },
+ relations: {
+ RELATION_LABELLED_BY: "figcaption"
+ },
+ children: [
+ { role: ROLE_GRAPHIC },
+ {
+ role: ROLE_CAPTION,
+ relations: {
+ RELATION_LABEL_FOR: "figure"
+ },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ }
+ ]
+ };
+ testElm("figure", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:footer
+
+ obj = {
+ role: ROLE_FOOTER,
+ attributes: { "xml-roles": "contentinfo" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("footer", obj);
+
+ obj = {
+ role: ROLE_FOOTER,
+ absentAttributes: { "xml-roles": "contentinfo" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("footer_in_article", obj);
+ testElm("footer_in_section", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:form
+
+ obj = {
+ role: ROLE_FORM
+ };
+ testElm("form", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // // HTML:frameset, HTML:frame and HTML:iframe
+
+ obj = {
+ INTERNAL_FRAME: [ { // HTML:iframe
+ DOCUMENT: [ {
+ INTERNAL_FRAME: [ { // HTML:frame
+ DOCUMENT: [ { role: ROLE_TEXT_LEAF} ]
+ } ]
+ } ]
+ } ]
+ };
+ testElm("frameset_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:h1, HTML:h2, HTML:h3, HTML:h4, HTML:h5, HTML:h6
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "1" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h1", obj);
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "2" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h2", obj);
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "3" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h3", obj);
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "4" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h4", obj);
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "5" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h5", obj);
+
+ obj = {
+ role: ROLE_HEADING,
+ attributes: { "level": "6" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("h6", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:header
+
+ obj = {
+ role: ROLE_HEADER,
+ attributes: { "xml-roles": "banner" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("header", obj);
+
+ obj = {
+ role: ROLE_HEADER,
+ absentAttributes: { "xml-roles": "banner" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("header_in_article", obj);
+ testElm("header_in_section", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:hr
+
+ obj = {
+ role: ROLE_SEPARATOR,
+ };
+ testElm("hr", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:i contained by paragraph
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-style": "italic" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:i text
+ ]
+ }
+ testElm("i_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:img
+
+ obj = {
+ role: ROLE_GRAPHIC,
+ interfaces: [ nsIAccessibleImage ]
+ };
+ testElm("img", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="button"
+
+ obj = {
+ role: ROLE_PUSHBUTTON,
+ absentStates: STATE_DEFAULT
+ };
+ testElm("input_button", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="checkbox"
+
+ obj = {
+ role: ROLE_CHECKBUTTON,
+ states: STATE_CHECKABLE,
+ absentStates: STATE_CHECKED,
+ actions: "check"
+ };
+ testElm("input_checkbox", obj);
+
+ obj = {
+ role: ROLE_CHECKBUTTON,
+ states: STATE_CHECKABLE | STATE_CHECKED,
+ actions: "uncheck"
+ };
+ testElm("input_checkbox_true", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="file"
+
+ obj = {
+ TEXT_CONTAINER: [
+ { role: ROLE_PUSHBUTTON },
+ { role: ROLE_LABEL }
+ ]
+ };
+ testElm("input_file", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="image"
+
+ obj = {
+ role: ROLE_PUSHBUTTON,
+ absentStates: STATE_DEFAULT,
+ actions: "press"
+ };
+ testElm("input_image", obj);
+ testElm("input_submit", obj);
+
+ obj = {
+ role: ROLE_PUSHBUTTON,
+ actions: "press",
+ states: STATE_DEFAULT
+ };
+ testElm("input_image_default", obj);
+ testElm("input_submit_default", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="number" and etc
+
+ obj = {
+ role: ROLE_SPINBUTTON,
+ interfaces: [ nsIAccessibleValue ],
+ children: [
+ {
+ role: ROLE_ENTRY,
+ extraStates: EXT_STATE_EDITABLE | EXT_STATE_SINGLE_LINE,
+ actions: "activate",
+ interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ],
+ children: [
+ { role: ROLE_TEXT_LEAF }
+ ]
+ },
+ {
+ role: ROLE_PUSHBUTTON,
+ actions: "press"
+ },
+ {
+ role: ROLE_PUSHBUTTON,
+ actions: "press"
+ }
+ ]
+ };
+ testElm("input_number", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="text" and etc
+
+ obj = {
+ role: ROLE_ENTRY,
+ extraStates: EXT_STATE_EDITABLE | EXT_STATE_SINGLE_LINE,
+ actions: "activate",
+ interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ],
+ children: [
+ { role: ROLE_TEXT_LEAF }
+ ]
+ };
+ testElm("input_email", obj);
+ testElm("input_search", obj);
+ testElm("input_tel", obj);
+ testElm("input_text", obj);
+ testElm("input_url", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="password"
+
+ obj = {
+ role: ROLE_PASSWORD_TEXT,
+ states: STATE_PROTECTED,
+ extraStates: EXT_STATE_EDITABLE,
+ actions: "activate",
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ };
+ testElm("input_password", obj);
+ ok(getAccessible("input_password").firstChild.name != "44",
+ "text leaf for password shouldn't have its real value as its name!");
+
+ obj = {
+ role: ROLE_PASSWORD_TEXT,
+ states: STATE_PROTECTED | STATE_READONLY,
+ actions: "activate",
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ };
+ testElm("input_password_readonly", obj);
+ ok(getAccessible("input_password_readonly").firstChild.name != "44",
+ "text leaf for password shouldn't have its real value as its name!");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="radio"
+
+ obj = {
+ role: ROLE_RADIOBUTTON,
+ states: STATE_CHECKABLE,
+ absentStates: STATE_CHECKED,
+ actions: "select"
+ };
+ testElm("input_radio", obj);
+
+ obj = {
+ role: ROLE_RADIOBUTTON,
+ states: STATE_CHECKABLE | STATE_CHECKED,
+ actions: "select"
+ };
+ testElm("input_radio_true", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="range"
+
+ obj = {
+ role: ROLE_SLIDER
+ };
+ testElm("input_range", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:input@type="reset"
+
+ obj = {
+ role: ROLE_PUSHBUTTON,
+ actions: "press",
+ absentStates: STATE_DEFAULT
+ };
+ testElm("input_reset", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:ins contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-underline-style": "solid" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:ins text
+ ]
+ };
+ testElm("ins_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:kbd contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-family": kMonospaceFontFamily }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:kbd text
+ ]
+ };
+ testElm("kbd_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:keygen
+
+ obj = {
+ role: ROLE_COMBOBOX,
+ states: STATE_COLLAPSED | STATE_HASPOPUP,
+ extraStates: EXT_STATE_EXPANDABLE,
+ actions: "open",
+ children: [
+ { COMBOBOX_LIST: [
+ { role: ROLE_COMBOBOX_OPTION }, // high grade
+ { role: ROLE_COMBOBOX_OPTION } // medium grade
+ ] }
+ ]
+ };
+ testElm("keygen", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:label
+
+ obj = {
+ role: ROLE_LABEL,
+ todo_relations: {
+ RELATION_LABEL_FOR: "label_input"
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ {
+ role: ROLE_ENTRY,
+ relations: {
+ RELATION_LABELLED_BY: "label"
+ }
+ }
+ ]
+ };
+ testElm("label", obj);
+
+ obj = {
+ role: ROLE_LABEL,
+ relations: {
+ RELATION_LABEL_FOR: "label_for_input"
+ }
+ };
+ testElm("label_for", obj);
+
+ obj = {
+ role: ROLE_ENTRY,
+ relations: {
+ RELATION_LABELLED_BY: "label_for"
+ }
+ };
+ testElm("label_for_input", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:ul, HTML:ol, HTML:li
+
+ obj = { // ul or ol
+ role: ROLE_LIST,
+ states: STATE_READONLY,
+ children: [
+ { // li
+ role: ROLE_LISTITEM,
+ states: STATE_READONLY,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ }
+ ]
+ };
+ testElm("ul", obj);
+ testElm("ol", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:link
+
+ ok(!isAccessible("link"), "link element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:main
+
+ obj = {
+ todo_role: ROLE_GROUPING,
+ attributes: { "xml-roles": "main" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("main", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:map
+
+ ok(!isAccessible("map_imagemap"),
+ "map element is not accessible if used as an image map");
+
+ obj = {
+ role: ROLE_TEXT_CONTAINER
+ };
+ testElm("map", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:mark contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "background-color": "rgb(255, 255, 0)" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:mark text
+ ]
+ };
+ testElm("mark_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:math
+
+ obj = {
+ role: ROLE_MATHML_MATH
+ };
+ testElm("math", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:menu
+
+ obj = {
+ todo_role: ROLE_MENUPOPUP
+ };
+ testElm("menu", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:meter
+
+ todo(isAccessible("meter"), "meter element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:nav
+
+ obj = {
+ role: ROLE_SECTION,
+ attributes: { "xml-roles": "navigation" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("nav", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:object (windowless and windowed plugins) and HTML:param
+
+ if (WIN) {
+ obj = {
+ role: ROLE_EMBEDDED_OBJECT,
+ states: STATE_UNAVAILABLE,
+ children: [ ] // no child for HTML:param
+ };
+ testElm("object_plugin_windowless", obj);
+
+ obj = {
+ role: ROLE_EMBEDDED_OBJECT,
+ absentStates: STATE_UNAVAILABLE
+ };
+ testElm("object_plugin_windowed", obj);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:select, HTML:optgroup and HTML:option
+
+ obj = { // HMTL:select@size > 1
+ role: ROLE_LISTBOX,
+ states: STATE_FOCUSABLE,
+ absentStates: STATE_MULTISELECTABLE,
+ interfaces: [ nsIAccessibleSelectable ],
+ children: [
+ { GROUPING: [ // HTML:optgroup
+ { role: ROLE_STATICTEXT },
+ { role: ROLE_OPTION }, // HTML:option
+ { role: ROLE_OPTION }
+ ] },
+ {
+ role: ROLE_OPTION,
+ states: STATE_FOCUSABLE,
+ actions: "select",
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ }
+ ]
+ };
+ testElm("select_listbox", obj);
+
+ obj = { // HTML:select@multiple
+ role: ROLE_LISTBOX,
+ states: STATE_FOCUSABLE | STATE_MULTISELECTABLE,
+ children: [
+ { role: ROLE_OPTION },
+ { role: ROLE_OPTION },
+ { role: ROLE_OPTION }
+ ]
+ };
+ testElm("select_listbox_multiselectable", obj);
+
+ obj = { // HTML:select
+ role: ROLE_COMBOBOX,
+ states: STATE_FOCUSABLE,
+ children: [
+ {
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ { role: ROLE_COMBOBOX_OPTION },
+ { role: ROLE_COMBOBOX_OPTION },
+ { role: ROLE_COMBOBOX_OPTION }
+ ]
+ }
+ ]
+ };
+ testElm("select_combobox", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:output
+
+ obj = {
+ role: ROLE_SECTION,
+ attributes: { "live": "polite" },
+ todo_relations: {
+ RELATION_CONTROLLED_BY: "output_input"
+ },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("output", obj);
+
+ obj = {
+ role: ROLE_ENTRY,
+ relations: {
+ RELATION_CONTROLLER_FOR: "output"
+ }
+ };
+ testElm("output_input", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:pre
+
+ obj = {
+ role: ROLE_TEXT_CONTAINER,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("pre", obj);
+
+ ///////////////////////////////////////////////////////////////////////////
+ // HTML:progress
+
+ obj = {
+ role: ROLE_PROGRESSBAR,
+ absentStates: STATE_MIXED,
+ interfaces: [ nsIAccessibleValue ]
+ };
+ testElm("progress", obj);
+
+ obj = {
+ role: ROLE_PROGRESSBAR,
+ states: STATE_MIXED
+ };
+ testElm("progress_indeterminate", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:q
+
+ obj = {
+ role: ROLE_TEXT,
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ],
+ children: [
+ { role: ROLE_STATICTEXT }, // left quote
+ { role: ROLE_TEXT_LEAF }, // quoted text
+ { role: ROLE_STATICTEXT } // right quote
+ ]
+ };
+ testElm("q", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:ruby
+
+ todo(isAccessible("ruby"), "ruby element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:s contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-line-through-style": "solid" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:i text
+ ]
+ };
+ testElm("s_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:samp contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:samp text
+ ]
+ };
+ testElm("samp_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:section
+
+ obj = {
+ role: ROLE_SECTION,
+ attributes: { "xml-roles": "region" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("section", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:small contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "font-size": "10pt" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:small text
+ ]
+ };
+ testElm("small_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:source
+
+ ok(!isAccessible("source"), "source element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:span
+
+ ok(!isAccessible("span"), "span element is not accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:strong contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:strong text
+ ]
+ };
+ testElm("strong_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:sub contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-position": "sub" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:sub text
+ ]
+ };
+ testElm("sub_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:sup contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-position": "super" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:sup text
+ ]
+ };
+ testElm("sup_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:svg
+
+ obj = {
+ todo_role: ROLE_GRAPHIC
+ };
+ testElm("svg", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:textarea
+
+ obj = {
+ role: ROLE_ENTRY,
+ extraStates: EXT_STATE_MULTI_LINE | EXT_STATE_EDITABLE,
+ actions: "activate",
+ interfaces: [ nsIAccessibleText, nsIAccessibleEditableText ]
+ };
+ testElm("textarea", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:time
+
+ obj = {
+ role: ROLE_TEXT,
+ attributes: { "xml-roles": "time", "datetime": "2001-05-15 19:00" },
+ interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
+ };
+ testElm("time", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:u contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ textAttrs: {
+ 0: { },
+ 6: { "text-underline-style" : "solid" }
+ },
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:u text
+ ]
+ };
+ testElm("u_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML:var contained by paragraph
+
+ obj = {
+ role: ROLE_PARAGRAPH,
+ children: [
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF }, // HTML:var text
+ { role: ROLE_TEXT_LEAF }, // plain text
+ { role: ROLE_TEXT_LEAF } // HTML:var text
+ ]
+ };
+ testElm("var_container", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ obj = { // HTML:video
+ role: ROLE_GROUPING
+ };
+ testElm("video", obj);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement figure and figcaption accessibility"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=658272">
+ Mozilla Bug 658272
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <a id="a_href" href="www.mozilla.com">mozilla site</a>
+ <a id="a_nohref">anchor</a>
+ <table>
+ <tr>
+ <td id="td_abbr"><abbr title="World Wide Web">WWW</abbr></td>
+ </tr>
+ </table>
+ <address id="address">
+ Mozilla Foundation<br>
+ 1981 Landings Drive<br>
+ Building K<br>
+ Mountain View, CA 94043-0801<br>
+ USA
+ </address>
+
+ <map name="atoz_map">
+ <area id="area_href"
+ href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ <area id="area_nohref"
+ coords="0,0,13,14" alt="a" shape="rect">
+ </map>
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="../letters.gif">
+
+ <article id="article">A document</article>
+ <audio id="audio" controls="true">
+ <source id="source" src="../bug461281.ogg" type="video/ogg">
+ </audio>
+
+ <aside id="aside">
+ <p>Some content related to an &lt;article&gt;</p>
+ </aside>
+
+ <p id="b_container">normal<b>bold</b></p>
+ <p id="bdi_container">User <bdi>إيان</bdi>: 90 points</p>
+ <p id="bdo_container"><bdo dir="rtl">This text will go right to left.</bdo></p>
+
+ <blockquote id="blockquote" cite="http://developer.mozilla.org">
+ <p>This is a quotation taken from the Mozilla Developer Center.</p>
+ </blockquote>
+
+ <!-- two BRs, one will be eaten -->
+ <p id="br_container"><br><br></p>
+
+ <button id="button">button</button>
+ <form>
+ <button id="button_default" type="submit">button</button>
+ </form>
+
+ <canvas id="canvas"></canvas>
+
+ <table id="table">
+ <caption id="caption">caption</caption>
+ <thead>
+ <tr>
+ <th>col1</th><th>col2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th>col1</th><td>cell2</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <td>cell5</td><td>cell6</td>
+ </tr>
+ </tfoot>
+ </table>
+
+ <p id="cite_container">normal<cite>cite</cite></p>
+ <p id="code_container">normal<code>code</code></p>
+
+ <table id="colNcolgroup_table">
+ <colgroup>
+ <col>
+ <col span="2">
+ </colgroup>
+ <tr>
+ <td>Lime</td>
+ <td>Lemon</td>
+ <td>Orange</td>
+ </tr>
+ </table>
+
+ <p id="data_container"><data value="8">Eight</data></p>
+
+ <datalist id="datalist">
+ <summary id="summary">details</summary>
+ <option>Paris</option>
+ <option>San Francisco</option>
+ </datalist>
+ <input id="autocomplete_datalist" list="datalist">
+
+ <dl id="dl">
+ <dt>item1</dt><dd>description</dd>
+ </dl>
+
+ <p id="del_container">normal<del>Removed</del></p>
+
+ <details id="details" open="open">
+ <summary>Information</summary>
+ <p>If your browser supports this element, it should allow you to expand and collapse these details.</p>
+ </details>
+
+ <details id="details_closed">
+ <summary>Information</summary>
+ <p>If your browser supports this element, it should allow you to expand and collapse these details.</p>
+ </details>
+
+ <p id="dfn_container"><dfn id="def-internet">The Internet</dfn> is a global
+ system of interconnected networks that use the Internet Protocol Suite (TCP/IP)
+ to serve billions of users worldwide.</p>
+
+ <dialog id="dialog" open="true">This is a dialog</dialog>
+
+ <div id="div">div</div>
+
+ <p id="em_container">normal<em>emphasis</em></p>
+
+ <embed id="embed_plugin_windowless" type="application/x-test"
+ width="300" height="300"></embed>
+ <embed id="embed_plugin_windowed" type="application/x-test" wmode="window"
+ width="300" height="300"></embed>
+
+ <fieldset id="fieldset">
+ <legend id="legend">legend</legend>
+ <input />
+ </fieldset>
+
+ <figure id="figure">
+ <img src="../moz.png" alt="An awesome picture">
+ <figcaption id="figcaption">Caption for the awesome picture</figcaption>
+ </figure>
+
+ <footer id="footer">Some copyright info</footer>
+ <article>
+ <footer id="footer_in_article">Some copyright info</footer>
+ </article>
+ <section>
+ <footer id="footer_in_section">Some copyright info</footer>
+ </section>
+
+ <form id="form"></form>
+
+ <iframe id="frameset_container"
+ src="data:text/html,<html><frameset><frame src='data:text/html,hi'></frame></frameset></html>">
+ </iframe>
+
+ <h1 id="h1">heading1</h1>
+ <h2 id="h2">heading2</h2>
+ <h3 id="h3">heading3</h3>
+ <h4 id="h4">heading4</h4>
+ <h5 id="h5">heading5</h5>
+ <h6 id="h6">heading6</h6>
+
+ <header id="header">A logo</header>
+ <article>
+ <header id="header_in_article">Not logo</header>
+ </article>
+ <section>
+ <header id="header_in_section">Not logo</header>
+ </section>
+
+ <hr id="hr">
+ <p id="i_container">normal<i>italic</i></p>
+ <img id="img" src="../moz.png">
+
+ <input id="input_button" type="button" value="Button">
+ <input id="input_checkbox" type="checkbox">
+ <input id="input_checkbox_true" type="checkbox" checked>
+ <input id="input_file" type="file">
+ <input id="input_image" type="image">
+ <form>
+ <input id="input_image_default" type="image">
+ </form>
+ <input id="input_submit" type="submit">
+ <form>
+ <input id="input_submit_default" type="submit">
+ </form>
+ <input id="input_number" type="number" value="44">
+ <input id="input_text" type="text" value="hi">
+ <input id="input_search" type="search" value="cats">
+ <input id="input_email" type="email" value="me@mozilla.com">
+ <input id="input_tel" type="tel" value="111.111.1111">
+ <input id="input_url" type="url" value="www.mozilla.com">
+ <input id="input_password" type="password" value="44">
+ <input id="input_password_readonly" type="password" value="44" readonly>
+ <input id="input_radio" type="radio">
+ <input id="input_radio_true" type="radio" checked>
+ <input id="input_range" type="range">
+ <form>
+ <input id="input_reset" type="reset">
+ </form>
+
+ <p id="ins_container">normal<ins>Inserted</ins></p>
+ <p id="kbd_container">normal<kbd>cmd</kbd></p>
+ <keygen id="keygen" name="RSA public key" challenge="123456789" keytype="RSA">
+
+ <label id="label">label<input id="label_input"></label>
+ <label id="label_for" for="label_for_input">label</label>
+ <input id="label_for_input">
+
+ <ul id="ul">
+ <li>item1</li>
+ </ul>
+ <ol id="ol">
+ <li>item1</li>
+ </ol>
+
+ <main id="main">main</main>
+
+ <map id="map_imagemap" name="atoz_map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,13,14" alt="a" shape="rect">
+ </map>
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="../letters.gif">
+
+ <map id="map" title="Navigation Bar" name="mapgroup">
+ <p>
+ [<a href="#how">Bypass navigation bar</a>]
+ [<a href="home.html">Home</a>]
+ </p>
+ </map>
+
+ <p id="mark_container">normal<mark>highlighted</mark></p>
+
+ <math id="math">
+ <mrow>
+ <mrow>
+ <msup>
+ <mi>a</mi>
+ <mn>2</mn>
+ </msup>
+ <mo>+</mo>
+ <msup>
+ <mi>b</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ <mo>=</mo>
+ <msup>
+ <mi>c</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ </math>
+
+ <menu id="menu" type="toolbar">
+ <li>
+ <menu label="File">
+ <button type="button" onclick="new()">New...</button>
+ <button type="button" onclick="save()">Save...</button>
+ </menu>
+ </li>
+ <li>
+ <menu label="Edit">
+ <button type="button" onclick="cut()">Cut...</button>
+ <button type="button" onclick="copy()">Copy...</button>
+ <button type="button" onclick="paste()">Paste...</button>
+ </menu>
+ </li>
+ </menu>
+
+ <meter id="meter" min="0" max="1000" low="300" high="700" value="200">200 Euro</meter>
+
+ <nav id="nav">
+ <ul>
+ <li><a href="#">Home</a></li>
+ <li><a href="#">About</a></li>
+ <li><a href="#">Contact</a></li>
+ </ul>
+ </nav>
+
+ <object id="object_plugin_windowless" type="application/x-test"
+ width="300" height="300">
+ <param name="foo" value="bar">
+ </object>
+ <object id="object_plugin_windowed" type="application/x-test" wmode="window"
+ width="300" height="300"></object>
+
+ <select id="select_listbox" size="4">
+ <optgroup label="Colors">
+ <option>Red</option>
+ <option>Blue</option>
+ </optgroup>
+ <option>Animal</option>
+ </select>
+
+ <select id="select_listbox_multiselectable" multiple>
+ <option>Red</option>
+ <option>Blue</option>
+ <option>Green</option>
+ </select>
+
+ <select id="select_combobox">
+ <option>Red</option>
+ <option>Blue</option>
+ <option>Green</option>
+ </select>
+
+ <input id="output_input">
+ <output id="output" for="output_input"></output>
+
+ <pre id="pre">pre</pre>
+
+ <progress id="progress" min="0" value="21" max="42"></progress>
+ <progress id="progress_indeterminate"></progress>
+
+ <q id="q" cite="http://en.wikipedia.org/wiki/Kenny_McCormick#Cultural_impact">
+ Oh my God, they killed Kenny!
+ </q>
+
+ <ruby id="ruby">
+ 漢 <rp>(</rp><rt>Kan</rt><rp>)</rp>
+ 字 <rp>(</rp><rt>ji</rt><rp>)</rp>
+ </ruby>
+
+ <p id="s_container">normal<s>striked</s></p>
+ <p id="samp_container">normal<samp>sample</samp></p>
+ <section id="section">section</section>
+ <p id="small_container">normal<small>small</small></p>
+ <span id="span"></span>
+ <p id="strong_container">normal<strong>strong</strong></p>
+ <p id="sub_container">normal<sub>sub</sub></p>
+ <p id="sup_container">normal<sup>sup</sup></p>
+
+ <svg id="svg"></svg>
+ <textarea id="textarea"></textarea>
+
+ <p>The concert took place on <time id="time" datetime="2001-05-15 19:00">May 15</time></p>
+ <p id="u_container">normal<u>underline</u></p>
+ <p id="var_container">An equation: <var>x</var> = <var>y</var></p>
+
+ <video id="video" controls="true">
+ <source id="source" src="../bug461281.ogg" type="video/ogg">
+ </video>
+
+</video>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_MathMLSpec.html b/accessible/tests/mochitest/elm/test_MathMLSpec.html
new file mode 100644
index 000000000..80f1e9e70
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_MathMLSpec.html
@@ -0,0 +1,620 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML a11y spec tests</title>
+ <link id="link" rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../actions.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // math
+
+ obj = {
+ role: ROLE_MATHML_MATH,
+ };
+ testElm("math", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mi
+
+ obj = {
+ role: ROLE_MATHML_IDENTIFIER,
+ };
+ testElm("mi", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mn
+
+ obj = {
+ role: ROLE_MATHML_NUMBER,
+ };
+ testElm("mn", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mo
+
+ obj = {
+ role: ROLE_MATHML_OPERATOR,
+ attributes: { accent: "true", largeop: "true" }
+ };
+ testElm("mo", obj);
+
+ obj = {
+ role: ROLE_MATHML_OPERATOR,
+ attributes: { fence: "true" }
+ };
+ testElm("mo_fence", obj);
+
+ obj = {
+ role: ROLE_MATHML_OPERATOR,
+ attributes: { separator: "true" }
+ };
+ testElm("mo_separator", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mtext
+
+ obj = {
+ role: ROLE_MATHML_TEXT,
+ };
+ testElm("mtext", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ms
+
+ obj = {
+ role: ROLE_MATHML_STRING_LITERAL,
+ };
+ testElm("ms", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mglyph
+
+ obj = {
+ role: ROLE_MATHML_GLYPH,
+ };
+ testElm("mglyph", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mrow
+
+ obj = {
+ role: ROLE_MATHML_ROW,
+ };
+ testElm("mrow", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mfrac
+
+ obj = {
+ role: ROLE_MATHML_FRACTION,
+ attributes: { bevelled: "true", linethickness: "thick" }
+ };
+ testElm("mfrac", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msqrt
+
+ obj = {
+ role: ROLE_MATHML_SQUARE_ROOT,
+ };
+ testElm("msqrt", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mroot
+
+ obj = {
+ role: ROLE_MATHML_ROOT,
+ relations: {
+ RELATION_NODE_PARENT_OF: ["mroot_index", "mroot_base"]
+ },
+ children: [
+ {
+ role: ROLE_MATHML_IDENTIFIER,
+ relations: { RELATION_NODE_CHILD_OF: "mroot" }
+ },
+ {
+ role: ROLE_MATHML_NUMBER,
+ relations: { RELATION_NODE_CHILD_OF: "mroot" }
+ }
+ ]
+ };
+ testElm("mroot", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mfenced
+
+ obj = {
+ role: ROLE_MATHML_FENCED,
+ attributes: { open: "]", close: "[", separators: "." }
+ };
+ testElm("mfenced", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // menclose
+
+ obj = {
+ role: ROLE_MATHML_ENCLOSED,
+ attributes: { notation: "circle" }
+ };
+ testElm("menclose", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mstyle, mpadded, mphantom
+
+ obj = {
+ role: ROLE_MATHML_STYLE,
+ };
+ testElm("mstyle", obj);
+
+ ok(!isAccessible("mpadded"), "mpadded should not have accessible");
+ ok(!isAccessible("mphantom"), "mphantom should not have accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // msub
+
+ obj = {
+ role: ROLE_MATHML_SUB,
+ };
+ testElm("msub", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msup
+
+ obj = {
+ role: ROLE_MATHML_SUP,
+ };
+ testElm("msup", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msubsup
+
+ obj = {
+ role: ROLE_MATHML_SUB_SUP,
+ };
+ testElm("msubsup", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // munder
+
+ obj = {
+ role: ROLE_MATHML_UNDER,
+ attributes: { accentunder: "true", align: "center" }
+ };
+ testElm("munder", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mover
+
+ obj = {
+ role: ROLE_MATHML_OVER,
+ attributes: { accent: "true", align: "center" }
+ };
+ testElm("mover", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // munderover
+
+ obj = {
+ role: ROLE_MATHML_UNDER_OVER,
+ attributes: { accent: "true", accentunder: "true", align: "center" },
+ };
+ testElm("munderover", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mmultiscripts
+
+ obj = {
+ role: ROLE_MATHML_MULTISCRIPTS,
+ };
+ testElm("mmultiscripts", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mtable
+
+ obj = {
+ role: ROLE_MATHML_TABLE,
+ attributes: { align: "center", columnlines: "solid", rowlines: "solid" }
+ };
+ testElm("mtable", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mlabeledtr
+
+ obj = {
+ role: ROLE_MATHML_LABELED_ROW,
+ };
+ testElm("mlabeledtr", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mtr
+
+ obj = {
+ role: ROLE_MATHML_TABLE_ROW,
+ };
+ testElm("mtr", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mtd
+
+ obj = {
+ role: ROLE_MATHML_CELL,
+ };
+ testElm("mtd", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // maction
+
+ obj = {
+ role: ROLE_MATHML_ACTION,
+ attributes: { actiontype: "toggle", selection: "1" }
+ };
+ testElm("maction", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // merror
+
+ obj = {
+ role: ROLE_MATHML_ERROR,
+ };
+ testElm("merror", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // semantics, annotation, annotation-xml
+ ok(!isAccessible("semantics"), "semantics should not have accessible");
+ ok(!isAccessible("annotation"), "annotation should not have accessible");
+ ok(!isAccessible("annotation-xml"), "annotation-xml should not have accessible");
+
+ //////////////////////////////////////////////////////////////////////////
+ // mstack
+
+ obj = {
+ role: ROLE_MATHML_STACK,
+ attributes: { align: "center" }
+ };
+ testElm("mstack", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mlongdiv
+
+ obj = {
+ role: ROLE_MATHML_LONG_DIVISION,
+ attributes: { longdivstyle: "stackedrightright" }
+ };
+ testElm("mlongdiv", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msgroup
+
+ obj = {
+ role: ROLE_MATHML_STACK_GROUP,
+ attributes: { position: "2", shift: "-1" }
+ };
+ testElm("msgroup", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msrow
+
+ obj = {
+ role: ROLE_MATHML_STACK_ROW,
+ attributes: { position: "1" }
+ };
+ testElm("msrow", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mscarries
+
+ obj = {
+ role: ROLE_MATHML_STACK_CARRIES,
+ attributes: { location: "nw", position: "1" }
+ };
+ testElm("mscarries", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // mscarry
+
+ obj = {
+ role: ROLE_MATHML_STACK_CARRY,
+ attributes: { crossout: "updiagonalstrike" }
+ };
+ testElm("mscarry", obj);
+
+ //////////////////////////////////////////////////////////////////////////
+ // msline
+
+ obj = {
+ role: ROLE_MATHML_STACK_LINE,
+ attributes: { position: "1" }
+ };
+ testElm("msline", obj);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement figure and figcaption accessibility"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=658272">
+ Mozilla Bug 658272
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <math id="math">
+ <mrow id="mrow">
+ <mrow>
+ <msup id="msup">
+ <mi id="mi">a</mi>
+ <mn id="mn">2</mn>
+ </msup>
+ <mo id="mo" accent="true" largeop="true">+</mo>
+ <msqrt id="msqrt">
+ <mn>2</mn>
+ </msqrt>
+ </mrow>
+ <mo>=</mo>
+ <msub id="msub">
+ <mi>c</mi>
+ <mn>2</mn>
+ </msub>
+ </mrow>
+ <mspace id="mspace" width="1em"/>
+ <mtext id="mtext">Arbitrary text</mtext>
+ <mspace width="1em"/>
+ <ms id="ms">InterpretedStringLiteral</ms>
+ <mi>
+ <mglyph id="mglyph" src="../letters.gif" alt="letters"/>
+ </mi>
+ <mfrac id="mfrac" bevelled="true" linethickness="thick">
+ <mi>x</mi>
+ <mn>2</mn>
+ </mfrac>
+ <mroot id="mroot">
+ <mi id="mroot_base">x</mi>
+ <mn id="mroot_index">5</mn>
+ </mroot>
+ <mspace width="1em"/>
+ <mfenced id="mfenced" close="[" open="]" separators=".">
+ <mrow>
+ <mi>x</mi>
+ <mi>y</mi>
+ </mrow>
+ </mfenced>
+ <mrow>
+ <mo id="mo_fence" fence="true">[</mo>
+ <mrow>
+ X
+ <mo id="mo_separator" separator="true">,</mo>
+ Y
+ </mrow>
+ <mo fence="true"> closing-fence </mo>
+ </mrow>
+ <mspace width="1em"/>
+ <menclose id="menclose" notation="circle">
+ <mi>a</mi>
+ <mo>+</mo>
+ <mi>b</mi>
+ </menclose>
+ <mstyle id="mstyle" dir="rtl" mathcolor="blue">
+ <mpadded id="mpadded" height="100px" width="200px">
+ <mi>x</mi>
+ <mphantom id="mphantom">
+ <mo>+</mo>
+ <mi>y</mi>
+ </mphantom>
+ </mpadded>
+ </mstyle>
+
+ <msubsup id="msubsup">
+ <mi>b</mi>
+ <mn>1</mn>
+ <mn>2</mn>
+ </msubsup>
+ <munder id="munder" accentunder="true" align="center">
+ <mrow>
+ <mi> x </mi>
+ <mo> + </mo>
+ <mi> y </mi>
+ <mo> + </mo>
+ <mi> z </mi>
+ </mrow>
+ <mo> &#x23DF;<!--BOTTOM CURLY BRACKET--> </mo>
+ </munder>
+ <mspace width="1em"/>
+ <mover id="mover" accent="true" align="center">
+ <mi> x </mi>
+ <mo> &#x5E;<!--CIRCUMFLEX ACCENT--> </mo>
+ </mover>
+ <munderover id="munderover" accentunder="true" accent="true" align="center">
+ <mo> &#x222B;<!--INTEGRAL--> </mo>
+ <mn> 0 </mn>
+ <mi> &#x221E;<!--INFINITY--> </mi>
+ </munderover>
+ <mmultiscripts id="mmultiscripts">
+ <mi> R </mi>
+ <mi> i </mi>
+ <none/>
+ <none/>
+ <mi> j </mi>
+ <mi> k </mi>
+ <none/>
+ <mi> l </mi>
+ <none/>
+ </mmultiscripts>
+
+ <mtable id="mtable" align="center" columnlines="solid" rowlines="solid">
+ <mlabeledtr id="mlabeledtr">
+ <mtd>
+ <mtext> (2.1) </mtext>
+ </mtd>
+ <mtd>
+ <mrow>
+ <mi>E</mi>
+ <mo>=</mo>
+ <mrow>
+ <mi>m</mi>
+ <mo>&#x2062;<!--INVISIBLE TIMES--></mo>
+ <msup>
+ <mi>c</mi>
+ <mn>2</mn>
+ </msup>
+ </mrow>
+ </mrow>
+ </mtd>
+ </mlabeledtr>
+ </mtable>
+ <mrow>
+ <mo> ( </mo>
+ <mtable>
+ <mtr id="mtr">
+ <mtd id="mtd"> <mn>1</mn> </mtd>
+ <mtd> <mn>0</mn> </mtd>
+ <mtd> <mn>0</mn> </mtd>
+ </mtr>
+ <mtr>
+ <mtd> <mn>0</mn> </mtd>
+ <mtd> <mn>1</mn> </mtd>
+ <mtd> <mn>0</mn> </mtd>
+ </mtr>
+ <mtr>
+ <mtd> <mn>0</mn> </mtd>
+ <mtd> <mn>0</mn> </mtd>
+ <mtd> <mn>1</mn> </mtd>
+ </mtr>
+ </mtable>
+ <mo> ) </mo>
+ </mrow>
+
+ <maction id="maction" actiontype="toggle" selection="1">
+ <mfrac>
+ <mn>6</mn>
+ <mn>8</mn>
+ </mfrac>
+ <mfrac>
+ <mrow>
+ <mn>3</mn>
+ <mo>⋅</mo>
+ <mn>2</mn>
+ </mrow>
+ <mrow>
+ <mn>4</mn>
+ <mo>⋅</mo>
+ <mn>2</mn>
+ </mrow>
+ </mfrac>
+ <mfrac>
+ <mn>3</mn>
+ <mn>4</mn>
+ </mfrac>
+ </maction>
+
+ <merror id="merror">
+ <mrow>
+ <mtext>Division by zero: </mtext>
+ <mfrac>
+ <mn>1</mn>
+ <mn>0</mn>
+ </mfrac>
+ </mrow>
+ </merror>
+
+ <semantics id="semantics">
+ <!-- Presentation MathML -->
+ <mrow>
+ <msup>
+ <mi>x</mi>
+ <mn>2</mn>
+ </msup>
+ <mo>+</mo>
+ <mi>y</mi>
+ </mrow>
+ <!-- Content MathML -->
+ <annotation-xml id="annotation-xml" encoding="MathML-Content">
+ <apply>
+ <plus/>
+ <apply>
+ <power/>
+ <ci>x</ci>
+ <cn type="integer">2</cn>
+ </apply>
+ <ci>y</ci>
+ </apply>
+ </annotation-xml>
+ <!-- annotate TeX -->
+ <annotation id="annotation" encoding="application/x-tex">
+ x^{2} + y
+ </annotation>
+ </semantics>
+
+ <mstack id="mstack" align="center">
+ <mscarries id="mscarries" location="nw" position="1">
+ <none/>
+ <mscarry id="mscarry" crossout="updiagonalstrike">
+ <mn>1</mn>
+ </mscarry>
+ <mscarry location="w">
+ <mn>1</mn>
+ </mscarry>
+ </mscarries>
+ <mn>523</mn>
+ <msrow id="msrow" position="1">
+ <mo>-</mo>
+ <none/>
+ <mn>15</mn>
+ </msrow>
+ <msline id="msline" position="1"/>
+ <mn>508</mn>
+ </mstack>
+ <mspace width="1em"/>
+ <mlongdiv id="mlongdiv" longdivstyle="stackedrightright">
+ <mn>5</mn>
+ <mn>1</mn>
+ <mn>5</mn>
+ </mlongdiv>
+
+ <mstack>
+ <msgroup id="msgroup" position="2" shift="-1">
+ <mn>123</mn>
+ <msrow><mo>&#xD7;<!--MULTIPLICATION SIGN--></mo><mn>321</mn></msrow>
+ </msgroup>
+ <msline/>
+ <msgroup shift="1">
+ <mn>123</mn>
+ <mn>246</mn>
+ <mn>369</mn>
+ </msgroup>
+ <msline/>
+ </mstack>
+ </math>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_canvas.html b/accessible/tests/mochitest/elm/test_canvas.html
new file mode 100644
index 000000000..b4b743800
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_canvas.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Accessible boundaries for hit regions</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ var kX = 10, kY = 10, kWidth = 150, kHeight = 100;
+ function doTest()
+ {
+ var canv = document.getElementById("c");
+ var context = canv.getContext('2d');
+ var element = document.getElementById("showA");
+ context.beginPath();
+ context.rect(kX, kY, kWidth, kHeight);
+ context.addHitRegion({control: element});
+
+ var input = getAccessible("showA");
+ var [cnvX, cnvY, cnvWidth, cnvHeight] = getBoundsForDOMElm(canv);
+ var [accX, accY, accWidth, accHeight] = getBounds(input);
+
+ var [x, y, w, h] = CSSToDevicePixels(window, kX, kY, kWidth, kHeight);
+ is(accX, cnvX + x, "wrong accX");
+ is(accY, cnvY + y, "wrong accY");
+ is(accWidth, w, "wrong accWidth");
+ is(accHeight, h, "wrong accHeight");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set": [['canvas.hitregions.enabled', true]]}, doTest);
+ });
+
+ </script>
+</head>
+<body>
+
+ <canvas id="c">
+ <input id="showA" type="checkbox"><label for="showA"> Show As </label>
+ </canvas>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_figure.html b/accessible/tests/mochitest/elm/test_figure.html
new file mode 100644
index 000000000..ba1bb489f
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_figure.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML5 figure/figcaption tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ testRole("figure", ROLE_FIGURE);
+ testRole("figcaption", ROLE_CAPTION);
+
+ todo(false, "figure name gets extra whitespace in the end!");
+ testName("figure", "figure caption ");
+ testName("figcaption", null);
+
+ testRelation("figure", RELATION_LABELLED_BY, "figcaption");
+ testRelation("figcaption", RELATION_LABEL_FOR, "figure");
+
+ testAttrs("figure", {"xml-roles" : "figure"}, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement figure and figcaption accessibility"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=658272">
+ Mozilla Bug 658272
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <figure id="figure">
+ <figcaption id="figcaption">figure caption</figcaption>
+ </figure>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_listbox.xul b/accessible/tests/mochitest/elm/test_listbox.xul
new file mode 100644
index 000000000..e284b3e5c
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_listbox.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL listbox element test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ var id = "";
+ var listbox = null, acc = null;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Simple listbox. There is no nsIAccessibleTable interface.
+
+ id = "listbox1";
+ acc = getAccessible(id);
+
+ // query nsIAccessibleTable
+ try {
+ acc.QueryInterface(nsIAccessibleTable);
+ ok(false,
+ id + " shouldn't implement nsIAccessibleTable interface.");
+ } catch(e) {
+ ok(true, id + " doesn't implement nsIAccessibleTable interface.");
+ }
+
+ // role
+ testRole(id, ROLE_LISTBOX);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=418371"
+ title="implement the rest of methods of nsIAccessibleTable on xul:listbox">
+ Mozilla Bug 418371
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <label control="listbox1" value="listbox: "/>
+ <listbox id="listbox1">
+ <listitem label="item1" id="item1"/>
+ <listitem label="item2" id="item2"/>
+ </listbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
new file mode 100644
index 000000000..58763e437
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -0,0 +1,75 @@
+<html>
+
+<head>
+ <title>application accessible name</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accessible = getApplicationAccessible();
+ if (!accessible) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var bundleServ =
+ Components.classes["@mozilla.org/intl/stringbundle;1"].
+ getService(Components.interfaces.nsIStringBundleService);
+ var brandBundle =
+ bundleServ.createBundle("chrome://branding/locale/brand.properties");
+
+ var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+ getService(Components.interfaces.nsIXULAppInfo);
+
+ // nsIAccessible::name
+ var applicationName = "";
+ if (LINUX || SOLARIS) {
+ applicationName = appInfo.name;
+ } else {
+ try {
+ applicationName = brandBundle.GetStringFromName("brandShortName");
+ } catch(e) {
+ }
+
+ if (applicationName == "")
+ applicationName = "Gecko based application";
+ }
+ is (accessible.name, applicationName, "wrong application accessible name");
+
+ // nsIAccessibleApplication
+ is(accessible.appName, appInfo.name, "Wrong application name");
+ is(accessible.appVersion, appInfo.version, "Wrong application version");
+ is(accessible.platformName, "Gecko", "Wrong platform name");
+ is(accessible.platformVersion, appInfo.platformVersion,
+ "Wrong platform version");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+ </head>
+ <body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=456121"
+ title="nsApplicationAccessible::GetName does not return a default value when brand.properties does not exist">
+ Mozilla Bug 454211
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_plugin.html b/accessible/tests/mochitest/elm/test_plugin.html
new file mode 100644
index 000000000..3350e6ccc
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_plugin.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Plugin tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ if (!WIN) {
+ ok(true,
+ "It's Windows specific test. Feel free to extend the test.");
+
+ SimpleTest.finish();
+ return;
+ }
+
+ testStates("plugin-windowless", STATE_UNAVAILABLE);
+ testAccessibleTree("plugin-windowless", { EMBEDDED_OBJECT: [ ] });
+
+ testStates("plugin-windowless-fallback", STATE_UNAVAILABLE);
+ testAccessibleTree("plugin-windowless-fallback", { EMBEDDED_OBJECT: [ ] });
+
+ testStates("plugin-windowed", 0, 0, STATE_UNAVAILABLE);
+ testAccessibleTree("plugin-windowed", { EMBEDDED_OBJECT: [ { NOTHING: [] } ] });
+
+ testStates("plugin-windowed-fallback", 0, 0, STATE_UNAVAILABLE);
+ testAccessibleTree("plugin-windowed-fallback",
+ { EMBEDDED_OBJECT: [ { NOTHING: [] } ] });
+
+ // make sure we handle content changes under the plugin.
+ getNode("fallback1").setAttribute("href", "5");
+ getNode("fallback2").setAttribute("href", "5");
+ SimpleTest.executeSoon(function () { SimpleTest.finish(); });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Embed and object HTML tags should be given an accessible role of embedded object"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=485270">Bug 485270</a>
+ <a target="_blank"
+ title="Embedded object accessibles for inaccessible/windowless plugins should not expose a NULL child"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=816856">Bug 816856</a>
+ <a target="_blank"
+ title="Updating accessible tree for plugin with fallback shouldn't crash"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=881636">Bug 881636</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <embed id="plugin-windowless" type="application/x-test"
+ width="300" height="300"></embed>
+ <embed id="plugin-windowed" type="application/x-test" wmode="window"
+ width="300" height="300"></embed>
+ <embed id="plugin-windowless-fallback" type="application/x-test"
+ width="300" height="300"><a id="fallback1">foo</a></embed>
+ <embed id="plugin-windowed-fallback" type="application/x-test" wmode="window"
+ width="300" height="300"><a id="fallback2">foo</a></embed>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/elm/test_shadowroot.html b/accessible/tests/mochitest/elm/test_shadowroot.html
new file mode 100644
index 000000000..e4c39b8d6
--- /dev/null
+++ b/accessible/tests/mochitest/elm/test_shadowroot.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>ShadowRoot tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ testElm("component", {
+ role: ROLE_GROUPING,
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ },
+ {
+ role: ROLE_LINK,
+ },
+ ]
+ });
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Ensure accessible objects are created for shadow root"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1026125">
+ Mozilla Bug 1026125
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="group" id="component"></div>
+ <script>
+ var component = document.getElementById('component');
+ var shadow = component.createShadowRoot();
+
+ shadow.innerHTML = '<button>Hello</button>' +
+ '<a href="#"> World</a>';
+ </script>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js
new file mode 100644
index 000000000..d1e5ec8a0
--- /dev/null
+++ b/accessible/tests/mochitest/events.js
@@ -0,0 +1,2329 @@
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+
+const EVENT_ALERT = nsIAccessibleEvent.EVENT_ALERT;
+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
+
+Components.utils.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 = "";
+
+/**
+ * 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 (target instanceof nsIDOMNode &&
+ 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.ownerDocument.defaultView);
+}
+
+/**
+ * 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)
+ return aTestFunc();
+
+ 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.
+ */
+ 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.
+ */
+ 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 isExppected = 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 = eventSeq[idx].target;
+ if (!target) {
+ ok(false, "no target for DOM event!");
+ return false;
+ }
+ var phase = eventQueue.getEventPhase(eventSeq[idx]);
+ target.ownerDocument.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 = eventSeq[idx].target;
+ var phase = eventQueue.getEventPhase(eventSeq[idx]);
+ target.ownerDocument.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 = new Array();
+ 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 (aEventOrChecker instanceof nsIDOMEvent)
+ 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 (aEventOrChecker instanceof nsIDOMEvent)
+ return prettyName(aEventOrChecker.originalTarget);
+
+ if (aEventOrChecker instanceof nsIDOMEvent)
+ 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.compareEventTypes =
+ function eventQueue_compareEventTypes(aChecker, aEvent)
+{
+ var eventType = (aEvent instanceof nsIDOMEvent) ?
+ 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 = (aEvent instanceof nsIDOMEvent) ?
+ 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 = (aEvent instanceof nsIDOMEvent) ?
+ 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 (aOrigEvent instanceof nsIDOMEvent) {
+ 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 = new Array();
+ 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 = new Array();
+
+ // 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 instanceof nsIDOMDocument) {
+ targetNode =
+ this.DOMNode.body ? this.DOMNode.body : this.DOMNode.documentElement;
+ }
+
+ // Scroll the node into view, otherwise synth click may fail.
+ if (targetNode instanceof nsIDOMHTMLElement) {
+ targetNode.scrollIntoView(true);
+ } else if (targetNode instanceof nsIDOMXULElement) {
+ var targetAcc = getAccessible(targetNode);
+ targetAcc.scrollTo(SCROLL_TYPE_ANYWHERE);
+ }
+
+ var x = 1, y = 1;
+ if (aArgs && ("where" in aArgs) && aArgs.where == "right") {
+ if (targetNode instanceof nsIDOMHTMLElement)
+ x = targetNode.offsetWidth - 1;
+ else if (targetNode instanceof nsIDOMXULElement)
+ x = targetNode.boxObject.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 instanceof Components.interfaces.nsIDOMNSEditableElement &&
+ this.DOMNode.editor ||
+ this.DOMNode instanceof Components.interfaces.nsIDOMXULTextBoxElement) {
+ 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("VK_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 (this.DOMNode instanceof Components.interfaces.nsIDOMHTMLInputElement ||
+ this.DOMNode instanceof Components.interfaces.nsIDOMXULTextBoxElement) {
+ 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, aID));
+ } else {
+ this.__proto__ = new synthEndKey(aID,
+ new caretMoveChecker(aCaretOffset, 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, aID));
+
+ this.invoke = function moveToPrevLineEnd_invoke()
+ {
+ synthesizeKey("VK_UP", { });
+
+ if (MAC)
+ synthesizeKey("VK_RIGHT", { metaKey: true });
+ else
+ synthesizeKey("VK_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, aID));
+ } else {
+ this.__proto__ = new synthHomeKey(aID,
+ new caretMoveChecker(aCaretOffset, 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, aID));
+ } else {
+ this.__proto__ = new synthKey(aID, "VK_HOME", { ctrlKey: true },
+ new caretMoveChecker(0, 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.ownerDocument.defaultView.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, 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, 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, aTargetOrFunc, aTargetFuncArg,
+ aIsAsync)
+{
+ this.__proto__ = new invokerChecker(EVENT_TEXT_CARET_MOVED,
+ aTargetOrFunc, aTargetFuncArg, aIsAsync);
+
+ this.check = function caretMoveChecker_check(aEvent)
+ {
+ is(aEvent.QueryInterface(nsIAccessibleCaretMoveEvent).caretOffset,
+ aCaretOffset,
+ "Wrong caret offset for " + prettyName(aEvent.accessible));
+ }
+}
+
+function asyncCaretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg)
+{
+ this.__proto__ = new caretMoveChecker(aCaretOffset, aTargetOrFunc,
+ aTargetFuncArg, true);
+}
+
+/**
+ * Text selection change checker.
+ */
+function textSelectionChecker(aID, aStartOffset, aEndOffset)
+{
+ 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);
+ }
+ }
+}
+
+/**
+ * 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.toString(), 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.toString() == 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 =
+{
+ 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 "[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", false);
+ } else {
+ // Remove observer when there are no more applicants only.
+ // '< 0' case should not happen, but just in case: removeObserver() will throw.
+ if (--gA11yEventApplicantsCount <= 0)
+ Services.obs.removeObserver(gA11yEventObserver, "accessible-event");
+ }
+}
+
+function addA11yEventListener(aEventType, aEventHandler)
+{
+ if (!(aEventType in gA11yEventListeners))
+ gA11yEventListeners[aEventType] = new Array();
+
+ 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 = document instanceof nsIDOMHTMLDocument ?
+ "div" : "description";
+
+ var container = document.createElement(containerTagName);
+ if (aHasIndent)
+ container.setAttribute("style", "padding-left: 10px;");
+
+ if (aPreEmphText) {
+ var inlineTagName = document instanceof nsIDOMHTMLDocument ?
+ "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();
+ }
+
+ var item = this;
+
+ 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 000000000..4ea7c9d10
--- /dev/null
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+support-files =
+ docload_wnd.html
+ focus.html
+ scroll.html
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+
+[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_caretmove.xul]
+[test_coalescence.html]
+[test_contextmenu.html]
+[test_descrchange.html]
+[test_docload.html]
+[test_docload.xul]
+skip-if = buildapp == 'mulet'
+[test_docload_aria.html]
+[test_dragndrop.html]
+[test_flush.html]
+[test_focus_aria_activedescendant.html]
+[test_focus_autocomplete.xul]
+# Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795
+skip-if = os == 'win' || os == 'linux'
+[test_focus_browserui.xul]
+[test_focus_canvas.html]
+[test_focus_contextmenu.xul]
+[test_focus_controls.html]
+[test_focus_dialog.html]
+[test_focus_doc.html]
+[test_focus_general.html]
+[test_focus_general.xul]
+[test_focus_listcontrols.xul]
+[test_focus_menu.xul]
+[test_focus_name.html]
+[test_focus_selects.html]
+[test_focus_tabbox.xul]
+[test_focus_tree.xul]
+[test_fromUserInput.html]
+[test_label.xul]
+[test_menu.xul]
+[test_mutation.html]
+[test_mutation.xhtml]
+[test_namechange.xul]
+[test_namechange.html]
+[test_scroll.xul]
+[test_scroll_caret.xul]
+[test_selection.html]
+skip-if = buildapp == 'mulet' || os == 'mac'
+[test_selection.xul]
+skip-if = os == 'mac'
+[test_selection_aria.html]
+[test_statechange.html]
+[test_text.html]
+[test_text_alg.html]
+[test_textattrchange.html]
+[test_textselchange.html]
+[test_tree.xul]
+[test_valuechange.html]
+skip-if = os == 'mac'
diff --git a/accessible/tests/mochitest/events/docload_wnd.html b/accessible/tests/mochitest/events/docload_wnd.html
new file mode 100644
index 000000000..86ddfac5e
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload_wnd.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+ <title>Accessible events testing for document</title>
+ <script>
+ const STATE_BUSY = Components.interfaces.nsIAccessibleStates.STATE_BUSY;
+
+ var gService = null;
+ function waitForDocLoad()
+ {
+ if (!gService) {
+ gService = Components.classes["@mozilla.org/accessibilityService;1"].
+ getService(Components.interfaces.nsIAccessibilityService);
+ }
+
+ var accDoc = gService.getAccessibleFor(document);
+
+ var state = {};
+ accDoc.getState(state, {});
+ if (state.value & STATE_BUSY) {
+ window.setTimeout(waitForDocLoad, 0);
+ return;
+ }
+
+ hideIFrame();
+ }
+
+ function hideIFrame()
+ {
+ var iframe = document.getElementById("iframe");
+ gService.getAccessibleFor(iframe.contentDocument);
+ iframe.style.display = 'none';
+ }
+ </script>
+</head>
+
+<body onload="waitForDocLoad();">
+ <iframe id="iframe"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/focus.html b/accessible/tests/mochitest/events/focus.html
new file mode 100644
index 000000000..ab055df82
--- /dev/null
+++ b/accessible/tests/mochitest/events/focus.html
@@ -0,0 +1,10 @@
+<html>
+
+<head>
+ <title>editable document</title>
+</head>
+
+<body contentEditable="true">
+ editable document
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/scroll.html b/accessible/tests/mochitest/events/scroll.html
new file mode 100644
index 000000000..562e0a382
--- /dev/null
+++ b/accessible/tests/mochitest/events/scroll.html
@@ -0,0 +1,181 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing for anchors</title>
+</head>
+
+<body>
+ <p>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+ <a name="link1">link1</a>
+
+ <p style="color: blue">
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+
+ <h1 id="heading_1">heading 1</h1>
+ <p style="color: blue">
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+</body>
+<html>
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 000000000..2dab35723
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_alert.html
@@ -0,0 +1,92 @@
+<html>
+
+<head>
+ <title>ARIA alert event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function showAlert(aID)
+ {
+ this.DOMNode = document.createElement("div");
+
+ this.invoke = function showAlert_invoke()
+ {
+ this.DOMNode.setAttribute("role", "alert");
+ this.DOMNode.setAttribute("id", aID);
+ var text = document.createTextNode("alert");
+ this.DOMNode.appendChild(text);
+ document.body.appendChild(this.DOMNode);
+ };
+
+ this.getID = function showAlert_getID()
+ {
+ return "Show ARIA alert " + aID;
+ };
+ }
+
+ function changeAlert(aID)
+ {
+ this.__defineGetter__("DOMNode", function() { return getNode(aID) });
+
+ this.invoke = function changeAlert_invoke()
+ {
+ this.DOMNode.textContent = "new alert";
+ }
+
+ this.getID = function showAlert_getID()
+ {
+ return "Change ARIA alert " + aID;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuging
+ //enableLogging("tree,events,verbose");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_ALERT);
+
+ gQueue.push(new showAlert("alert"));
+ gQueue.push(new changeAlert("alert"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=591199"
+ title="mochitest for bug 334386: fire alert event when ARIA alert is shown or new its children are inserted">
+ Mozilla Bug 591199
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+</body>
+</html>
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 000000000..5ac595ebf
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_menu.html
@@ -0,0 +1,285 @@
+<html>
+
+<head>
+ <title>ARIA menu events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ const kViaDisplayStyle = 0;
+ const kViaVisibilityStyle = 1;
+
+ function focusMenu(aMenuBarID, aMenuID, aActiveMenuBarID)
+ {
+ this.eventSeq = [];
+
+ if (aActiveMenuBarID) {
+ this.eventSeq.push(new invokerChecker(EVENT_MENU_END,
+ getNode(aActiveMenuBarID)));
+ }
+
+ this.eventSeq.push(new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)));
+ this.eventSeq.push(new invokerChecker(EVENT_FOCUS, getNode(aMenuID)));
+
+ this.invoke = function focusMenu_invoke()
+ {
+ getNode(aMenuID).focus();
+ }
+
+ this.getID = function focusMenu_getID()
+ {
+ return "focus menu '" + aMenuID + "'";
+ }
+ }
+
+ function showMenu(aMenuID, aParentMenuID, aHow)
+ {
+ this.menuNode = getNode(aMenuID);
+
+ // Because of aria-owns processing we may have menupopup start fired before
+ // related show event.
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.menuNode),
+ new invokerChecker(EVENT_REORDER, getNode(aParentMenuID)),
+ new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode)
+ ];
+
+ this.invoke = function showMenu_invoke()
+ {
+ if (aHow == kViaDisplayStyle)
+ this.menuNode.style.display = "block";
+ else
+ this.menuNode.style.visibility = "visible";
+ };
+
+ this.getID = function showMenu_getID()
+ {
+ return "Show ARIA menu '" + aMenuID + "' by " +
+ (aHow == kViaDisplayStyle ? "display" : "visibility") +
+ " style tricks";
+ };
+ }
+
+ function closeMenu(aMenuID, aParentMenuID, aHow)
+ {
+ this.menuNode = getNode(aMenuID);
+ this.menu = null;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getMenu, this),
+ new invokerChecker(EVENT_MENUPOPUP_END, getMenu, this),
+ new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
+ ];
+
+ this.invoke = function closeMenu_invoke()
+ {
+ // Store menu accessible reference while menu is still open.
+ this.menu = getAccessible(this.menuNode);
+
+ // Hide menu.
+ if (aHow == kViaDisplayStyle)
+ this.menuNode.style.display = "none";
+ else
+ this.menuNode.style.visibility = "hidden";
+ }
+
+ this.getID = function closeMenu_getID()
+ {
+ return "Close ARIA menu " + aMenuID + " by " +
+ (aHow == kViaDisplayStyle ? "display" : "visibility") +
+ " style tricks";
+ }
+
+ function getMenu(aThisObj)
+ {
+ return aThisObj.menu;
+ }
+ }
+
+ function focusInsideMenu(aMenuID, aMenuBarID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode(aMenuID))
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID))
+ ];
+
+ this.invoke = function focusInsideMenu_invoke()
+ {
+ getNode(aMenuID).focus();
+ }
+
+ this.getID = function focusInsideMenu_getID()
+ {
+ return "focus menu '" + aMenuID + "'";
+ }
+ }
+
+ function blurMenu(aMenuBarID)
+ {
+ var eventSeq = [
+ new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID)),
+ new invokerChecker(EVENT_FOCUS, getNode("outsidemenu"))
+ ];
+
+ this.__proto__ = new synthClick("outsidemenu", eventSeq);
+
+ this.getID = function blurMenu_getID()
+ {
+ return "blur menu";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuging
+ //enableLogging("tree,events,verbose");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusMenu("menubar2", "menu-help"));
+ gQueue.push(new focusMenu("menubar", "menu-file", "menubar2"));
+ gQueue.push(new showMenu("menupopup-file", "menu-file", kViaDisplayStyle));
+ gQueue.push(new closeMenu("menupopup-file", "menu-file", kViaDisplayStyle));
+ gQueue.push(new showMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
+ gQueue.push(new closeMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
+ gQueue.push(new focusInsideMenu("menu-edit", "menubar"));
+ gQueue.push(new blurMenu("menubar"));
+
+ gQueue.push(new focusMenu("menubar3", "mb3-mi-outside"));
+ gQueue.push(new showMenu("mb4-menu", document, kViaDisplayStyle));
+ gQueue.push(new focusMenu("menubar4", "mb4-item1"));
+ gQueue.push(new focusMenu("menubar5", "mb5-mi"));
+
+ gQueue.push(new synthFocus("mi6"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=606207"
+ title="Dojo dropdown buttons are broken">
+ Bug 606207
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=614829"
+ title="Menupopup end event isn't fired for ARIA menus">
+ Bug 614829
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=615189"
+ title="Clean up FireAccessibleFocusEvent">
+ Bug 615189
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=933322"
+ title="menustart/end events are missing when aria-owns makes a menu hierarchy">
+ Bug 933322
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=934460"
+ title="menustart/end events may be missed when top level menuitem is focused">
+ Bug 934460
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=970005"
+ title="infinite long loop in a11y:FocusManager::ProcessFocusEvent">
+ Bug 970005
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="menubar" role="menubar">
+ <div id="menu-file" role="menuitem" tabindex="0">
+ File
+ <div id="menupopup-file" role="menu" style="display: none;">
+ <div id="menuitem-newtab" role="menuitem" tabindex="0">New Tab</div>
+ <div id="menuitem-newwindow" role="menuitem" tabindex="0">New Window</div>
+ </div>
+ </div>
+ <div id="menu-edit" role="menuitem" tabindex="0">
+ Edit
+ <div id="menupopup-edit" role="menu" style="visibility: hidden;">
+ <div id="menuitem-undo" role="menuitem" tabindex="0">Undo</div>
+ <div id="menuitem-redo" role="menuitem" tabindex="0">Redo</div>
+ </div>
+ </div>
+ </div>
+ <div id="menubar2" role="menubar">
+ <div id="menu-help" role="menuitem" tabindex="0">
+ Help
+ <div id="menupopup-help" role="menu" style="display: none;">
+ <div id="menuitem-about" role="menuitem" tabindex="0">About</div>
+ </div>
+ </div>
+ </div>
+ <div tabindex="0" id="outsidemenu">outsidemenu</div>
+
+ <!-- aria-owns relations -->
+ <div id="menubar3" role="menubar" aria-owns="mb3-mi-outside"></div>
+ <div id="mb3-mi-outside" role="menuitem" tabindex="0">Outside</div>
+
+ <div id="menubar4" role="menubar">
+ <div id="mb4_topitem" role="menuitem" aria-haspopup="true"
+ aria-owns="mb4-menu">Item</div>
+ </div>
+ <div id="mb4-menu" role="menu" style="display:none;">
+ <div role="menuitem" id="mb4-item1" tabindex="0">Item 1.1</div>
+ <div role="menuitem" tabindex="0">Item 1.2</div>
+ </div>
+
+ <!-- focus top-level menu item having haspopup -->
+ <div id="menubar5" role="menubar">
+ <div role="menuitem" aria-haspopup="true" id="mb5-mi" tabindex="0">
+ Item
+ <div role="menu" style="display:none;">
+ <div role="menuitem" tabindex="0">Item 1.1</div>
+ <div role="menuitem" tabindex="0">Item 1.2</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- other aria-owns relations -->
+ <div id="mi6" tabindex="0" role="menuitem">aria-owned item</div>
+ <div aria-owns="mi6">Obla</div>
+
+ <div id="eventdump"></div>
+
+</body>
+</html>
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 000000000..5f16ba794
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_objattr.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible ARIA object attribute changes</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+ function updateAttribute(aID, aAttr, aValue)
+ {
+ this.node = getNode(aID);
+ this.accessible = getAccessible(this.node);
+
+ this.eventSeq = [
+ new objAttrChangedChecker(aID, aAttr),
+ ];
+
+ this.invoke = function updateAttribute_invoke()
+ {
+ this.node.setAttribute(aAttr, aValue);
+ };
+
+ this.getID = function updateAttribute_getID()
+ {
+ return aAttr + " for " + aID + " " + aValue;
+ };
+ }
+
+ function updateARIAHidden(aID, aIsDefined, aChildId)
+ {
+ this.__proto__ = new updateAttribute(aID, "aria-hidden",
+ aIsDefined ? "true" : "false");
+
+ this.finalCheck = function updateARIAHidden()
+ {
+ if (aIsDefined) {
+ testAttrs(aID, {"hidden" : "true"}, true);
+ testAttrs(aChildId, {"hidden" : "true"}, true);
+ } else {
+ testAbsentAttrs(aID, { "hidden": "true"});
+ testAbsentAttrs(aChildId, { "hidden": "true"});
+ }
+ }
+ }
+
+ // Debug stuff.
+ // gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new updateARIAHidden("hideable", true, "hideable_child"));
+ gQueue.push(new updateARIAHidden("hideable", false, "hideable_child"));
+
+ gQueue.push(new updateAttribute("sortable", "aria-sort", "ascending"));
+
+ // For experimental ARIA extensions
+ gQueue.push(new updateAttribute("custom", "aria-blah", "true"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=581096"
+ title="Add support for aria-hidden">
+ Mozilla Bug 581096
+ </a>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
+ title="Add event support for aria-sort">
+ Mozilla Bug 640707
+ </a>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
+ title="Expand support for aria attribute change events">
+ Mozilla Bug 563862
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="hideable"><div id="hideable_child">Hi</div><div>there</div></div>
+
+ <div id="sortable" role="columnheader" aria-sort="none">aria-sort</div>
+
+ <div id="custom" role="custom" aria-blah="false">Fat free cheese</div>
+</body>
+</html>
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 000000000..a415a6d8d
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_owns.html
@@ -0,0 +1,129 @@
+<html>
+
+<head>
+ <title>Aria-owns targets shouldn't be on invalidation list so shouldn't have
+ show/hide events</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests.
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("tree,eventTree,verbose");
+
+ /**
+ * Aria-owns target shouldn't have a show event.
+ * Markup:
+ * <div id="t1_fc" aria-owns="t1_owns"></div>
+ * <span id="t1_owns"></div>
+ */
+ function testAriaOwns()
+ {
+ this.parent = getNode("t1");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t1_fc");
+ this.owns = document.createElement("span");
+ this.owns.setAttribute("id", "t1_owns");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function testAriaOwns_invoke()
+ {
+ getNode("t1").appendChild(this.fc);
+ getNode("t1").appendChild(this.owns);
+ getNode("t1_fc").setAttribute("aria-owns", "t1_owns");
+ };
+
+ this.getID = function testAriaOwns_getID() {
+ return "Aria-owns target shouldn't have show event";
+ };
+ }
+
+ /**
+ * Target of both aria-owns and other aria attribute like aria-labelledby
+ * shouldn't have a show event.
+ * Markup:
+ * <div id="t2_fc" aria-owns="t1_owns"></div>
+ * <div id="t2_sc" aria-labelledby="t2_owns"></div>
+ * <span id="t2_owns"></div>
+ */
+ function testAriaOwnsAndLabelledBy()
+ {
+ this.parent = getNode("t2");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t2_fc");
+ this.sc = document.createElement("div");
+ this.sc.setAttribute("id", "t2_sc");
+ this.owns = document.createElement("span");
+ this.owns.setAttribute("id", "t2_owns");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new invokerChecker(EVENT_SHOW, this.sc),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function testAriaOwns_invoke()
+ {
+ getNode("t2").appendChild(this.fc);
+ getNode("t2").appendChild(this.sc);
+ getNode("t2").appendChild(this.owns);
+ getNode("t2_fc").setAttribute("aria-owns", "t2_owns");
+ getNode("t2_sc").setAttribute("aria-labelledby", "t2_owns");
+ };
+
+ this.getID = function testAriaOwns_getID() {
+ return "Aria-owns and aria-labelledby target shouldn't have show event";
+ };
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new testAriaOwns());
+ gQueue.push(new testAriaOwnsAndLabelledBy());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1296420"
+ title="Aria-owns targets shouldn't be on invalidation list so shouldn't
+ have show/hide events">
+ Mozilla Bug 1296420
+ </a><br>
+
+ <div id="testContainer">
+ <div id="t1"></div>
+
+ <div id="t2"></div>
+ </div>
+
+</body>
+</html>
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 000000000..d8c833157
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_statechange.html
@@ -0,0 +1,208 @@
+<html>
+
+<head>
+ <title>ARIA state change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ //gA11yEventDumpID = "eventdump"; // debugging
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function expandNode(aID, aIsExpanded)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new expandedStateChecker(aIsExpanded, this.DOMNode)
+ ];
+
+ this.invoke = function expandNode_invoke()
+ {
+ this.DOMNode.setAttribute("aria-expanded",
+ (aIsExpanded ? "true" : "false"));
+ };
+
+ this.getID = function expandNode_getID()
+ {
+ return prettyName(aID) + " aria-expanded changed to '" + aIsExpanded + "'";
+ };
+ }
+
+ function busyify(aID, aIsBusy)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(STATE_BUSY, kOrdinalState, aIsBusy, this.DOMNode)
+ ];
+
+ this.invoke = function busyify_invoke()
+ {
+ this.DOMNode.setAttribute("aria-busy", (aIsBusy ? "true" : "false"));
+ };
+
+ this.getID = function busyify_getID()
+ {
+ return prettyName(aID) + " aria-busy changed to '" + aIsBusy + "'";
+ };
+ }
+
+ function setAttrOfMixedType(aID, aAttr, aState, aValue)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(aState, kOrdinalState,
+ aValue == "true", this.DOMNode)
+ ];
+
+ if (hasState(aID, STATE_MIXED) || aValue == "mixed") {
+ this.eventSeq.push(
+ new stateChangeChecker(STATE_MIXED, kOrdinalState,
+ aValue == "mixed", this.DOMNode)
+ );
+ }
+
+ this.invoke = function setAttrOfMixedType_invoke()
+ {
+ this.DOMNode.setAttribute(aAttr, aValue);
+ };
+
+ this.getID = function setAttrOfMixedType_getID()
+ {
+ return prettyName(aID) + " " + aAttr + " changed to '" + aValue + "'";
+ };
+ }
+
+ function setPressed(aID, aValue)
+ {
+ this.__proto__ =
+ new setAttrOfMixedType(aID, "aria-pressed", STATE_PRESSED, aValue);
+ }
+
+ function setChecked(aID, aValue)
+ {
+ this.__proto__ =
+ new setAttrOfMixedType(aID, "aria-checked", STATE_CHECKED, aValue);
+ }
+
+ function buildQueueForAttr(aList, aQueue, aID, aInvokerFunc)
+ {
+ for (var i = 0; i < aList.length; i++) {
+ for (var j = i + 1; j < aList.length; j++) {
+ // XXX: changes from/to "undefined"/"" shouldn't fire state change
+ // events, bug 472142.
+ aQueue.push(new aInvokerFunc(aID, aList[i]));
+ aQueue.push(new aInvokerFunc(aID, aList[j]));
+ }
+ }
+ }
+
+ function buildQueueForAttrOfMixedType(aQueue, aID, aInvokerFunc)
+ {
+ var list = [ "", "undefined", "false", "true", "mixed" ];
+ buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
+ }
+
+ function buildQueueForAttrOfBoolType(aQueue, aID, aInvokerFunc)
+ {
+ var list = [ "", "undefined", "false", "true" ];
+ buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
+ }
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new expandNode("section", true));
+ gQueue.push(new expandNode("section", false));
+ gQueue.push(new expandNode("div", true));
+ gQueue.push(new expandNode("div", false));
+
+ gQueue.push(new busyify("aria_doc", true));
+ gQueue.push(new busyify("aria_doc", false));
+
+ buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
+ buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
+ buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
+ buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=551684"
+ title="No statechange event for aria-expanded on native HTML elements, is fired on ARIA widgets">
+ Mozilla Bug 551684
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=648133"
+ title="fire state change event for aria-busy">
+ Mozilla Bug 648133
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467143"
+ title="mixed state change event is fired for focused accessible only">
+ Mozilla Bug 467143
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=989958"
+ title="Pressed state is not exposed on a button element with aria-pressed attribute">
+ Mozilla Bug 989958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
+ title="Support ARIA 1.1 switch role">
+ Mozilla Bug 1136563
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- aria-expanded -->
+ <div id="section" role="section" aria-expanded="false">expandable section</div>
+ <div id="div" aria-expanded="false">expandable native div</div>
+
+ <!-- aria-busy -->
+ <div id="aria_doc" role="document" tabindex="0">A document</div>
+
+ <!-- aria-pressed -->
+ <div id="pressable" role="button"></div>
+ <button id="pressable_native"></button>
+
+ <!-- aria-checked -->
+ <div id="checkable" role="checkbox"></div>
+ <div id="checkableBool" role="switch"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_attrs.html b/accessible/tests/mochitest/events/test_attrs.html
new file mode 100644
index 000000000..1d5577572
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_attrs.html
@@ -0,0 +1,90 @@
+<html>
+
+<head>
+ <title>Event object attributes tests</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Test "event-from-input" object attribute.
+ */
+ function eventFromInputChecker(aEventType, aID, aValue, aNoTargetID)
+ {
+ this.type = aEventType;
+ this.target = getAccessible(aID);
+
+ this.noTarget = getNode(aNoTargetID);
+
+ this.check = function checker_check(aEvent)
+ {
+ testAttrs(aEvent.accessible, { "event-from-input": aValue }, true);
+
+ var accessible = getAccessible(this.noTarget);
+ testAbsentAttrs(accessible, { "event-from-input": "" });
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var id = "textbox", noTargetId = "textarea";
+ var checker =
+ new eventFromInputChecker(EVENT_FOCUS, id, "false", noTargetId);
+ gQueue.push(new synthFocus(id, checker));
+
+ if (!MAC) { // Mac failure is bug 541093
+ var checker =
+ new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "true", noTargetId);
+ gQueue.push(new synthHomeKey(id, checker));
+ }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=540285"
+ title="Event object attributes testing">
+ Mozilla Bug 540285
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox" value="hello">
+ <textarea id="textarea"></textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
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 000000000..20136d393
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_bug1322593-2.html
@@ -0,0 +1,83 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function changeMultipleElements()
+ {
+ this.node1 = getNode("span1");
+ this.node2 = getNode("span2");
+
+ this.eventSeq = [
+ new textChangeChecker("container", 0, 5, "hello", false, undefined, true),
+ new textChangeChecker("container", 6, 11, "world", false, undefined, true),
+ new orderChecker(),
+ new textChangeChecker("container", 0, 1, "a", true, undefined, true),
+ new textChangeChecker("container", 7, 8, "b", true, undefined, true)
+ ];
+
+ this.invoke = function changeMultipleElements_invoke()
+ {
+ this.node1.textContent = "a";
+ this.node2.textContent = "b";
+ }
+
+ this.getID = function changeMultipleElements_invoke_getID()
+ {
+ return "Change the text content of multiple sibling divs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+// gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeMultipleElements());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322593"
+ title="missing text change events when multiple elements updated at once">
+ Mozilla Bug 1322593
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container">
+ <span id="span1">hello</span>
+ <span>your</span>
+ <span id="span2">world</span>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_bug1322593.html b/accessible/tests/mochitest/events/test_bug1322593.html
new file mode 100644
index 000000000..38786d0b9
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_bug1322593.html
@@ -0,0 +1,80 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function changeMultipleElements()
+ {
+ this.node1 = getNode("div1");
+ this.node2 = getNode("div2");
+
+ this.eventSeq = [
+ new textChangeChecker("div1", 0, 5, "hello", false, undefined, true),
+ new textChangeChecker("div2", 0, 5, "world", false, undefined, true),
+ new orderChecker(),
+ new textChangeChecker("div1", 0, 1, "a", true, undefined, true),
+ new textChangeChecker("div2", 0, 1, "b", true, undefined, true)
+ ];
+
+ this.invoke = function changeMultipleElements_invoke()
+ {
+ this.node1.textContent = "a";
+ this.node2.textContent = "b";
+ }
+
+ this.getID = function changeMultipleElements_invoke_getID()
+ {
+ return "Change the text content of multiple sibling divs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+// gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeMultipleElements());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322593"
+ title="missing text change events when multiple elements updated at once">
+ Mozilla Bug 1322593
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="div1">hello</div>
+ <div id="div2">world</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_caretmove.html b/accessible/tests/mochitest/events/test_caretmove.html
new file mode 100644
index 000000000..6d39c4ef6
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -0,0 +1,140 @@
+<html>
+
+<head>
+ <title>Accessible caret move events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Click checker.
+ */
+ function clickChecker(aCaretOffset, aID, aExtraNodeOrID, aExtraCaretOffset)
+ {
+ this.__proto__ = new caretMoveChecker(aCaretOffset, aID);
+
+ this.extraNode = getNode(aExtraNodeOrID);
+
+ this.check = function clickChecker_check(aEvent)
+ {
+ this.__proto__.check(aEvent);
+
+ if (this.extraNode) {
+ var acc = getAccessible(this.extraNode, [nsIAccessibleText]);
+ is(acc.caretOffset, aExtraCaretOffset,
+ "Wrong caret offset for " + aExtraNodeOrID);
+ }
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ // test caret move events and caret offsets
+ gQueue = new eventQueue();
+
+ var id = "textbox";
+ gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
+ gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+
+ id = "textarea";
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+ gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
+
+ id = "textarea_wrapped";
+ gQueue.push(new setCaretOffset(id, 4, id));
+ gQueue.push(new synthLeftKey(id, new caretMoveChecker(4, id)));
+
+ id = "p";
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+ gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
+
+ id = "p1_in_div";
+ gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
+
+ id = "p";
+ gQueue.push(new synthShiftTab(id, new caretMoveChecker(0, id)));
+ id = "textarea";
+ gQueue.push(new synthShiftTab(id, new caretMoveChecker(12, id)));
+ id = "p";
+ gQueue.push(new synthTab(id, new caretMoveChecker(0, id)));
+
+ // Set caret after a child of span element, i.e. after 'text' text.
+ gQueue.push(new moveCaretToDOMPoint("test1", getNode("test1_span"), 1,
+ 4, "test1"));
+ gQueue.push(new moveCaretToDOMPoint("test2", getNode("test2_span"), 1,
+ 4, "test2"));
+
+ // empty text element
+ gQueue.push(new moveCaretToDOMPoint("test3", getNode("test3"), 0,
+ 0, "test3"));
+ gQueue.push(new moveCaretToDOMPoint("test4", getNode("test4_span"), 0,
+ 0, "test4"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=454377"
+ title="Accessible caret move events testing">
+ Bug 454377
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=567571"
+ title="caret-moved events missing at the end of a wrapped line of text">
+ Bug 567571
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=824901"
+ title="HyperTextAccessible::DOMPointToHypertextOffset fails for node and offset equal to node child count">
+ Bug 824901
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox" value="hello"/>
+ <textarea id="textarea">text<br>text</textarea>
+ <p id="p" contentEditable="true"><span>text</span><br/>text</p>
+ <div id="div" contentEditable="true"><p id="p1_in_div">text</p><p id="p2_in_div">text</p></div>
+
+ <p contentEditable="true" id="test1"><span id="test1_span">text</span>ohoho</p>
+ <p contentEditable="true" id="test2"><span><span id="test2_span">text</span></span>ohoho</p>
+ <p contentEditable="true" id="test3"></p>
+ <p contentEditable="true" id="test4"><span id="test4_span"></span></p>
+
+ <textarea id="textarea_wrapped" cols="5">hey friend</textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_caretmove.xul b/accessible/tests/mochitest/events/test_caretmove.xul
new file mode 100644
index 000000000..cf4dcd483
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_caretmove.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Caret move event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ if (MAC) {
+ todo(false, "Make these tests pass on OSX (bug 650294)");
+ SimpleTest.finish();
+ return;
+ }
+
+ gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
+
+ var id = "textbox";
+ var input = getNode(id).inputField;
+ gQueue.push(new synthFocus(id, new caretMoveChecker(5, input)));
+ gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, input)));
+ gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, input)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, input)));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=634240"
+ title="No caret move events are fired for XUL textbox accessible">
+ Mozilla Bug 634240
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="textbox" value="hello"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_coalescence.html b/accessible/tests/mochitest/events/test_coalescence.html
new file mode 100644
index 000000000..d95ef99b0
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -0,0 +1,864 @@
+<html>
+
+<head>
+ <title>Accessible mutation events coalescence testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker base classes
+
+ const kRemoveElm = 1;
+ const kHideElm = 2;
+ const kAddElm = 3;
+ const kShowElm = 4;
+
+ /**
+ * Base class to test of mutation events coalescence.
+ */
+ function coalescenceBase(aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ // Invoker interface
+
+ this.invoke = function coalescenceBase_invoke()
+ {
+ if (aPerformActionOnChildInTheFirstPlace) {
+ this.invokeAction(this.childNode, aChildAction);
+ this.invokeAction(this.parentNode, aParentAction);
+ } else {
+ this.invokeAction(this.parentNode, aParentAction);
+ this.invokeAction(this.childNode, aChildAction);
+ }
+ }
+
+ this.getID = function coalescenceBase_getID()
+ {
+ var childAction = this.getActionName(aChildAction) + " child";
+ var parentAction = this.getActionName(aParentAction) + " parent";
+
+ if (aPerformActionOnChildInTheFirstPlace)
+ return childAction + " and then " + parentAction;
+
+ return parentAction + " and then " + childAction;
+ }
+
+ this.finalCheck = function coalescenceBase_check()
+ {
+ if (this.getEventType(aChildAction) == EVENT_HIDE) {
+ testIsDefunct(this.child);
+ }
+ if (this.getEventType(aParentAction) == EVENT_HIDE) {
+ testIsDefunct(this.parent);
+ }
+ }
+
+ // Implementation details
+
+ this.invokeAction = function coalescenceBase_invokeAction(aNode, aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm:
+ aNode.parentNode.removeChild(aNode);
+ break;
+
+ case kHideElm:
+ aNode.style.display = "none";
+ break;
+
+ case kAddElm:
+ if (aNode == this.parentNode)
+ this.hostNode.appendChild(this.parentNode);
+ else
+ this.parentNode.appendChild(this.childNode);
+ break;
+
+ case kShowElm:
+ aNode.style.display = "block";
+ break;
+
+ default:
+ return INVOKER_ACTION_FAILED;
+ }
+ }
+
+ this.getEventType = function coalescenceBase_getEventType(aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm: case kHideElm:
+ return EVENT_HIDE;
+ case kAddElm: case kShowElm:
+ return EVENT_SHOW;
+ }
+ }
+
+ this.getActionName = function coalescenceBase_getActionName(aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm:
+ return "remove";
+ case kHideElm:
+ return "hide";
+ case kAddElm:
+ return "add";
+ case kShowElm:
+ return "show";
+ default:
+ return "??";
+ }
+ }
+
+ this.initSequence = function coalescenceBase_initSequence()
+ {
+ // expected events
+ var eventType = this.getEventType(aParentAction);
+ this.eventSeq = [
+ new invokerChecker(eventType, this.parentNode),
+ new invokerChecker(EVENT_REORDER, this.hostNode)
+ ];
+
+ // unexpected events
+ this.unexpectedEventSeq = [
+ new invokerChecker(this.getEventType(aChildAction), this.childNode),
+ new invokerChecker(EVENT_REORDER, this.parentNode)
+ ];
+ }
+ }
+
+ /**
+ * Remove or hide mutation events coalescence testing.
+ */
+ function removeOrHideCoalescenceBase(aChildID, aParentID,
+ aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.__proto__ = new coalescenceBase(aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init = function removeOrHideCoalescenceBase_init()
+ {
+ this.childNode = getNode(aChildID);
+ this.parentNode = getNode(aParentID);
+ this.child = getAccessible(this.childNode);
+ this.parent = getAccessible(this.parentNode);
+ this.hostNode = this.parentNode.parentNode;
+ }
+
+ // Initalization
+
+ this.init();
+ this.initSequence();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Remove child node and then its parent node from DOM tree.
+ */
+ function removeChildNParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kRemoveElm,
+ true);
+ }
+
+ /**
+ * Remove parent node and then its child node from DOM tree.
+ */
+ function removeParentNChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kRemoveElm,
+ false);
+ }
+
+ /**
+ * Hide child node and then its parent node.
+ */
+ function hideChildNParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kHideElm,
+ true);
+ }
+
+ /**
+ * Hide parent node and then its child node.
+ */
+ function hideParentNChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kHideElm,
+ false);
+ }
+
+ /**
+ * Hide child node and then remove its parent node.
+ */
+ function hideChildNRemoveParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kRemoveElm,
+ true);
+ }
+
+ /**
+ * Hide parent node and then remove its child node.
+ */
+ function hideParentNRemoveChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kHideElm,
+ false);
+ }
+
+ /**
+ * Remove child node and then hide its parent node.
+ */
+ function removeChildNHideParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kHideElm,
+ true);
+ }
+
+ /**
+ * Remove parent node and then hide its child node.
+ */
+ function removeParentNHideChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kRemoveElm,
+ false);
+ }
+
+ /**
+ * Create and append parent node and create and append child node to it.
+ */
+ function addParentNChild(aHostID, aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function addParentNChild_init()
+ {
+ this.hostNode = getNode(aHostID);
+ this.parentNode = document.createElement("select");
+ this.childNode = document.createElement("option");
+ this.childNode.textContent = "testing";
+ }
+
+ this.__proto__ = new coalescenceBase(kAddElm, kAddElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Show parent node and show child node to it.
+ */
+ function showParentNChild(aParentID, aChildID,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function showParentNChild_init()
+ {
+ this.parentNode = getNode(aParentID);
+ this.hostNode = this.parentNode.parentNode;
+ this.childNode = getNode(aChildID);
+ }
+
+ this.__proto__ = new coalescenceBase(kShowElm, kShowElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Create and append child node to the DOM and then show parent node.
+ */
+ function showParentNAddChild(aParentID,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function showParentNAddChild_init()
+ {
+ this.parentNode = getNode(aParentID);
+ this.hostNode = this.parentNode.parentNode;
+ this.childNode = document.createElement("option");
+ this.childNode.textContent = "testing";
+ }
+
+ this.__proto__ = new coalescenceBase(kAddElm, kShowElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Remove children and parent
+ */
+ function removeGrandChildrenNHideParent(aChild1Id, aChild2Id, aParentId)
+ {
+ this.child1 = getNode(aChild1Id);
+ this.child2 = getNode(aChild2Id);
+ this.parent = getNode(aParentId);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aParentId)),
+ new invokerChecker(EVENT_REORDER, getNode(aParentId).parentNode),
+ new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild1Id)),
+ new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild2Id)),
+ new unexpectedInvokerChecker(EVENT_REORDER, getAccessible(aParentId))
+ ];
+
+ this.invoke = function removeGrandChildrenNHideParent_invoke()
+ {
+ this.child1.parentNode.removeChild(this.child1);
+ this.child2.parentNode.removeChild(this.child2);
+ this.parent.hidden = true;
+ }
+
+ this.getID = function removeGrandChildrenNHideParent_getID() {
+ return "remove grand children of different parents and then hide their grand parent";
+ }
+ }
+
+ /**
+ * Remove a child, and then its parent.
+ */
+ function test3()
+ {
+ this.o = getAccessible("t3_o");
+ this.ofc = getAccessible("t3_o").firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.o),
+ new invokerChecker(EVENT_REORDER, "t3_lb"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o)
+ ];
+
+ this.invoke = function test3_invoke()
+ {
+ getNode("t3_o").textContent = "";
+ getNode("t3_lb").removeChild(getNode("t3_o"));
+ }
+
+ this.finalCheck = function test3_finalCheck()
+ {
+ testIsDefunct(this.o);
+ testIsDefunct(this.ofc);
+ }
+
+ this.getID = function test3_getID() {
+ return "remove a child, and then its parent";
+ }
+ }
+
+ /**
+ * Remove children, and then a parent of 2nd child.
+ */
+ function test4()
+ {
+ this.o1 = getAccessible("t4_o1");
+ this.o1fc = this.o1.firstChild;
+ this.o2 = getAccessible("t4_o2");
+ this.o2fc = this.o2.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.o1fc),
+ new invokerChecker(EVENT_HIDE, this.o2),
+ new invokerChecker(EVENT_REORDER, "t4_lb"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.o2fc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o1),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o2)
+ ];
+
+ this.invoke = function test4_invoke()
+ {
+ getNode("t4_o1").textContent = "";
+ getNode("t4_o2").textContent = "";
+ getNode("t4_lb").removeChild(getNode("t4_o2"));
+ }
+
+ this.finalCheck = function test4_finalCheck()
+ {
+ testIsDefunct(this.o1fc);
+ testIsDefunct(this.o2);
+ testIsDefunct(this.o2fc);
+ }
+
+ this.getID = function test4_getID() {
+ return "remove children, and then a parent of 2nd child";
+ }
+ }
+
+ /**
+ * Remove a child, remove a parent sibling, remove the parent
+ */
+ function test5()
+ {
+ this.o = getAccessible("t5_o");
+ this.ofc = this.o.firstChild;
+ this.b = getAccessible("t5_b");
+ this.lb = getAccessible("t5_lb");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.b),
+ new invokerChecker(EVENT_HIDE, this.o),
+ new invokerChecker(EVENT_REORDER, "t5"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.lb)
+ ];
+
+ this.invoke = function test5_invoke()
+ {
+ getNode("t5_o").textContent = "";
+ getNode("t5").removeChild(getNode("t5_b"));
+ getNode("t5_lb").removeChild(getNode("t5_o"));
+ }
+
+ this.finalCheck = function test5_finalCheck()
+ {
+ testIsDefunct(this.ofc);
+ testIsDefunct(this.o);
+ testIsDefunct(this.b);
+ }
+
+ this.getID = function test5_getID() {
+ return "remove a child, remove a parent sibling, remove the parent";
+ }
+ }
+
+ /**
+ * Insert accessibles with a child node moved by aria-owns
+ * Markup:
+ * <div id="t6_fc">
+ * <div id="t6_owns"></div>
+ * </div>
+ * <div id="t6_sc" aria-owns="t6_owns"></div>
+ */
+ function test6()
+ {
+ this.parent = getNode("t6");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t6_fc");
+ this.owns = document.createElement("div");
+ this.owns.setAttribute("id", "t6_owns");
+ this.sc = document.createElement("div");
+ this.sc.setAttribute("id", "t6_sc");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new invokerChecker(EVENT_SHOW, this.sc),
+ new invokerChecker(EVENT_REORDER, this.parent),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.fc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.sc),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.owns),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function test6_invoke()
+ {
+ getNode("t6").appendChild(this.fc);
+ getNode("t6_fc").appendChild(this.owns);
+ getNode("t6").appendChild(this.sc);
+ getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
+ };
+
+ this.getID = function test6_getID() {
+ return "Insert accessibles with a child node moved by aria-owns";
+ };
+ }
+
+ /**
+ * Insert text nodes under direct and grand children, and then hide
+ * their container by means of aria-owns.
+ *
+ * Markup:
+ * <div id="t7_moveplace" aria-owns="t7_c"></div>
+ * <div id="t7_c">
+ * <div id="t7_c_directchild">ha</div>
+ * <div><div id="t7_c_grandchild">ha</div></div>
+ * </div>
+ */
+ function test7()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t7_c')),
+ new invokerChecker(EVENT_SHOW, getNode('t7_c')),
+ new invokerChecker(EVENT_REORDER, getNode('t7')),
+ new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_directchild')),
+ new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_grandchild')),
+ new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_directchild').firstChild),
+ new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_grandchild').firstChild)
+ ];
+
+ this.invoke = function test7_invoke()
+ {
+ getNode('t7_c_directchild').textContent = 'ha';
+ getNode('t7_c_grandchild').textContent = 'ha';
+ getNode('t7_moveplace').setAttribute('aria-owns', 't7_c');
+ };
+
+ this.getID = function test7_getID() {
+ return "Show child accessibles and then hide their container";
+ };
+ }
+
+ /**
+ * Move a node by aria-owns from right to left in the tree, so that
+ * the eventing looks this way:
+ * reorder for 't8_c1'
+ * hide for 't8_c1_child'
+ * show for 't8_c2_moved'
+ * reorder for 't8_c2'
+ * hide for 't8_c2_moved'
+ *
+ * The hide event should be delivered before the paired show event.
+ */
+ function test8()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t8_c1_child')),
+ new invokerChecker(EVENT_HIDE, 't8_c2_moved'),
+ new invokerChecker(EVENT_SHOW, 't8_c2_moved'),
+ new invokerChecker(EVENT_REORDER, 't8_c2'),
+ new invokerChecker(EVENT_REORDER, 't8_c1'),
+ ];
+
+ this.invoke = function test8_invoke()
+ {
+ // Remove a node from 't8_c1' container to give the event tree a
+ // desired structure (the 't8_c1' container node goes first in the event
+ // tree)
+ getNode('t8_c1_child').remove();
+ // then move 't8_c2_moved' from 't8_c2' to 't8_c1'.
+ getNode('t8_c1').setAttribute('aria-owns', 't8_c2_moved');
+ };
+
+ this.getID = function test8_getID() {
+ return "Move a node by aria-owns to left within the tree";
+ };
+ }
+
+ /**
+ * Move 't9_c3_moved' node under 't9_c2_moved', and then move 't9_c2_moved'
+ * node by aria-owns (same as test10 but has different aria-owns
+ * ordering), the eventing looks same way as in test10:
+ * reorder for 't9_c1'
+ * hide for 't9_c1_child'
+ * show for 't9_c2_moved'
+ * reorder for 't9_c2'
+ * hide for 't9_c2_child'
+ * hide for 't9_c2_moved'
+ * reorder for 't9_c3'
+ * hide for 't9_c3_moved'
+ *
+ * The hide events for 't9_c2_moved' and 't9_c3_moved' should be delivered
+ * before the show event for 't9_c2_moved'.
+ */
+ function test9()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t9_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t9_c2_child')),
+ new invokerChecker(EVENT_HIDE, 't9_c3_moved'),
+ new invokerChecker(EVENT_HIDE, 't9_c2_moved'),
+ new invokerChecker(EVENT_SHOW, 't9_c2_moved'),
+ new invokerChecker(EVENT_REORDER, 't9_c3'),
+ new invokerChecker(EVENT_REORDER, 't9_c2'),
+ new invokerChecker(EVENT_REORDER, 't9_c1'),
+ new unexpectedInvokerChecker(EVENT_SHOW, 't9_c3_moved')
+ ];
+
+ this.invoke = function test9_invoke()
+ {
+ // Remove child nodes from 't9_c1' and 't9_c2' containers to give
+ // the event tree a needed structure ('t9_c1' and 't9_c2' nodes go
+ // first in the event tree),
+ getNode('t9_c1_child').remove();
+ getNode('t9_c2_child').remove();
+ // then do aria-owns magic.
+ getNode('t9_c2_moved').setAttribute('aria-owns', 't9_c3_moved');
+ getNode('t9_c1').setAttribute('aria-owns', 't9_c2_moved');
+ };
+
+ this.getID = function test9_getID() {
+ return "Move node #1 by aria-owns and then move node #2 into node #1";
+ };
+ }
+
+ /**
+ * Move a node 't10_c3_moved' by aria-owns under a node 't10_c2_moved',
+ * moved by under 't10_1', so that the eventing looks this way:
+ * reorder for 't10_c1'
+ * hide for 't10_c1_child'
+ * show for 't10_c2_moved'
+ * reorder for 't10_c2'
+ * hide for 't10_c2_child'
+ * hide for 't10_c2_moved'
+ * reorder for 't10_c3'
+ * hide for 't10_c3_moved'
+ *
+ * The hide events for 't10_c2_moved' and 't10_c3_moved' should be delivered
+ * before the show event for 't10_c2_moved'.
+ */
+ function test10()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t10_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c2_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c2_moved')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c3_moved')),
+ new invokerChecker(EVENT_SHOW, getNode('t10_c2_moved')),
+ new invokerChecker(EVENT_REORDER, 't10_c2'),
+ new invokerChecker(EVENT_REORDER, 't10_c1'),
+ new invokerChecker(EVENT_REORDER, 't10_c3')
+ ];
+
+ this.invoke = function test10_invoke()
+ {
+ // Remove child nodes from 't10_c1' and 't10_c2' containers to give
+ // the event tree a needed structure ('t10_c1' and 't10_c2' nodes go first
+ // in the event tree),
+ getNode('t10_c1_child').remove();
+ getNode('t10_c2_child').remove();
+ // then do aria-owns stuff.
+ getNode('t10_c1').setAttribute('aria-owns', 't10_c2_moved');
+ getNode('t10_c2_moved').setAttribute('aria-owns', 't10_c3_moved');
+ };
+
+ this.getID = function test10_getID() {
+ return "Move a node by aria-owns into a node moved by aria-owns to left within the tree";
+ };
+ }
+
+ /**
+ * Move a node by aria-owns from right to left in the tree, and then
+ * move its parent too by aria-owns. No hide event should be fired for
+ * original node.
+ */
+ function test11()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t11_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t11_c2')),
+ new orderChecker(),
+ new asyncInvokerChecker(EVENT_SHOW, 't11_c2_child'),
+ new asyncInvokerChecker(EVENT_SHOW, 't11_c2'),
+ new orderChecker(),
+ new invokerChecker(EVENT_REORDER, 't11'),
+ new unexpectedInvokerChecker(EVENT_HIDE, 't11_c2_child'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c1'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c2'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c3')
+ ];
+
+ this.invoke = function test11_invoke()
+ {
+ // Remove a node from 't11_c1' container to give the event tree a
+ // desired structure (the 't11_c1' container node goes first in
+ // the event tree),
+ getNode('t11_c1_child').remove();
+ // then move 't11_c2_moved' from 't11_c2' to 't11_c1', and then move
+ // 't11_c2' to 't11_c3'.
+ getNode('t11_c1').setAttribute('aria-owns', 't11_c2_child');
+ getNode('t11_c3').setAttribute('aria-owns', 't11_c2');
+ };
+
+ this.getID = function test11_getID() {
+ return "Move a node by aria-owns to left within the tree";
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests.
+
+ gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("eventTree");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new removeChildNParent("option1", "select1"));
+ gQueue.push(new removeParentNChild("option2", "select2"));
+ gQueue.push(new hideChildNParent("option3", "select3"));
+ gQueue.push(new hideParentNChild("option4", "select4"));
+ gQueue.push(new hideChildNRemoveParent("option5", "select5"));
+ gQueue.push(new hideParentNRemoveChild("option6", "select6"));
+ gQueue.push(new removeChildNHideParent("option7", "select7"));
+ gQueue.push(new removeParentNHideChild("option8", "select8"));
+
+ gQueue.push(new addParentNChild("testContainer", false));
+ gQueue.push(new addParentNChild("testContainer", true));
+ gQueue.push(new showParentNChild("select9", "option9", false));
+ gQueue.push(new showParentNChild("select10", "option10", true));
+ gQueue.push(new showParentNAddChild("select11", false));
+ gQueue.push(new showParentNAddChild("select12", true));
+
+ gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
+ gQueue.push(new test3());
+ gQueue.push(new test4());
+ gQueue.push(new test5());
+ gQueue.push(new test6());
+ gQueue.push(new test7());
+ gQueue.push(new test8());
+ gQueue.push(new test9());
+ gQueue.push(new test10());
+ gQueue.push(new test11());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=513213"
+ title="coalesce events when new event is appended to the queue">
+ Mozilla Bug 513213
+ </a><br>
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer">
+ <select id="select1">
+ <option id="option1">option</option>
+ </select>
+ <select id="select2">
+ <option id="option2">option</option>
+ </select>
+ <select id="select3">
+ <option id="option3">option</option>
+ </select>
+ <select id="select4">
+ <option id="option4">option</option>
+ </select>
+ <select id="select5">
+ <option id="option5">option</option>
+ </select>
+ <select id="select6">
+ <option id="option6">option</option>
+ </select>
+ <select id="select7">
+ <option id="option7">option</option>
+ </select>
+ <select id="select8">
+ <option id="option8">option</option>
+ </select>
+
+ <select id="select9" style="display: none">
+ <option id="option9" style="display: none">testing</option>
+ </select>
+ <select id="select10" style="display: none">
+ <option id="option10" style="display: none">testing</option>
+ </select>
+ <select id="select11" style="display: none"></select>
+ <select id="select12" style="display: none"></select>
+ </div>
+
+ <div id="testContainer2">
+ <div id="t1_parent">
+ <div id="t1_mid1"><div id="t1_child1"></div></div>
+ <div id="t1_mid2"><div id="t1_child2"></div></div>
+ </div>
+ </div>
+
+ <div id="t3">
+ <div role="listbox" id="t3_lb">
+ <div role="option" id="t3_o">opt</div>
+ </div>
+ </div>
+
+ <div id="t4">
+ <div role="listbox" id="t4_lb">
+ <div role="option" id="t4_o1">opt1</div>
+ <div role="option" id="t4_o2">opt2</div>
+ </div>
+ </div>
+
+ <div id="t5">
+ <div role="button" id="t5_b">btn</div>
+ <div role="listbox" id="t5_lb">
+ <div role="option" id="t5_o">opt</div>
+ </div>
+ </div>
+
+ <div id="t6">
+ </div>
+
+ <div id="t7">
+ <div id="t7_moveplace"></div>
+ <div id="t7_c">
+ <div><div id="t7_c_grandchild"></div></div>
+ <div id="t7_c_directchild"></div>
+ </div>
+ </div>
+
+ <div id="t8">
+ <div id="t8_c1"><div id="t8_c1_child"></div></div>
+ <div id="t8_c2">
+ <div id="t8_c2_moved"></div>
+ </div>
+ </div>
+
+ <div id="t9">
+ <div id="t9_c1"><div id="t9_c1_child"></div></div>
+ <div id="t9_c2">
+ <div id="t9_c2_child"></div>
+ <div id="t9_c2_moved"></div>
+ </div>
+ <div id="t9_c3">
+ <div id="t9_c3_moved"></div>
+ </div>
+ </div>
+
+ <div id="t10">
+ <div id="t10_c1"><div id="t10_c1_child"></div></div>
+ <div id="t10_c2">
+ <div id="t10_c2_child"></div>
+ <div id="t10_c2_moved"></div>
+ </div>
+ <div id="t10_c3">
+ <div id="t10_c3_moved"></div>
+ </div>
+ </div>
+
+ <div id="t11">
+ <div id="t11_c1"><div id="t11_c1_child"></div></div>
+ <div id="t11_c2"><div id="t11_c2_child"></div></div>
+ <div id="t11_c3"></div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_contextmenu.html b/accessible/tests/mochitest/events/test_contextmenu.html
new file mode 100644
index 000000000..065e1b50e
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_contextmenu.html
@@ -0,0 +1,139 @@
+<html>
+
+<head>
+ <title>Context menu tests</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function showContextMenu(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_START, getContextMenuNode()),
+ ];
+
+ this.invoke = function showContextMenu_invoke()
+ {
+ synthesizeMouse(this.DOMNode, 4, 4, { type: "contextmenu", button: 2 });
+ }
+
+ this.getID = function showContextMenu_getID()
+ {
+ return "show context menu";
+ }
+ }
+
+ function selectMenuItem()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getFocusedMenuItem)
+ ];
+
+ this.invoke = function selectMenuItem_invoke()
+ {
+ synthesizeKey("VK_DOWN", { });
+ }
+
+ this.getID = function selectMenuItem_getID()
+ {
+ return "select first menuitem";
+ }
+ }
+
+ function closeContextMenu(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_END,
+ getAccessible(getContextMenuNode()))
+ ];
+
+ this.invoke = function closeContextMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeContextMenu_getID()
+ {
+ return "close context menu";
+ }
+ }
+
+ function getContextMenuNode()
+ {
+ return getRootAccessible().DOMDocument.
+ getElementById("contentAreaContextMenu");
+ }
+
+ function getFocusedMenuItem()
+ {
+ var menu = getAccessible(getAccessible(getContextMenuNode()));
+ for (var idx = 0; idx < menu.childCount; idx++) {
+ var item = menu.getChildAt(idx);
+
+ if (hasState(item, STATE_FOCUSED))
+ return getAccessible(item);
+ }
+ return null;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new showContextMenu("input"));
+ gQueue.push(new selectMenuItem());
+ gQueue.push(new closeContextMenu());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=580535"
+ title="Broken accessibility in context menus">
+ Mozilla Bug 580535
+ </a><br>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_descrchange.html b/accessible/tests/mochitest/events/test_descrchange.html
new file mode 100644
index 000000000..d51318532
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_descrchange.html
@@ -0,0 +1,85 @@
+<html>
+
+<head>
+ <title>Accessible description change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function setAttr(aID, aAttr, aValue, aChecker)
+ {
+ this.eventSeq = [ aChecker ];
+ this.invoke = function setAttr_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setAttr("tst1", "aria-describedby", "display",
+ new invokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "title", "title",
+ new unexpectedInvokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+
+ gQueue.push(new setAttr("tst2", "title", "title",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=991969"
+ title="Event not fired when description changes">
+ Bug 991969
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <button id="tst1">btn1</button>
+ <button id="tst2">btn2</button>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_docload.html b/accessible/tests/mochitest/events/test_docload.html
new file mode 100644
index 000000000..a530592ee
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload.html
@@ -0,0 +1,360 @@
+<html>
+
+<head>
+ <title>Accessible events testing for document</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ // Front end stuff sometimes likes to stuff things in the hidden window(s)
+ // in which case there's accessibles for that content.
+ Components.utils.import("resource://gre/modules/Services.jsm");
+
+ // Force the creation of an accessible for the hidden window's document.
+ var doc = Services.appShell.hiddenDOMWindow.document;
+ gAccService.getAccessibleFor(doc);
+
+ // The private hidden window will be lazily created that's why we need to do
+ // it here *before* loading '../events.js' or else we'll have a duplicate
+ // reorder event.
+ var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
+
+ // Force the creation of an accessible for the private hidden window's doc.
+ gAccService.getAccessibleFor(privateDoc);
+ </script>
+
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function changeIframeSrc(aIdentifier, aURL)
+ {
+ this.DOMNode = getNode(aIdentifier);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
+ ];
+
+ this.invoke = function changeIframeSrc_invoke()
+ {
+ this.DOMNode.src = aURL;
+ }
+
+ this.finalCheck = function changeIframeSrc_finalCheck()
+ {
+ var accTree = {
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ role: ROLE_DOCUMENT,
+ name: aURL == "about:" ? "About:" : aURL
+ }
+ ]
+ };
+
+ testAccessibleTree(this.DOMNode, accTree);
+ }
+
+ this.getID = function changeIframeSrc_getID()
+ {
+ return "change iframe src on " + aURL;
+ }
+
+ function getIframeDoc()
+ {
+ return getAccessible(getNode(aIdentifier).contentDocument);
+ }
+ }
+
+ const kHide = 1;
+ const kShow = 2;
+ const kRemove = 3;
+ function morphIFrame(aIdentifier, aAction)
+ {
+ this.DOMNode = getNode(aIdentifier);
+ this.IFrameContainerDOMNode = this.DOMNode.parentNode;
+
+ this.eventSeq = [];
+
+ var checker = null;
+ if (aAction == kShow)
+ checker = new invokerChecker(EVENT_SHOW, this.DOMNode);
+ else
+ checker = new invokerChecker(EVENT_HIDE, this.DOMNode);
+ this.eventSeq.push(checker);
+
+ var reorderChecker =
+ new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode);
+ this.eventSeq.push(reorderChecker);
+
+ this.invoke = function morphIFrame_invoke()
+ {
+ if (aAction == kHide) {
+ this.DOMNode.style.display = "none";
+ } else if (aAction == kShow) {
+ this.DOMNode.style.display = "block";
+ } else {
+ this.IFrameContainerDOMNode.removeChild(this.DOMNode);
+ }
+ }
+
+ this.finalCheck = function morphIFrame_finalCheck()
+ {
+ var accTree = {
+ role: ROLE_SECTION,
+ children: (aAction == kHide || aAction == kRemove) ? [ ] :
+ [
+ {
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ { role: ROLE_DOCUMENT }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree(this.IFrameContainerDOMNode, accTree);
+ }
+
+ this.getID = function morphIFrame_getID()
+ {
+ if (aAction == kRemove)
+ return "remove iframe";
+
+ return "change display style of iframe to " +
+ ((aAction == kHide) ? "none" : "block");
+ }
+ }
+
+ function makeIFrameVisible(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode)
+ ];
+
+ this.invoke = function makeIFrameVisible_invoke()
+ {
+ this.DOMNode.style.visibility = "visible";
+ }
+
+ this.getID = function makeIFrameVisible_getID()
+ {
+ return "The accessible for DOM document loaded before it's shown shouldn't have busy state.";
+ }
+ }
+
+ function openDialogWnd(aURL)
+ {
+ // Get application root accessible.
+ var docAcc = getAccessible(document);
+ while (docAcc) {
+ this.mRootAcc = docAcc;
+ try {
+ docAcc = docAcc.parent;
+ } catch (e) {
+ ok(false, "Can't get parent for " + prettyName(docAcc));
+ throw e;
+ }
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.mRootAcc)
+ ];
+
+ this.invoke = function openDialogWnd_invoke()
+ {
+ this.mDialog = window.openDialog(aURL);
+ }
+
+ this.finalCheck = function openDialogWnd_finalCheck()
+ {
+ this.finalCheckImpl();
+ }
+
+ this.finalCheckImpl = function openDialogWnd_finalCheckImpl()
+ {
+ var accTree = {
+ role: ROLE_APP_ROOT,
+ children: [
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ }
+ ]
+ };
+
+ testAccessibleTree(this.mRootAcc, accTree);
+
+ var dlgDoc = this.mDialog.document;
+ ok(isAccessibleInCache(dlgDoc),
+ "The document accessible for '" + aURL + "' is not in cache!");
+
+ this.mDialog.close();
+
+ // close() is asynchronous.
+ SimpleTest.executeSoon(function() {
+ ok(!isAccessibleInCache(dlgDoc),
+ "The document accessible for '" + aURL + "' is in cache still!");
+ });
+ }
+
+ this.getID = function openDialogWnd_getID()
+ {
+ return "open dialog '" + aURL + "'";
+ }
+ }
+
+ function openWndShutdownDoc()
+ {
+ this.__proto__ =
+ new openDialogWnd("../events/docload_wnd.html");
+
+ var thisObj = this;
+ var docChecker = {
+ type: EVENT_HIDE,
+ get target()
+ {
+ var iframe = this.invoker.mDialog.document.getElementById("iframe");
+ this.invoker.iframeDoc = iframe.contentDocument;
+ return iframe;
+ },
+ get targetDescr()
+ {
+ return "inner iframe of docload_wnd.html document";
+ },
+ invoker: thisObj
+ };
+
+ this.eventSeq.push(docChecker);
+
+ this.finalCheck = function openWndShutdownDoc_finalCheck()
+ {
+ // After timeout after event hide for iframe was handled the document
+ // accessible for iframe's document is in cache still.
+ ok(!isAccessibleInCache(this.iframeDoc),
+ "The document accessible for iframe is in cache still after iframe hide!");
+
+ this.finalCheckImpl();
+
+ // After the window is closed all alive subdocument accessibles should
+ // be shut down.
+ ok(!isAccessibleInCache(this.iframeDoc),
+ "The document accessible for iframe is in cache still!");
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // Debug stuff.
+ // gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeIframeSrc("iframe", "about:"));
+ gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
+ gQueue.push(new morphIFrame("iframe", kHide));
+ gQueue.push(new morphIFrame("iframe", kShow));
+ gQueue.push(new morphIFrame("iframe", kRemove));
+ gQueue.push(new makeIFrameVisible("iframe2"));
+ gQueue.push(new openDialogWnd("about:"));
+ gQueue.push(new openWndShutdownDoc());
+
+ gQueue.onFinish = doLastCallTests;
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ function doLastCallTests()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // makeIFrameVisible() test, part2
+
+ // The document shouldn't have busy state (the DOM document was loaded
+ // before its accessible was created). Do this test lately to make sure
+ // the content of document accessible was created initially, prior to this
+ // the document accessible keeps busy state. The initial creation happens
+ // asynchronously after document creation, there are no events we could
+ // use to catch it.
+ var iframeDoc = getAccessible("iframe2").firstChild;
+ testStates(iframeDoc, 0, 0, STATE_BUSY);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
+ title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
+ Mozilla Bug 420845
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
+ title="Fire event_reorder application root accessible">
+ Mozilla Bug 506206
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
+ title="Reorganize accessible document handling">
+ Mozilla Bug 566103
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
+ title="Shutdown document accessible when presshell goes away">
+ Mozilla Bug 571459
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
+ title="The DOM document loaded before it's shown shouldn't have busy state">
+ Mozilla Bug 658185
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
+ title="Fire document load events on iframes too">
+ Mozilla Bug 754165
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer"><iframe id="iframe"></iframe></div>
+ <div id="testContainer2"><iframe id="iframe2" src="about:" style="visibility: hidden;"></iframe></div>
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_docload.xul b/accessible/tests/mochitest/events/test_docload.xul
new file mode 100644
index 000000000..4b07b0e72
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload.xul
@@ -0,0 +1,243 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Loading Document Events Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker checkers.
+ function stateBusyChecker(aIsEnabled)
+ {
+ this.type = EVENT_STATE_CHANGE;
+ this.__defineGetter__("target", currentTabDocument);
+
+ this.check = function stateBusyChecker_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_BUSY, "Wrong state of statechange event.");
+ is(event.isEnabled, aIsEnabled,
+ "Wrong value of state of statechange event");
+
+ testStates(event.accessible, (aIsEnabled ? STATE_BUSY : 0), 0,
+ (aIsEnabled ? 0 : STATE_BUSY), 0);
+ }
+ }
+
+ function documentReloadChecker(aIsFromUserInput)
+ {
+ this.type = EVENT_DOCUMENT_RELOAD;
+ this.__defineGetter__("target", currentTabDocument);
+
+ this.check = function documentReloadChecker_check(aEvent)
+ {
+ is(aEvent.isFromUserInput, aIsFromUserInput,
+ "Wrong value of isFromUserInput");
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers.
+
+ /**
+ * Load URI.
+ */
+ function loadURIInvoker(aURI)
+ {
+ this.invoke = function loadURIInvoker_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ // We don't expect state change event for busy true since things happen
+ // quickly and it's coalesced.
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function loadURIInvoker_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ /**
+ * Load the document having sub document. No document loading events for
+ * nested document.
+ */
+ function loadNestedDocURIInvoker(aNestedDocURI)
+ {
+ this.__proto__ = new loadURIInvoker(aNestedDocURI);
+
+ // Remove reorder event checker since the event is likely coalesced by
+ // reorder event on Firefox UI (refer to bug 759670 for details).
+ this.eventSeq.shift();
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getNestedDoc),
+ new invokerChecker(EVENT_STATE_CHANGE, getNestedDoc)
+ ];
+
+ function getNestedDoc()
+ {
+ var iframeNodes = currentTabDocument().getElementsByTagName("iframe");
+ return iframeNodes && iframeNodes.length > 0 ?
+ iframeNodes[0].firstChild : null;
+ }
+ }
+
+ /**
+ * Reload the page by F5 (isFromUserInput flag is true).
+ */
+ function userReloadInvoker()
+ {
+ this.invoke = function userReloadInvoker_invoke()
+ {
+ synthesizeKey("VK_F5", {}, browserWindow());
+ }
+
+ this.eventSeq = [
+ new documentReloadChecker(true),
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function userReloadInvoker_getID()
+ {
+ return "user reload page";
+ }
+ }
+
+ /**
+ * Reload the page (isFromUserInput flag is false).
+ */
+ function reloadInvoker()
+ {
+ this.invoke = function reloadInvoker_invoke()
+ {
+ tabBrowser().reload();
+ }
+
+ this.eventSeq = [
+ new documentReloadChecker(false),
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function reloadInvoker_getID()
+ {
+ return "reload page";
+ }
+ }
+
+ /**
+ * Load wrong URI what results in error page loading.
+ */
+ function loadErrorPageInvoker(aURL, aURLDescr)
+ {
+ this.invoke = function loadErrorPageInvoker_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.eventSeq = [
+ // We don't expect state change for busy true, load stopped events since
+ // things happen quickly and it's coalesced.
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function loadErrorPageInvoker_getID()
+ {
+ return "load error page: '" + aURLDescr + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpToConsole = true; // debug
+ //gA11yEventDumpFeature = "parentchain:reorder";
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var dataURL =
+ "data:text/html,<html><body><iframe src='http://example.com'></iframe></body></html>";
+ gQueue.push(new loadNestedDocURIInvoker(dataURL));
+
+ gQueue.push(new loadURIInvoker("about:"));
+ gQueue.push(new userReloadInvoker());
+ gQueue.push(new loadURIInvoker("about:mozilla"));
+ gQueue.push(new reloadInvoker());
+ gQueue.push(new loadErrorPageInvoker("www.wronguri.wronguri",
+ "Server not found"));
+ gQueue.push(new loadErrorPageInvoker("https://nocert.example.com:443",
+ "Untrusted Connection"));
+
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
+ title=" reorganize accessible document handling">
+ Mozilla Bug 566103
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
+ title="Fire document load events on iframes too">
+ Mozilla Bug 754165
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_docload_aria.html b/accessible/tests/mochitest/events/test_docload_aria.html
new file mode 100644
index 000000000..c5f470aee
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload_aria.html
@@ -0,0 +1,83 @@
+<html>
+
+<head>
+ <title>Accessible events testing for ARIA document</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function showARIADialog(aID)
+ {
+ this.dialogNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, this.dialogNode)
+ ];
+
+ this.invoke = function showARIADialog_invoke()
+ {
+ this.dialogNode.style.display = "block";
+ }
+
+ this.getID = function showARIADialog_getID()
+ {
+ return "show ARIA dialog";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // Debug stuff.
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new showARIADialog("dialog"));
+ gQueue.push(new showARIADialog("document"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=759833"
+ title="ARIA documents should fire document loading events">
+ Mozilla Bug 759833
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="dialog" id="dialog" style="display: none;">It's a dialog</div>
+ <div role="document" id="document" style="display: none;">It's a document</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_dragndrop.html b/accessible/tests/mochitest/events/test_dragndrop.html
new file mode 100644
index 000000000..cfba80a46
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_dragndrop.html
@@ -0,0 +1,110 @@
+<html>
+
+<head>
+ <title>Accessible drag and drop event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // aria grabbed invoker
+ function changeGrabbed(aNodeOrID, aGrabValue)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function changeGrabbed_invoke() {
+ if (aGrabValue != undefined) {
+ this.DOMNode.setAttribute("aria-grabbed", aGrabValue);
+ }
+ }
+
+ this.check = function changeGrabbed_check() {
+ testAttrs(aNodeOrID, {"grabbed" : aGrabValue}, true);
+ }
+
+ this.getID = function changeGrabbed_getID() {
+ return prettyName(aNodeOrID) + " aria-grabbed changed";
+ }
+ }
+
+ // aria dropeffect invoker
+ function changeDropeffect(aNodeOrID, aDropeffectValue)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function changeDropeffect_invoke() {
+ if (aDropeffectValue != undefined) {
+ this.DOMNode.setAttribute("aria-dropeffect", aDropeffectValue);
+ }
+ }
+
+ this.check = function changeDropeffect_check() {
+ testAttrs(aNodeOrID, {"dropeffect" : aDropeffectValue}, true);
+ }
+
+ this.getID = function changeDropeffect_getID() {
+ return prettyName(aNodeOrID) + " aria-dropeffect changed";
+ }
+ }
+
+ function doTests()
+ {
+ // Test aria attribute mutation events
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED);
+
+ var id="grabbable";
+ gQueue.push(new changeGrabbed(id, "true"));
+ gQueue.push(new changeGrabbed(id, "false"));
+ todo(false, "uncomment this test when 472142 is fixed.");
+ //gQueue.push(new changeGrabbed(id, "undefined"));
+
+ var id="dropregion";
+ gQueue.push(new changeDropeffect(id, "copy"));
+ gQueue.push(new changeDropeffect(id, "execute"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=510441"
+ title="Add support for nsIAccessibleEvent::OBJECT_ATTRIBUTE_CHANGED">
+ Mozilla Bug 510441
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- ARIA grabbed -->
+ <div id="grabbable" role="button" aria-grabbed="foo">button</div>
+
+ <!-- ARIA dropeffect -->
+ <div id="dropregion" role="region" aria-dropeffect="none">button</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_flush.html b/accessible/tests/mochitest/events/test_flush.html
new file mode 100644
index 000000000..44a9afd94
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_flush.html
@@ -0,0 +1,77 @@
+<html>
+
+<head>
+ <title>Flush delayed events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ SimpleTest.expectAssertions(0, 1);
+
+ var gFocusHandler = {
+ handleEvent: function(aEvent) {
+ switch (this.count) {
+ case 0:
+ is(aEvent.DOMNode, getNode("input1"),
+ "Focus event for input1 was expected!");
+ getAccessible("input2").takeFocus();
+ break;
+
+ case 1:
+ is(aEvent.DOMNode, getNode("input2"),
+ "Focus event for input2 was expected!");
+
+ unregisterA11yEventListener(EVENT_FOCUS, gFocusHandler);
+ SimpleTest.finish();
+ break;
+
+ default:
+ ok(false, "Wrong focus event!");
+ }
+
+ this.count++;
+ },
+
+ count: 0
+ };
+
+ function doTests()
+ {
+ registerA11yEventListener(EVENT_FOCUS, gFocusHandler);
+
+ getAccessible("input1").takeFocus();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=477551"
+ title="DocAccessible::FlushPendingEvents isn't robust">
+ Mozilla Bug 477551
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input1">
+ <input id="input2">
+</body>
+</html>
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 000000000..4cd57fe3b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429547
+-->
+<head>
+ <title>aria-activedescendant focus tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function changeARIAActiveDescendant(aID, aItemID)
+ {
+ this.eventSeq = [
+ new focusChecker(aItemID)
+ ];
+
+ this.invoke = function changeARIAActiveDescendant_invoke()
+ {
+ getNode(aID).setAttribute("aria-activedescendant", aItemID);
+ }
+
+ this.getID = function changeARIAActiveDescendant_getID()
+ {
+ return "change aria-activedescendant on " + aItemID;
+ }
+ }
+
+ function insertItemNFocus(aID, aNewItemID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, aNewItemID),
+ new focusChecker(aNewItemID)
+ ];
+
+ this.invoke = function insertItemNFocus_invoke()
+ {
+ var container = getNode(aID);
+ var itemNode = document.createElement("div");
+ itemNode.setAttribute("id", aNewItemID);
+ itemNode.textContent = aNewItemID;
+ container.appendChild(itemNode);
+
+ container.setAttribute("aria-activedescendant", aNewItemID);
+ }
+
+ this.getID = function insertItemNFocus_getID()
+ {
+ return "insert new node and focus it with ID: " + aNewItemID;
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("item1")));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item2"));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item3"));
+
+ gQueue.push(new synthFocus("combobox_entry", new focusChecker("combobox_entry")));
+ gQueue.push(new changeARIAActiveDescendant("combobox", "combobox_option2"));
+
+ todo(false, "No focus for inserted element, bug 687011");
+ //gQueue.push(new insertItemNFocus("listbox", "item4"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
+ title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
+ Mozilla Bug 429547
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761102"
+ title="Focus may be missed when ARIA active-descendant is changed on active composite widget">
+ Mozilla Bug 761102
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="listbox" aria-activedescendant="item1" id="listbox" tabindex="1"
+ aria-owns="item3">
+ <div role="listitem" id="item1">item1</div>
+ <div role="listitem" id="item2">item2</div>
+ </div>
+ <div role="listitem" id="item3">item3</div>
+
+ <div role="combobox" id="combobox">
+ <input id="combobox_entry">
+ <ul>
+ <li role="option" id="combobox_option1">option1</li>
+ <li role="option" id="combobox_option2">option2</li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_autocomplete.xul b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
new file mode 100644
index 000000000..2a32a6587
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -0,0 +1,518 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<!-- Firefox searchbar -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+ type="text/css"?>
+<!-- SeaMonkey searchbar -->
+<?xml-stylesheet href="chrome://navigator/content/navigator.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript"
+ src="../autocomplete.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Hacky stuffs
+
+ // This is the hacks needed to use a searchbar without browser.js.
+ function getBrowser()
+ {
+ return {
+ mCurrentBrowser: { engines: new Array() }
+ };
+ }
+
+ var BrowserSearch = {
+ updateOpenSearchBadge: function() {}
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadFormAutoComplete(aIFrameID)
+ {
+ this.iframeNode = getNode(aIFrameID);
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function loadFormAutoComplete_invoke()
+ {
+ var url = "data:text/html,<html><body><form id='form'>" +
+ "<input id='input' name='a11ytest-formautocomplete'>" +
+ "</form></body></html>";
+ this.iframeNode.setAttribute("src", url);
+ }
+
+ this.getID = function loadFormAutoComplete_getID()
+ {
+ return "load form autocomplete page";
+ }
+ }
+
+ function initFormAutoCompleteBy(aIFrameID, aAutoCompleteValue)
+ {
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function initFormAutoCompleteBy_invoke()
+ {
+ var iframeDOMDoc = getIFrameDOMDoc(aIFrameID);
+
+ var inputNode = iframeDOMDoc.getElementById("input");
+ inputNode.value = aAutoCompleteValue;
+ var formNode = iframeDOMDoc.getElementById("form");
+ formNode.submit();
+ }
+
+ this.getID = function initFormAutoCompleteBy_getID()
+ {
+ return "init form autocomplete by '" + aAutoCompleteValue + "'";
+ }
+ }
+
+ function loadHTML5ListAutoComplete(aIFrameID)
+ {
+ this.iframeNode = getNode(aIFrameID);
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function loadHTML5ListAutoComplete_invoke()
+ {
+ var url = "data:text/html,<html><body>" +
+ "<datalist id='cities'><option>hello</option><option>hi</option></datalist>" +
+ "<input id='input' list='cities'>" +
+ "</body></html>";
+ this.iframeNode.setAttribute("src", url);
+ }
+
+ this.getID = function loadHTML5ListAutoComplete_getID()
+ {
+ return "load HTML5 list autocomplete page";
+ }
+ }
+
+ function removeChar(aID, aCheckerOrEventSeq)
+ {
+ this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+ this.invoke = function removeChar_invoke()
+ {
+ synthesizeKey("VK_LEFT", { shiftKey: true });
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeChar_getID()
+ {
+ return "remove char on " + prettyName(aID);
+ }
+ }
+
+ function replaceOnChar(aID, aChar, aCheckerOrEventSeq)
+ {
+ this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+ this.invoke = function replaceOnChar_invoke()
+ {
+ this.DOMNode.select();
+ synthesizeKey(aChar, {});
+ }
+
+ this.getID = function replaceOnChar_getID()
+ {
+ return "replace on char '" + aChar + "' for" + prettyName(aID);
+ }
+ }
+
+ function focusOnMouseOver(aIDFunc, aIDFuncArg)
+ {
+ this.eventSeq = [ new focusChecker(aIDFunc, aIDFuncArg) ];
+
+ this.invoke = function focusOnMouseOver_invoke()
+ {
+ this.id = aIDFunc.call(null, aIDFuncArg);
+ this.node = getNode(this.id);
+ this.window = this.node.ownerDocument.defaultView;
+
+ if (this.node.localName == "tree") {
+ var tree = getAccessible(this.node);
+ var accessible = getAccessible(this.id);
+ if (tree != accessible) {
+ var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+ accessible.getBounds(itemX, itemY, {}, {});
+ tree.getBounds(treeX, treeY, {}, {});
+ this.x = itemX.value - treeX.value;
+ this.y = itemY.value - treeY.value;
+ }
+ }
+
+ // Generate mouse move events in timeouts until autocomplete popup list
+ // doesn't have it, the reason is do that because autocomplete popup
+ // ignores mousemove events firing in too short range.
+ synthesizeMouse(this.node, this.x, this.y, { type: "mousemove" });
+ this.doMouseMoveFlood(this);
+ }
+
+ this.finalCheck = function focusOnMouseOver_getID()
+ {
+ this.isFlooding = false;
+ }
+
+ this.getID = function focusOnMouseOver_getID()
+ {
+ return "mouse over on " + prettyName(aIDFunc.call(null, aIDFuncArg));
+ }
+
+ this.doMouseMoveFlood = function focusOnMouseOver_doMouseMoveFlood(aThis)
+ {
+ synthesizeMouse(aThis.node, aThis.x + 1, aThis.y + 1,
+ { type: "mousemove" }, aThis.window);
+
+ if (aThis.isFlooding)
+ aThis.window.setTimeout(aThis.doMouseMoveFlood, 0, aThis);
+ }
+
+ this.id = null;
+ this.node = null;
+ this.window = null;
+
+ this.isFlooding = true;
+ this.x = 1;
+ this.y = 1;
+ }
+
+ function selectByClick(aIDFunc, aIDFuncArg,
+ aFocusTargetFunc, aFocusTargetFuncArg)
+ {
+ this.eventSeq = [ new focusChecker(aFocusTargetFunc, aFocusTargetFuncArg) ];
+
+ this.invoke = function selectByClick_invoke()
+ {
+ var id = aIDFunc.call(null, aIDFuncArg);
+ var node = getNode(id);
+ var targetWindow = node.ownerDocument.defaultView;
+
+ var x = 0, y = 0;
+ if (node.localName == "tree") {
+ var tree = getAccessible(node);
+ var accessible = getAccessible(id);
+ if (tree != accessible) {
+ var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+ accessible.getBounds(itemX, itemY, {}, {});
+ tree.getBounds(treeX, treeY, {}, {});
+ x = itemX.value - treeX.value;
+ y = itemY.value - treeY.value;
+ }
+ }
+
+ synthesizeMouseAtCenter(node, {}, targetWindow);
+ }
+
+ this.getID = function selectByClick_getID()
+ {
+ return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Target getters
+
+ function getItem(aItemObj)
+ {
+ var autocomplete = aItemObj.autocomplete;
+ var autocompleteNode = aItemObj.autocompleteNode;
+
+ // XUL searchbar
+ if (autocompleteNode.localName == "searchbar") {
+ var popupNode = autocompleteNode._popup;
+ if (popupNode) {
+ var list = getAccessible(popupNode);
+ return list.getChildAt(aItemObj.index);
+ }
+ }
+
+ // XUL autocomplete
+ var popupNode = autocompleteNode.popup;
+ if (!popupNode) {
+ // HTML form autocomplete
+ var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.nsIAutoCompleteController);
+ popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
+ }
+
+ if (popupNode) {
+ if ("richlistbox" in popupNode) {
+ var list = getAccessible(popupNode.richlistbox);
+ return list.getChildAt(aItemObj.index);
+ }
+
+ var list = getAccessible(popupNode.tree);
+ return list.getChildAt(aItemObj.index + 1);
+ }
+ }
+
+ function getTextEntry(aID)
+ {
+ // For form autocompletes the autocomplete widget and text entry widget
+ // is the same widget, for XUL autocompletes the text entry is a first
+ // child.
+ var localName = getNode(aID).localName;
+
+ // XUL autocomplete
+ if (localName == "textbox")
+ return getAccessible(aID).firstChild;
+
+ // HTML form autocomplete
+ if (localName == "input")
+ return getAccessible(aID);
+
+ // XUL searchbar
+ if (localName == "searchbar")
+ return getAccessible(getNode(aID).textbox.inputField);
+
+ return null;
+ }
+
+ function itemObj(aID, aIdx)
+ {
+ this.autocompleteNode = getNode(aID);
+
+ this.autocomplete = this.autocompleteNode.localName == "searchbar" ?
+ getAccessible(this.autocompleteNode.textbox) :
+ getAccessible(this.autocompleteNode);
+
+ this.index = aIdx;
+ }
+
+ function getIFrameDOMDoc(aIFrameID)
+ {
+ return getNode(aIFrameID).contentDocument;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test helpers
+
+ function queueAutoCompleteTests(aID)
+ {
+ // focus autocomplete text entry
+ gQueue.push(new synthFocus(aID, new focusChecker(getTextEntry, aID)));
+
+ // open autocomplete popup
+ gQueue.push(new synthDownKey(aID, new nofocusChecker()));
+
+ // select second option ('hi' option), focus on it
+ gQueue.push(new synthUpKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 1))));
+
+ // choose selected option, focus on text entry
+ gQueue.push(new synthEnterKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // remove char, autocomplete popup appears
+ gQueue.push(new removeChar(aID, new nofocusChecker()));
+
+ // select first option ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // mouse move on second option ('hi' option), focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 1)));
+
+ // autocomplete popup updated (no selected item), focus on textentry
+ gQueue.push(new synthKey(aID, "e", null, new focusChecker(getTextEntry, aID)));
+
+ // select first option ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // popup gets hidden, focus on textentry
+ gQueue.push(new synthRightKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // popup gets open, no focus
+ gQueue.push(new synthOpenComboboxKey(aID, new nofocusChecker()));
+
+ // select first option again ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // no option is selected, focus on text entry
+ gQueue.push(new synthUpKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // close popup, no focus
+ gQueue.push(new synthEscapeKey(aID, new nofocusChecker()));
+
+ // autocomplete popup appears (no selected item), focus stays on textentry
+ gQueue.push(new replaceOnChar(aID, "h", new nofocusChecker()));
+
+ // mouse move on first option ('hello' option), focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 0)));
+
+ // click first option ('hello' option), popup closes, focus on text entry
+ gQueue.push(new selectByClick(getItem, new itemObj(aID, 0), getTextEntry, aID));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gInitQueue = null;
+ function initTests()
+ {
+ if (SEAMONKEY || MAC) {
+ todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237), and on Mac (bug 746177)");
+ shutdownAutoComplete();
+ SimpleTest.finish();
+ return;
+ }
+
+ gInitQueue = new eventQueue();
+ gInitQueue.push(new loadFormAutoComplete("iframe"));
+ gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
+ gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
+ gInitQueue.push(new loadHTML5ListAutoComplete("iframe2"));
+ gInitQueue.onFinish = function initQueue_onFinish()
+ {
+ SimpleTest.executeSoon(doTests);
+ return DO_NOT_FINISH_TEST;
+ }
+ gInitQueue.invoke();
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ ////////////////////////////////////////////////////////////////////////////
+ // tree popup autocomplete tests
+ queueAutoCompleteTests("autocomplete");
+
+ ////////////////////////////////////////////////////////////////////////////
+ // richlistbox popup autocomplete tests
+ queueAutoCompleteTests("richautocomplete");
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML form autocomplete tests
+ queueAutoCompleteTests(getIFrameDOMDoc("iframe").getElementById("input"));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML5 list autocomplete tests
+ queueAutoCompleteTests(getIFrameDOMDoc("iframe2").getElementById("input"));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // searchbar tests
+
+ // focus searchbar, focus on text entry
+ gQueue.push(new synthFocus("searchbar",
+ new focusChecker(getTextEntry, "searchbar")));
+ // open search engine popup, no focus
+ gQueue.push(new synthOpenComboboxKey("searchbar", new nofocusChecker()));
+ // select first item, focus on it
+ gQueue.push(new synthDownKey("searchbar",
+ new focusChecker(getItem, new itemObj("searchbar", 0))));
+ // mouse over on second item, focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj("searchbar", 1)));
+ // press enter key, focus on text entry
+ gQueue.push(new synthEnterKey("searchbar",
+ new focusChecker(getTextEntry, "searchbar")));
+ // click on search button, open popup, focus goes to document
+ var searchBtn = getAccessible(getNode("searchbar").searchButton);
+ gQueue.push(new synthClick(searchBtn, new focusChecker(document)));
+ // select first item, focus on it
+ gQueue.push(new synthDownKey("searchbar",
+ new focusChecker(getItem, new itemObj("searchbar", 0))));
+ // close popup, focus goes on document
+ gQueue.push(new synthEscapeKey("searchbar", new focusChecker(document)));
+
+ gQueue.onFinish = function()
+ {
+ // unregister 'test-a11y-search' autocomplete search
+ shutdownAutoComplete();
+ }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Register 'test-a11y-search' autocomplete search.
+ // XPFE AutoComplete needs to register early.
+ initAutoComplete([ "hello", "hi" ],
+ [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
+ addA11yLoadEvent(initTests);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=383759"
+ title="Focus event inconsistent for search box autocomplete">
+ Mozilla Bug 383759
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559766"
+ title="Add accessibility support for @list on HTML input and for HTML datalist">
+ Mozilla Bug 559766
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="autocomplete" type="autocomplete"
+ autocompletesearch="test-a11y-search"/>
+
+ <textbox id="richautocomplete" type="autocomplete"
+ autocompletesearch="test-a11y-search"
+ autocompletepopup="richpopup"/>
+ <panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
+
+ <iframe id="iframe"/>
+
+ <iframe id="iframe2"/>
+
+ <searchbar id="searchbar"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_browserui.xul b/accessible/tests/mochitest/events/test_focus_browserui.xul
new file mode 100644
index 000000000..bd621ebf2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_browserui.xul
@@ -0,0 +1,149 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Loading Document Events Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function inputInDocument()
+ {
+ var tabdoc = currentTabDocument();
+ return tabdoc.getElementById("input");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadURI(aURI)
+ {
+ this.invoke = function loadURI_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ new focusChecker(currentTabDocument)
+ ];
+
+ this.getID = function loadURI_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ function goBack()
+ {
+ this.invoke = function goBack_invoke()
+ {
+ tabBrowser().goBack();
+ }
+
+ this.eventSeq = [
+ new focusChecker(inputInDocument)
+ ];
+
+ this.getID = function goBack_getID()
+ {
+ return "go back one page in history ";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Testing
+
+ var gInputDocURI = "data:text/html,<html><input id='input'></html>";
+ var gButtonDocURI = "data:text/html,<html><input id='input' type='button' value='button'></html>";
+
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var tabDocument = currentTabDocument();
+ var input = inputInDocument();
+
+ // move focus to input inside tab document
+ gQueue.push(new synthTab(tabDocument, new focusChecker(input),
+ browserWindow()));
+
+ // open new url, focus moves to new document
+ gQueue.push(new loadURI(gButtonDocURI));
+
+ // back one page in history, moves moves on input of tab document
+ gQueue.push(new goBack());
+
+ // open new tab, focus moves to urlbar
+ gQueue.push(new synthKey(tabDocument, "t", { accelKey: true, window: browserWindow() },
+ new focusChecker(urlbarInput)));
+
+ // close open tab, focus goes on input of tab document
+ gQueue.push(new synthKey(tabDocument, "w", { accelKey: true, window: browserWindow() },
+ new focusChecker(inputInDocument)));
+
+ gQueue.onFinish = function()
+ {
+ closeBrowserWindow();
+ }
+ gQueue.invoke();
+ }
+
+ if (navigator.oscpu.startsWith("Windows NT 6.1") || navigator.oscpu.startsWith("Windows NT 6.2")) {
+ todo(false, "fix the leak!");
+ } else {
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, gInputDocURI);
+ }
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=644452"
+ title="Focus not set when switching to cached document with back or forward if anything other than the document was last focused">
+ Mozilla Bug 644452
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=665412"
+ title="Broken focus when returning to editable text field after closing a tab while focused in the Navigation toolbar">
+ Mozilla Bug 665412
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_canvas.html b/accessible/tests/mochitest/events/test_focus_canvas.html
new file mode 100644
index 000000000..dcccb08e0
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_canvas.html
@@ -0,0 +1,61 @@
+<html>
+
+<head>
+ <title>Accessible focus testing in canvas subdom</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthTab("button", new focusChecker("textbox")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="Expose content in Canvas element"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912">
+ Mozilla Bug 495912
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <canvas>
+ <input id="button" type="button">
+ <input id="textbox">
+ </canvas>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_contextmenu.xul b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
new file mode 100644
index 000000000..0cf62b912
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Context nenu focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var winLowerThanVista = navigator.platform.indexOf("Win") == 0;
+ if (winLowerThanVista) {
+ var version = Components.classes["@mozilla.org/system-info;1"]
+ .getService(Components.interfaces.nsIPropertyBag2)
+ .getProperty("version");
+ version = parseFloat(version);
+ winLowerThanVista = !(version >= 6.0);
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ // bug 746183 - Whole file times out on OS X
+ if (MAC || winLowerThanVista) {
+ todo(false, "Reenable on mac after fixing bug 746183!");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthContextMenu("button",
+ new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+ gQueue.push(new synthEscapeKey("contextmenu", new focusChecker("button")));
+
+ gQueue.push(new synthContextMenu("button",
+ new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+ gQueue.push(new synthDownKey("contextmenu", new focusChecker("item1")));
+ gQueue.push(new synthDownKey("item1", new focusChecker("item2")));
+ gQueue.push(new synthRightKey("item2", new focusChecker("item2.1")));
+ if (WIN) {
+ todo(false, "synthEscapeKey for item2.1 and item2 disabled due to bug 691580");
+ } else {
+ gQueue.push(new synthEscapeKey("item2.1", new focusChecker("item2")));
+ gQueue.push(new synthEscapeKey("item2", new focusChecker("button")));
+ }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <button id="button" context="contextmenu" label="button"/>
+ <menupopup id="contextmenu">
+ <menuitem id="item1" label="item1"/>
+ <menu id="item2" label="item2">
+ <menupopup>
+ <menuitem id="item2.1" label="item2.1"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_controls.html b/accessible/tests/mochitest/events/test_focus_controls.html
new file mode 100644
index 000000000..a18832a8f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_controls.html
@@ -0,0 +1,75 @@
+<html>
+
+<head>
+ <title>Accessible focus testing on HTML controls</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue(EVENT_FOCUS);
+
+ gQueue.push(new synthFocus("textbox"));
+ gQueue.push(new synthFocus("textarea"));
+ gQueue.push(new synthFocus("button1"));
+ gQueue.push(new synthFocus("button2"));
+ gQueue.push(new synthFocus("checkbox"));
+ gQueue.push(new synthFocus("radio1"));
+ gQueue.push(new synthDownKey("radio1", new focusChecker("radio2")));
+
+ // no focus events for checkbox or radio inputs when they are checked
+ // programmatically
+ gQueue.push(new changeCurrentItem("checkbox"));
+ gQueue.push(new changeCurrentItem("radio1"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox">
+ <textarea id="textarea"></textarea>
+
+ <input id="button1" type="button" value="button">
+ <button id="button2">button</button>
+ <input id="checkbox" type="checkbox">
+ <input id="radio1" type="radio" name="radiogroup">
+ <input id="radio2" type="radio" name="radiogroup">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_dialog.html b/accessible/tests/mochitest/events/test_focus_dialog.html
new file mode 100644
index 000000000..9d88b0cb4
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_dialog.html
@@ -0,0 +1,164 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function openCloseDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(getNode(aID))
+ ];
+
+ this.invoke = function openCloseDialog_invoke()
+ {
+ var wnd = window.open("focus.html");
+ wnd.close();
+ }
+
+ this.getID = function openCloseDialog_getID()
+ {
+ return "Open close dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ var gDialogWnd = null;
+ function getDialogDocument()
+ {
+ return gDialogWnd.document;
+ }
+
+ function openDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(getDialogDocument)
+ ];
+
+ this.invoke = function openDialog_invoke()
+ {
+ gDialogWnd = window.open("focus.html");
+ }
+
+ this.getID = function openDialog_getID()
+ {
+ return "Open dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ function closeDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(aID)
+ ];
+
+ this.invoke = function closeDialog_invoke()
+ {
+ gDialogWnd.close();
+ }
+
+ this.getID = function closeDialog_getID()
+ {
+ return "Close dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ function showNFocusAlertDialog()
+ {
+ this.ID = "alertdialog";
+ this.DOMNode = getNode(this.ID);
+
+ this.invoke = function showNFocusAlertDialog_invoke()
+ {
+ document.getElementById(this.ID).style.display = 'block';
+ document.getElementById(this.ID).focus();
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.DOMNode),
+ new focusChecker(this.DOMNode)
+ ];
+
+ this.getID = function showNFocusAlertDialog_getID()
+ {
+ return "Show and focus alert dialog " + prettyName(this.ID);
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue(EVENT_FOCUS);
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new openDialog("button"));
+ gQueue.push(new closeDialog("button"));
+
+ var frameNode = getNode("editabledoc");
+ gQueue.push(new synthFocusOnFrame(frameNode));
+ gQueue.push(new openCloseDialog(frameNode.contentDocument));
+
+ gQueue.push(new showNFocusAlertDialog());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=551679"
+ title="focus is not fired for focused document when switching between windows">
+ Mozilla Bug 551679
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
+ title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
+ Mozilla Bug 580464
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <button id="button">button</button>
+ <iframe id="editabledoc" src="focus.html"></iframe>
+
+ <div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
+ <div id="title2">Blah blah</div>
+ <div id="desc2">Woof woof woof.</div>
+ <button>Close</button>
+ </div>
+
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_doc.html b/accessible/tests/mochitest/events/test_focus_doc.html
new file mode 100644
index 000000000..bd4934d84
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_doc.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>Accessible document focus event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ var gQueue = null;
+
+ //var gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ // setup
+ var frameDoc = document.getElementById("iframe").contentDocument;
+ frameDoc.designMode = "on";
+ var frameDocAcc = getAccessible(frameDoc, [nsIAccessibleDocument]);
+ var buttonAcc = getAccessible("b1");
+
+ var frame2Doc = document.getElementById("iframe2").contentDocument;
+ var frame2Input = frame2Doc.getElementById("input");
+ var frame2DocAcc = getAccessible(frame2Doc);
+ var frame2InputAcc = getAccessible(frame2Input);
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ // try to give focus to contentEditable frame twice to cover bug 512059
+ gQueue.push(new synthFocus(buttonAcc));
+ gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+ gQueue.push(new synthFocus(buttonAcc));
+ gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+
+ // focus on not editable document
+ gQueue.push(new synthFocus(frame2InputAcc));
+ gQueue.push(new synthShiftTab(frame2DocAcc, new focusChecker(frame2DocAcc)));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512058"
+ title="Can't set focus to designMode document via accessibility APIs">
+ Mozilla Bug 512058
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512059"
+ title="Accessibility focus event never fired for designMode document after the first focus">
+ Mozilla Bug 512059
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=618046"
+ title="No focus change event when Shift+Tab at top of screen">
+ Mozilla Bug 618046
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="eventdump"></div>
+
+ <div id="testContainer">
+ <button id="b1">a button</button>
+ <iframe id="iframe" src="about:blank"></iframe>
+ <button id="b2">a button</button>
+ <iframe id="iframe2" src="data:text/html,<html><input id='input'></html>"></iframe>
+ </div>
+</body>
+</html>
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 000000000..e881a5a4f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_general.html
@@ -0,0 +1,179 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function focusElmWhileSubdocIsFocused(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function focusElmWhileSubdocIsFocused_invoke()
+ {
+ this.DOMNode.focus();
+ }
+
+ this.eventSeq = [
+ new focusChecker(this.DOMNode)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
+ ];
+
+ this.getID = function focusElmWhileSubdocIsFocused_getID()
+ {
+ return "Focus element while subdocument is focused " + prettyName(aID);
+ }
+ }
+
+ function imageMapChecker(aID)
+ {
+ var node = getNode(aID);
+ this.type = EVENT_FOCUS;
+ this.match = function imageMapChecker_match(aEvent)
+ {
+ return aEvent.DOMNode == node;
+ }
+ }
+
+ function topMenuChecker()
+ {
+ this.type = EVENT_FOCUS;
+ this.match = function topMenuChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_PARENT_MENUITEM;
+ }
+ }
+
+ function contextMenuChecker()
+ {
+ this.type = EVENT_MENUPOPUP_START;
+ this.match = function contextMenuChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_MENUPOPUP;
+ }
+ }
+
+ function focusContextMenuItemChecker()
+ {
+ this.__proto__ = new focusChecker();
+
+ this.match = function focusContextMenuItemChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_MENUITEM;
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ var frameDoc = document.getElementById("iframe").contentDocument;
+
+ var editableDoc = document.getElementById('editabledoc').contentDocument;
+ editableDoc.designMode = 'on';
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("editablearea"));
+ gQueue.push(new synthFocus("navarea"));
+ gQueue.push(new synthTab("navarea", new focusChecker(frameDoc)));
+ gQueue.push(new focusElmWhileSubdocIsFocused("link"));
+
+ gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
+ if (WIN || LINUX) {
+ // Alt key is used to active menubar and focus menu item on Windows,
+ // other platforms requires setting a ui.key.menuAccessKeyFocuses
+ // preference.
+ gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
+ gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
+ }
+ gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
+ gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
+ gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
+ if (SEAMONKEY) {
+ todo(false, "shift tab from editable document fails on (Windows) SeaMonkey! (Bug 718235)");
+ } else {
+ if (LINUX || MAC)
+ todo(false, "shift tab from editable document fails on linux and Mac, bug 746519!");
+ else
+ gQueue.push(new synthShiftTab("link", new focusChecker("link")));
+ } // ! SEAMONKEY
+
+ gQueue.push(new synthFocus("a", new imageMapChecker("a")));
+ gQueue.push(new synthFocus("b", new imageMapChecker("b")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=352220"
+ title="Inconsistent focus events when returning to a document frame">
+ Mozilla Bug 352220
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=550338"
+ title="Broken focus when returning to editable documents from menus">
+ Mozilla Bug 550338
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=961696"
+ title="Accessible object:state-changed:focused events for imagemap links are broken">
+ Mozilla Bug 961696
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="editablearea" contentEditable="true">editable area</div>
+ <div id="navarea" tabindex="0">navigable area</div>
+ <iframe id="iframe" src="data:text/html,<html></html>"></iframe>
+ <a id="link" href="">link</a>
+ <iframe id="editabledoc" src="about:blank"></iframe>
+
+ <map name="atoz_map">
+ <area id="a" coords="0,0,13,14" shape="rect">
+ <area id="b" coords="17,0,30,14" shape="rect">
+ </map>
+ <img width="447" height="15" usemap="#atoz_map" src="../letters.gif">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_general.xul b/accessible/tests/mochitest/events/test_focus_general.xul
new file mode 100644
index 000000000..f72834f39
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_general.xul
@@ -0,0 +1,179 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ function getColorBtn(aBtnObj)
+ {
+ var colorpicker = aBtnObj.colorpicker;
+ var container = colorpicker.firstChild;
+ var btn = container.getChildAt(aBtnObj.btnIndex);
+ return btn;
+ }
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("textbox",
+ new focusChecker(getNode("textbox").inputField)));
+ gQueue.push(new synthFocus("textbox_multiline",
+ new focusChecker(getNode("textbox_multiline").inputField)));
+ gQueue.push(new synthFocus("scale"));
+ gQueue.push(new synthFocusOnFrame("editabledoc"));
+ gQueue.push(new synthFocus("radioclothes",
+ new focusChecker("radiosweater")));
+ gQueue.push(new synthDownKey("radiosweater",
+ new focusChecker("radiojacket")));
+ gQueue.push(new synthFocus("checkbox"));
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthFocus("checkbutton"));
+ gQueue.push(new synthFocus("radiobutton"));
+
+ // focus menubutton
+ gQueue.push(new synthFocus("menubutton"));
+ // click menubutton, open popup, focus stays on menu button
+ gQueue.push(new synthClick("menubutton", new nofocusChecker()));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("menubutton", new focusChecker("mb_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("mb_item1", new focusChecker("menubutton")));
+ // press enter to open popup, focus stays on menubutton
+ gQueue.push(new synthEnterKey("menubutton", new nofocusChecker()));
+ // select second menu item ("item 2"), focus on menu item
+ gQueue.push(new synthUpKey("menubutton", new focusChecker("mb_item2")));
+
+ // clicking on button having associated popup doesn't change a focus
+ gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("popupbutton", new focusChecker("bp_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("bp_item1", new focusChecker("menubutton")));
+ // show popup again for the next test
+ gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+
+if (!MAC) {
+ // click menubutton of the 'menubutton' button while popup of button open.
+ gQueue.push(new synthClick("mbb", new focusChecker("mbb"), { where: "right" }));
+ // close popup, focus stays on menubutton, fire focus event
+ gQueue.push(new synthEscapeKey("mbb", new focusChecker("mbb")));
+ // click menubutton, open popup, focus stays on menubutton
+ gQueue.push(new synthClick("mbb", new nofocusChecker(), { where: "right" }));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("mbb", new focusChecker("mbb_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("mbb_item1", new focusChecker("mbb")));
+ // open popup, focus stays on menubutton
+ gQueue.push(new synthOpenComboboxKey("mbb", new nofocusChecker()));
+ // select second menu item ("item 2"), focus on menu item
+ gQueue.push(new synthUpKey("menubutton", new focusChecker("mbb_item2")));
+ // click on menu item of menubutton menu, focus menubutton
+ gQueue.push(new synthClick("mbb_item2", new focusChecker("mbb")));
+} else {
+ todo(false, "mbb tests time out on OS X, fix bug 746970 and reenable!");
+}
+
+ // focus colorpicker button
+ gQueue.push(new synthFocus("colorpicker"));
+ // click on button, open popup, focus goes to current color button
+ var btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 0 };
+ var checker = new focusChecker(getColorBtn, btnObj);
+ gQueue.push(new synthClick("colorpicker", checker));
+ // select sibling color button, focus on it
+ btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 1 };
+ var checker = new focusChecker(getColorBtn, btnObj);
+ gQueue.push(new synthRightKey("colorpicker", checker));
+ // choose selected color button, close popup, focus on colorpicker button
+ gQueue.push(new synthEnterKey("colorpicker", new focusChecker("colorpicker")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=492518"
+ title="xul:slider accessible of xul:scale is accessible illegally">
+ Mozilla Bug 492518
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+ title=" fire focus event on document accessible whenever the root or body element is focused">
+ Mozilla Bug 552368
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="textbox" value="hello"/>
+ <textbox id="textbox_multiline" multiline="true" value="hello"/>
+ <scale id="scale" min="0" max="9" value="5"/>
+ <iframe id="editabledoc" src="focus.html"/>
+ <radiogroup id="radioclothes">
+ <radio id="radiosweater" label="radiosweater"/>
+ <radio id="radiocap" label="radiocap" disabled="true"/>
+ <radio id="radiojacket" label="radiojacket"/>
+ </radiogroup>
+ <checkbox id="checkbox" label="checkbox"/>
+ <button id="button" label="button"/>
+ <button id="checkbutton" type="checkbox" label="checkbutton"/>
+ <button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
+
+ <button id="menubutton" type="menu" label="menubutton">
+ <menupopup>
+ <menuitem id="mb_item1" label="item1"/>
+ <menuitem id="mb_item2" label="item2"/>
+ </menupopup>
+ </button>
+ <button id="mbb" type="menu-button" label="menubutton button">
+ <menupopup>
+ <menuitem id="mbb_item1" label="item1"/>
+ <menuitem id="mbb_item2" label="item2"/>
+ </menupopup>
+ </button>
+
+ <colorpicker id="colorpicker" type="button" label="color picker"
+ color="#FFFFFF"/>
+
+ <popupset>
+ <menupopup id="backpopup" position="after_start">
+ <menuitem id="bp_item1" label="Page 1"/>
+ <menuitem id="bp_item2" label="Page 2"/>
+ </menupopup>
+ </popupset>
+ <button id="popupbutton" label="Pop Me Up" popup="backpopup"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_listcontrols.xul b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
new file mode 100644
index 000000000..db45048e2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("lb_item1")));
+ gQueue.push(new synthDownKey("lb_item1", new focusChecker("lb_item2")));
+ gQueue.push(new synthTab("lb_item2", new focusChecker("mslb_item1")));
+ gQueue.push(new synthDownKey("mslb_item1", new focusChecker("mslb_item2"), { shiftKey: true }));
+ gQueue.push(new synthTab("mslb_item2", new focusChecker("emptylistbox")));
+ gQueue.push(new synthFocus("mcolumnlistbox", new focusChecker("mclb_item1")));
+ gQueue.push(new synthDownKey("mclb_item1", new focusChecker("mclb_item2")));
+ gQueue.push(new synthFocus("headerlistbox", new focusChecker("hlb_item1")));
+ gQueue.push(new synthDownKey("hlb_item1", new focusChecker("hlb_item2")));
+
+ gQueue.push(new synthFocus("richlistbox", new focusChecker("rlb_item1")));
+ gQueue.push(new synthDownKey("rlb_item1", new focusChecker("rlb_item2")));
+ gQueue.push(new synthFocus("multiselrichlistbox", new focusChecker("msrlb_item1")));
+ gQueue.push(new synthDownKey("msrlb_item1", new focusChecker("msrlb_item2"), { shiftKey: true }));
+ gQueue.push(new synthFocus("emptyrichlistbox", new focusChecker("emptyrichlistbox")));
+
+ gQueue.push(new synthFocus("menulist"));
+ gQueue.push(new synthClick("menulist", new focusChecker("ml_tangerine")));
+ gQueue.push(new synthDownKey("ml_tangerine", new focusChecker("ml_marmalade")));
+ gQueue.push(new synthEscapeKey("ml_marmalade", new focusChecker("menulist")));
+if (!MAC) {
+ // On Windows, items get selected during navigation.
+ let expectedItem = WIN ? "ml_tangerine" : "ml_marmalade";
+ gQueue.push(new synthDownKey("menulist", new nofocusChecker(expectedItem)));
+ gQueue.push(new synthOpenComboboxKey("menulist", new focusChecker(expectedItem)));
+ gQueue.push(new synthEnterKey(expectedItem, new focusChecker("menulist")));
+} else {
+ todo(false, "Bug 746531 - timeouts of last three menulist tests on OS X");
+}
+
+ var textentry = getAccessible("emenulist").firstChild;
+ gQueue.push(new synthFocus("emenulist", new focusChecker(textentry)));
+ gQueue.push(new synthDownKey(textentry, new nofocusChecker("eml_tangerine")));
+ gQueue.push(new synthUpKey(textentry, new focusChecker("eml_marmalade")));
+ gQueue.push(new synthEnterKey("eml_marmalade", new focusChecker(textentry)));
+ gQueue.push(new synthOpenComboboxKey("emenulist", new focusChecker("eml_marmalade")));
+ gQueue.push(new synthEscapeKey("eml_marmalade", new focusChecker(textentry)));
+
+ // no focus events for unfocused list controls when current item is
+ // changed.
+ gQueue.push(new synthFocus("emptylistbox"));
+
+ gQueue.push(new changeCurrentItem("listbox", "lb_item1"));
+ gQueue.push(new changeCurrentItem("richlistbox", "rlb_item1"));
+if (!MAC) {
+ gQueue.push(new changeCurrentItem("menulist", WIN ? "ml_marmalade" : "ml_tangerine"));
+}
+ gQueue.push(new changeCurrentItem("emenulist", "eml_tangerine"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=433418"
+ title="Accessibles for focused HTML Select elements are not getting focused state">
+ Mozilla Bug 433418
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+ title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+ Mozilla Bug 474893
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+ title=" fire focus event on document accessible whenever the root or body element is focused">
+ Mozilla Bug 552368
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <listbox id="listbox" rows="3">
+ <listitem id="lb_item1" label="item1"/>
+ <listitem id="lb_item2" label="item1"/>
+ </listbox>
+ <listbox id="multisellistbox" rows="3" seltype="multiple">
+ <listitem id="mslb_item1" label="item1"/>
+ <listitem id="mslb_item2" label="item1"/>
+ </listbox>
+ <listbox id="emptylistbox" rows="3"/>
+ <listbox id="mcolumnlistbox" rows="3">
+ <listcols>
+ <listcol/>
+ <listcol/>
+ </listcols>
+ <listitem id="mclb_item1">
+ <listcell label="George"/>
+ <listcell label="House Painter"/>
+ </listitem>
+ <listitem id="mclb_item2">
+ <listcell label="Mary Ellen"/>
+ <listcell label="Candle Maker"/>
+ </listitem>
+ </listbox>
+ <listbox id="headerlistbox" rows="3">
+ <listhead>
+ <listheader label="Name"/>
+ <listheader label="Occupation"/>
+ </listhead>
+ <listcols>
+ <listcol/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem id="hlb_item1">
+ <listcell label="George"/>
+ <listcell label="House Painter"/>
+ </listitem>
+ <listitem id="hlb_item2">
+ <listcell label="Mary Ellen"/>
+ <listcell label="Candle Maker"/>
+ </listitem>
+ </listbox>
+
+ <richlistbox id="richlistbox">
+ <richlistitem id="rlb_item1">
+ <description>A XUL Description!</description>
+ </richlistitem>
+ <richlistitem id="rlb_item2">
+ <button label="A XUL Button"/>
+ </richlistitem>
+ </richlistbox>
+ <richlistbox id="multiselrichlistbox" seltype="multiple">
+ <richlistitem id="msrlb_item1">
+ <description>A XUL Description!</description>
+ </richlistitem>
+ <richlistitem id="msrlb_item2">
+ <button label="A XUL Button"/>
+ </richlistitem>
+ </richlistbox>
+ <richlistbox id="emptyrichlistbox" seltype="multiple"/>
+
+ <menulist id="menulist">
+ <menupopup>
+ <menuitem id="ml_tangerine" label="tangerine trees"/>
+ <menuitem id="ml_marmalade" label="marmalade skies"/>
+ </menupopup>
+ </menulist>
+ <menulist id="emenulist" editable="true">
+ <menupopup>
+ <menuitem id="eml_tangerine" label="tangerine trees"/>
+ <menuitem id="eml_marmalade" label="marmalade skies"/>
+ </menupopup>
+ </menulist>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_menu.xul b/accessible/tests/mochitest/events/test_focus_menu.xul
new file mode 100644
index 000000000..f205e8d25
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_menu.xul
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Menu focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ if (WIN) {
+ gQueue.push(new toggleTopMenu("fruit", new focusChecker("fruit")));
+ gQueue.push(new synthRightKey("fruit", new focusChecker("vehicle")));
+ gQueue.push(new synthEscapeKey("vehicle", new focusChecker(document)));
+ }
+
+ // mouse move activate items but no focus event until menubar is active
+ gQueue.push(new synthMouseMove("fruit", new nofocusChecker("apple")));
+
+ // mouseover and click on menuitem makes it active before menubar is
+ // active
+ gQueue.push(new synthClick("fruit", new focusChecker("fruit")));
+
+ // mouseover on menuitem when menubar is active
+ gQueue.push(new synthMouseMove("apple", new focusChecker("apple")));
+
+ // keydown on disabled menuitem (disabled items are skipped on linux)
+ if (WIN)
+ gQueue.push(new synthDownKey("apple", new focusChecker("orange")));
+
+ // menu and menuitem are both active
+ // XXX: intermitent failure because two focus events may be coalesced,
+ // think to workaround or fix this issue, when done enable queue invoker
+ // below and remove next two.
+ //gQueue.push(new synthRightKey("apple",
+ // [ new focusChecker("vehicle"),
+ // new focusChecker("cycle")]));
+ gQueue.push(new synthClick("vehicle", new focusChecker("vehicle")));
+ gQueue.push(new synthDownKey("cycle", new focusChecker("cycle")));
+
+ // open submenu
+ gQueue.push(new synthRightKey("cycle", new focusChecker("tricycle")));
+
+ // move to first menu in cycle, DOMMenuItemActive is fired for fruit,
+ // cycle and apple menuitems (bug 685191)
+ todo(false, "focus is fired for 'cycle' menuitem");
+ //gQueue.push(new synthRightKey("vehicle", new focusChecker("apple")));
+
+ // click menuitem to close menu, focus gets back to document
+ gQueue.push(new synthClick("tricycle", new focusChecker(document)));
+
+ //enableLogging("focus,DOMEvents,tree"); // logging for bug708927
+ //gQueue.onFinish = function() { disableLogging(); }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar>
+ <menu id="fruit" label="Fruit">
+ <menupopup>
+ <menuitem id="apple" label="Apple"/>
+ <menuitem id="orange" label="Orange" disabled="true"/>
+ </menupopup>
+ </menu>
+ <menu id="vehicle" label="Vehicle">
+ <menupopup>
+ <menu id="cycle" label="cycle">
+ <menupopup>
+ <menuitem id="tricycle" label="tricycle"/>
+ </menupopup>
+ </menu>
+ <menuitem id="car" label="Car" disabled="true"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_name.html b/accessible/tests/mochitest/events/test_focus_name.html
new file mode 100644
index 000000000..f0db26427
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_name.html
@@ -0,0 +1,122 @@
+<html>
+
+<head>
+ <title>Accessible name testing on focus</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Checker for invokers.
+ */
+ function actionChecker(aID, aDescription)
+ {
+ this.__proto__ = new invokerChecker(EVENT_FOCUS, aID);
+
+ this.check = function actionChecker_check(aEvent)
+ {
+ var target = aEvent.accessible;
+ is(target.description, aDescription,
+ "Wrong description for " + prettyName(target));
+ }
+ }
+
+ var gFocusHandler = {
+ handleEvent: function gFocusHandler_handleEvent(aEvent) {
+ var elm = aEvent.target;
+ if (elm.nodeType != nsIDOMNode.ELEMENT_NODE)
+ return;
+
+ gTooltipElm.style.display = "block";
+
+ elm.setAttribute("aria-describedby", "tooltip");
+ }
+ };
+
+ var gBlurHandler = {
+ handleEvent: function gBlurHandler_handleEvent(aEvent) {
+ gTooltipElm.style.display = "none";
+
+ var elm = aEvent.target;
+ if (elm.nodeType == nsIDOMNode.ELEMENT_NODE)
+ elm.removeAttribute("aria-describedby");
+ }
+ };
+
+ /**
+ * Do tests.
+ */
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ var gButtonElm = null;
+ var gTextboxElm = null;
+ var gTooltipElm = null;
+
+ function doTests()
+ {
+ gButtonElm = getNode("button");
+ gTextboxElm = getNode("textbox");
+ gTooltipElm = getNode("tooltip");
+
+ gButtonElm.addEventListener("focus", gFocusHandler, false);
+ gButtonElm.addEventListener("blur", gBlurHandler, false);
+ gTextboxElm.addEventListener("focus", gFocusHandler, false);
+ gTextboxElm.addEventListener("blur", gBlurHandler, false);
+
+ // The aria-describedby is changed on DOM focus. Accessible description
+ // should be updated when a11y focus is fired.
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
+ gQueue.onFinish = function()
+ {
+ gButtonElm.removeEventListener("focus", gFocusHandler, false);
+ gButtonElm.removeEventListener("blur", gBlurHandler, false);
+ gTextboxElm.removeEventListener("focus", gFocusHandler, false);
+ gTextboxElm.removeEventListener("blur", gBlurHandler, false);
+ }
+
+ var descr = "It's a tooltip";
+ gQueue.push(new synthFocus("button", new actionChecker("button", descr)));
+ gQueue.push(new synthTab("textbox", new actionChecker("textbox", descr)));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=520709"
+ title="mochitest to ensure name/description are updated on a11y focus if they were changed on DOM focus">
+ Mozilla Bug 520709
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="tooltip" style="display: none" aria-hidden="true">It's a tooltip</div>
+ <button id="button">button</button>
+ <input id="textbox">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_selects.html b/accessible/tests/mochitest/events/test_focus_selects.html
new file mode 100644
index 000000000..ef742c4b2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_selects.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ // Bug 746534 - File causes crash or hang on OS X
+ if (MAC) {
+ todo(false, "Bug 746534 - test file causes crash or hang on OS X");
+ SimpleTest.finish();
+ return;
+ }
+
+ gQueue = new eventQueue();
+
+ // first item is focused until there's selection
+ gQueue.push(new synthFocus("list", new focusChecker("orange")));
+
+ // item is selected and stays focused
+ gQueue.push(new synthDownKey("list", new nofocusChecker()));
+
+ // last selected item is focused
+ gQueue.push(new synthDownKey("list", new focusChecker("apple"), { shiftKey: true }));
+
+ // no focus event if nothing is changed
+ gQueue.push(new synthDownKey("list", new nofocusChecker("apple")));
+
+ // current item is focused
+ gQueue.push(new synthUpKey("list", new focusChecker("orange"), { ctrlKey: true }));
+
+ // focus on empty list (no items to be focused)
+ gQueue.push(new synthTab("list", new focusChecker("emptylist")));
+
+ // current item is focused
+ gQueue.push(new synthShiftTab("emptylist", new focusChecker("orange")));
+
+ // collapsed combobox keeps a focus
+ gQueue.push(new synthFocus("combobox", new focusChecker("combobox")));
+ gQueue.push(new synthDownKey("combobox", new nofocusChecker("combobox")));
+
+ // selected item is focused for expanded combobox
+ gQueue.push(new synthOpenComboboxKey("combobox", new focusChecker("cb_apple")));
+ gQueue.push(new synthUpKey("combobox", new focusChecker("cb_orange")));
+
+ // collapsed combobx keeps a focus
+ gQueue.push(new synthEscapeKey("combobox", new focusChecker("combobox")));
+
+ // no focus events for unfocused list controls when current item is
+ // changed
+ gQueue.push(new synthFocus("emptylist"));
+
+ gQueue.push(new changeCurrentItem("list", "orange"));
+ gQueue.push(new changeCurrentItem("combobox", "cb_apple"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=433418"
+ title="Accessibles for focused HTML Select elements are not getting focused state">
+ Mozilla Bug 433418
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+ title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+ Mozilla Bug 474893
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="list" size="5" multiple="">
+ <option id="orange">Orange</option>
+ <option id="apple">Apple</option>
+ </select>
+
+ <select id="emptylist" size="5"></select>
+
+ <select id="combobox">
+ <option id="cb_orange">Orange</option>
+ <option id="cb_apple">Apple</option>
+ </select>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_tabbox.xul b/accessible/tests/mochitest/events/test_focus_tabbox.xul
new file mode 100644
index 000000000..c51546405
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_tabbox.xul
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tabbox focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ if (MAC) {
+ todo(false, "Tests disabled because of imminent failure.");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ var textbox = getNode("textbox").inputField;
+ gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
+ gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
+ gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
+ new focusChecker(textbox)));
+ gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
+ new focusChecker("tab3")));
+ gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },
+ new focusChecker("tab1")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=370396"
+ title="Control+Tab to an empty tab panel in a tabbox causes focus to leave the tabbox">
+ Mozilla Bug 370396
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tabbox>
+ <tabs>
+ <tab id="tab1" label="Tab1" selected="true"/>
+ <tab id="tab2" label="Tab2" />
+ <tab id="tab3" label="Tab3" />
+ </tabs>
+ <tabpanels>
+ <tabpanel orient="vertical">
+ <groupbox orient="vertical">
+ <checkbox id="checkbox1" label="Monday" width="75"/>
+ <checkbox label="Tuesday" width="75"/>
+ <checkbox label="Wednesday" width="75"/>
+ <checkbox label="Thursday" width="75"/>
+ <checkbox label="Friday" width="75"/>
+ <checkbox label="Saturday" width="75"/>
+ <checkbox label="Sunday" width="75"/>
+ </groupbox>
+
+ <spacer style="height: 10px" />
+ <label value="Label After checkboxes" />
+ </tabpanel>
+ <tabpanel orient="vertical">
+ <textbox id="textbox" />
+ </tabpanel>
+ <tabpanel orient="vertical">
+ <description>Tab 3 content</description>
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_tree.xul b/accessible/tests/mochitest/events/test_focus_tree.xul
new file mode 100644
index 000000000..995b08e25
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_tree.xul
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL tree focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function focusTree(aTreeID)
+ {
+ var checker = new focusChecker(getFirstTreeItem, aTreeID);
+ this.__proto__ = new synthFocus(aTreeID, [ checker ]);
+ }
+
+ function moveToNextItem(aTreeID)
+ {
+ var checker = new focusChecker(getSecondTreeItem, aTreeID);
+ this.__proto__ = new synthDownKey(aTreeID, [ checker ]);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function getTreeItemAt(aTreeID, aIdx)
+ { return getAccessible(aTreeID).getChildAt(aIdx + 1); }
+
+ function getFirstTreeItem(aTreeID)
+ { return getTreeItemAt(aTreeID, 0); }
+
+ function getSecondTreeItem(aTreeID)
+ { return getTreeItemAt(aTreeID, 1); }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ var gQueue = null;
+
+ //gA11yEventDumpID = "debug"; // debugging
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusTree("tree"));
+ gQueue.push(new moveToNextItem("tree"));
+ gQueue.push(new synthFocus("emptytree"));
+
+ // no focus event for changed current item for unfocused tree
+ gQueue.push(new changeCurrentItem("tree", 0));
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTableTreeView(5));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=386821"
+ title="Need better solution for firing delayed event against xul tree">
+ Mozilla Bug 386821
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=406308"
+ title="Don't fire accessible focus events if widget is not actually in focus, confuses screen readers">
+ Mozilla Bug 406308
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ <tree id="emptytree" flex="1">
+ <treecols>
+ <treecol id="emptytree_col1" flex="1" primary="true" label="column"/>
+ <treecol id="emptytree_col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="emptytree_treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_fromUserInput.html b/accessible/tests/mochitest/events/test_fromUserInput.html
new file mode 100644
index 000000000..1cfeedf0b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_fromUserInput.html
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+ <title>Testing of isFromUserInput in text events</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Remove text data from HTML input.
+ */
+ function removeTextFromInput(aID, aStart, aEnd, aText, aFromUser)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false, aFromUser)
+ ];
+
+ this.invoke = function removeTextFromInput_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.DOMNode.setSelectionRange(aStart, aEnd);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromInput_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove text data from text node.
+ */
+ function removeTextFromContentEditable(aID, aStart, aEnd, aText, aFromUser)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false, aFromUser)
+ ];
+
+ this.invoke = function removeTextFromContentEditable_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.textNode = getNode(aID).firstChild;
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.setStart(this.textNode, aStart);
+ range.setEnd(this.textNode, aEnd);
+ selection.addRange(range);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromContentEditable_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Focused editable text node
+ gQueue.push(new removeTextFromContentEditable("div", 0, 3, "hel", true));
+
+ // Focused editable HTML input
+ gQueue.push(new removeTextFromInput("input", 1, 2, "n", true));
+
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+
+ </script>
+</head>
+
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=686909"
+ title="isFromUserInput flag on accessible text change events not correct">
+ Mozilla Bug 686909
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <div id="eventdump"></div>
+
+ <div id="div" contentEditable="true">hello</div>
+ <input id="input" value="input">
+
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/events/test_label.xul b/accessible/tests/mochitest/events/test_label.xul
new file mode 100644
index 000000000..e82763a7c
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_label.xul
@@ -0,0 +1,177 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tests: accessible XUL label/description events">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ const kRecreated = 0;
+ const kTextRemoved = 1;
+ const kTextChanged = 2;
+
+ const kNoValue = 0;
+
+ /**
+ * Set/remove @value attribute.
+ */
+ function setValue(aID, aValue, aResult, aOldValue)
+ {
+ this.labelNode = getNode(aID);
+
+ this.eventSeq = [];
+
+ switch (aResult) {
+ case kRecreated:
+ this.eventSeq.push(new invokerChecker(EVENT_HIDE, this.labelNode));
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW, this.labelNode));
+ break;
+ case kTextRemoved:
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aOldValue.length,
+ aOldValue, false));
+ break;
+ case kTextChanged:
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aOldValue.length,
+ aOldValue, false));
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aValue.length,
+ aValue, true));
+ break;
+ }
+
+ this.invoke = function setValue_invoke()
+ {
+ if (aValue === kNoValue)
+ this.labelNode.removeAttribute("value");
+ else
+ this.labelNode.setAttribute("value", aValue);
+ }
+
+ this.finalCheck = function setValue_finalCheck()
+ {
+ var tree =
+ { LABEL: [
+ { TEXT_LEAF: [ ] }
+ ] };
+ testAccessibleTree(aID, tree);
+ }
+
+ this.getID = function setValue_getID()
+ {
+ return "set @value='" + aValue + "' for label " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Change @crop attribute.
+ */
+ function setCrop(aID, aCropValue, aRemovedText, aInsertedText)
+ {
+ this.labelNode = getNode(aID);
+ this.width = this.labelNode.boxObject.width;
+ this.charWidth = this.width / this.labelNode.value.length;
+
+ this.eventSeq = [
+ new textChangeChecker(this.labelNode, 0, -1, aRemovedText, false),
+ new textChangeChecker(this.labelNode, 0, -1, aInsertedText, true)
+ ];
+
+ this.invoke = function setCrop_invoke()
+ {
+ if (!this.labelNode.hasAttribute("crop"))
+ this.labelNode.width = Math.floor(this.width - 2 * this.charWidth);
+
+ this.labelNode.setAttribute("crop", aCropValue);
+ }
+
+ this.getID = function setCrop_finalCheck()
+ {
+ return "set crop " + aCropValue;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setValue("label", "shiroka strana", kRecreated));
+ gQueue.push(new setValue("label", "?<>!+_", kTextChanged, "shiroka strana"));
+ gQueue.push(new setValue("label", "", kTextRemoved, "?<>!+_"));
+ gQueue.push(new setValue("label", kNoValue, kRecreated));
+
+ gQueue.push(new setValue("descr", "hello world", kRecreated));
+ gQueue.push(new setValue("descr", "si_ya", kTextChanged, "hello world"));
+ gQueue.push(new setValue("descr", "", kTextRemoved, "si_ya"));
+ gQueue.push(new setValue("descr", kNoValue, kRecreated));
+
+ if (MAC) {
+ // "valuetocro" -> "…etocro"
+ gQueue.push(new setCrop("croplabel", "left", "valu", "…"));
+ // "…etocro", "val…cro"
+ gQueue.push(new setCrop("croplabel", "center", "…eto", "val…"));
+ } else {
+ // "valuetocro" -> "…uetocro"
+ gQueue.push(new setCrop("croplabel", "left", "val", "…"));
+ // "…uetocro" -> "valu…cro"
+ gQueue.push(new setCrop("croplabel", "center", "…ueto", "valu…"));
+ }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=396166"
+ title="xul:label@value accessible should implement nsIAccessibleText">
+ Bug 396166
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <label id="label">hello</label>
+ <description id="descr">hello</description>
+
+ <hbox>
+ <label id="croplabel" value="valuetocro"
+ style="font-family: monospace;"/>
+ </hbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_menu.xul b/accessible/tests/mochitest/events/test_menu.xul
new file mode 100644
index 000000000..bae36fb71
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_menu.xul
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible menu events testing for XUL menu">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ function openFileMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENU_START, getNode("menubar")),
+ new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-file"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-newtab")) intermitent failure
+ ];
+
+ this.invoke = function openFileMenu_invoke()
+ {
+ synthesizeKey("F", { altKey: true, shiftKey: true });
+ }
+
+ this.getID = function openFileMenu_getID()
+ {
+ return "open file menu by alt+F press";
+ }
+ }
+
+ function openEditMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-file")),
+ new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-edit"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-undo")) intermitent failure
+ ];
+
+ this.invoke = function openEditMenu_invoke()
+ {
+ synthesizeKey("VK_RIGHT", { });
+ }
+
+ this.getID = function openEditMenu_getID()
+ {
+ return "open edit menu by lef arrow press";
+ }
+ }
+
+ function closeEditMenu()
+ {
+ this.eventSeq = [
+ //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+ new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-edit")),
+ new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+ ];
+
+ this.invoke = function closeEditMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeEditMenu_getID()
+ {
+ return "close edit menu, leave menubar";
+ }
+ }
+
+ function focusFileMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENU_START, getNode("menubar"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-file")) //intermitent failure
+ ];
+
+ this.invoke = function focusFileMenu_invoke()
+ {
+ synthesizeKey("VK_ALT", { });
+ }
+
+ this.getID = function focusFileMenu_getID()
+ {
+ return "activate menubar, focus file menu (atl press)";
+ }
+ }
+
+ function focusEditMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode("menuitem-edit"))
+ ];
+
+ this.invoke = function focusEditMenu_invoke()
+ {
+ synthesizeKey("VK_RIGHT", { });
+ }
+
+ this.getID = function focusEditMenu_getID()
+ {
+ return "focus edit menu";
+ }
+ }
+
+ function leaveMenubar()
+ {
+ this.eventSeq = [
+ //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+ new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+ ];
+
+ this.invoke = function leaveMenubar_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function leaveMenubar_getID()
+ {
+ return "leave menubar";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ if (!WIN) {
+ todo(false, "Enable this test on other platforms.");
+ SimpleTest.finish();
+ return;
+ }
+
+ todo(false,
+ "Fix intermitent failures. Focus may randomly occur before or after menupopup events!");
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new openFileMenu());
+ gQueue.push(new openEditMenu());
+ gQueue.push(new closeEditMenu());
+
+ // Alt key is used to active menubar and focus menu item on Windows,
+ // other platforms requires setting a ui.key.menuAccessKeyFocuses
+ // preference.
+ if (WIN || LINUX) {
+ gQueue.push(new focusFileMenu());
+ gQueue.push(new focusEditMenu());
+ gQueue.push(new leaveMenubar());
+ }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=615189"
+ title="Clean up FireAccessibleFocusEvent">
+ Mozilla Bug 615189
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar id="menubar">
+ <menu id="menuitem-file" label="File" accesskey="F">
+ <menupopup id="menupopup-file">
+ <menuitem id="menuitem-newtab" label="New Tab"/>
+ </menupopup>
+ </menu>
+ <menu id="menuitem-edit" label="Edit" accesskey="E">
+ <menupopup id="menupopup-edit">
+ <menuitem id="menuitem-undo" label="Undo"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <vbox id="eventdump" role="log"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_mutation.html b/accessible/tests/mochitest/events/test_mutation.html
new file mode 100644
index 000000000..232a09727
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_mutation.html
@@ -0,0 +1,632 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ div.displayNone a { display:none; }
+ div.visibilityHidden a { visibility:hidden; }
+</style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Invokers.
+ */
+ var kNoEvents = 0;
+
+ var kShowEvent = 1;
+ var kHideEvent = 2;
+ var kReorderEvent = 4;
+ var kShowEvents = kShowEvent | kReorderEvent;
+ var kHideEvents = kHideEvent | kReorderEvent;
+ var kHideAndShowEvents = kHideEvents | kShowEvent;
+
+ /**
+ * Base class to test mutation a11y events.
+ *
+ * @param aNodeOrID [in] node invoker's action is executed for
+ * @param aEventTypes [in] events to register (see constants above)
+ * @param aDoNotExpectEvents [in] boolean indicates if events are expected
+ */
+ function mutateA11yTree(aNodeOrID, aEventTypes, aDoNotExpectEvents)
+ {
+ // Interface
+ this.DOMNode = getNode(aNodeOrID);
+ this.doNotExpectEvents = aDoNotExpectEvents;
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [];
+
+ /**
+ * Change default target (aNodeOrID) registered for the given event type.
+ */
+ this.setTarget = function mutateA11yTree_setTarget(aEventType, aTarget)
+ {
+ var type = this.getA11yEventType(aEventType);
+ for (var idx = 0; idx < this.getEventSeq().length; idx++) {
+ if (this.getEventSeq()[idx].type == type) {
+ this.getEventSeq()[idx].target = aTarget;
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Replace the default target currently registered for a given event type
+ * with the nodes in the targets array.
+ */
+ this.setTargets = function mutateA11yTree_setTargets(aEventType, aTargets) {
+ var targetIdx = this.setTarget(aEventType, aTargets[0]);
+
+ var type = this.getA11yEventType(aEventType);
+ for (var i = 1; i < aTargets.length; i++) {
+ var checker = new invokerChecker(type, aTargets[i]);
+ this.getEventSeq().splice(++targetIdx, 0, checker);
+ }
+ }
+
+ // Implementation
+ this.getA11yEventType = function mutateA11yTree_getA11yEventType(aEventType)
+ {
+ if (aEventType == kReorderEvent)
+ return nsIAccessibleEvent.EVENT_REORDER;
+
+ if (aEventType == kHideEvent)
+ return nsIAccessibleEvent.EVENT_HIDE;
+
+ if (aEventType == kShowEvent)
+ return nsIAccessibleEvent.EVENT_SHOW;
+ }
+
+ this.getEventSeq = function mutateA11yTree_getEventSeq()
+ {
+ return this.doNotExpectEvents ? this.unexpectedEventSeq : this.eventSeq;
+ }
+
+ if (aEventTypes & kHideEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kHideEvent),
+ this.DOMNode);
+ this.getEventSeq().push(checker);
+ }
+
+ if (aEventTypes & kShowEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kShowEvent),
+ this.DOMNode);
+ this.getEventSeq().push(checker);
+ }
+
+ if (aEventTypes & kReorderEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kReorderEvent),
+ this.DOMNode.parentNode);
+ this.getEventSeq().push(checker);
+ }
+ }
+
+ /**
+ * Change CSS style for the given node.
+ */
+ function changeStyle(aNodeOrID, aProp, aValue, aEventTypes)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
+
+ this.invoke = function changeStyle_invoke()
+ {
+ this.DOMNode.style[aProp] = aValue;
+ }
+
+ this.getID = function changeStyle_getID()
+ {
+ return aNodeOrID + " change style " + aProp + " on value " + aValue;
+ }
+ }
+
+ /**
+ * Change class name for the given node.
+ */
+ function changeClass(aParentNodeOrID, aNodeOrID, aClassName, aEventTypes)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
+
+ this.invoke = function changeClass_invoke()
+ {
+ this.parentDOMNode.className = aClassName;
+ }
+
+ this.getID = function changeClass_getID()
+ {
+ return aNodeOrID + " change class " + aClassName;
+ }
+
+ this.parentDOMNode = getNode(aParentNodeOrID);
+ }
+
+ /**
+ * Clone the node and append it to its parent.
+ */
+ function cloneAndAppendToDOM(aNodeOrID, aEventTypes,
+ aTargetsFunc, aReorderTargetFunc)
+ {
+ var eventTypes = aEventTypes || kShowEvents;
+ var doNotExpectEvents = (aEventTypes == kNoEvents);
+
+ this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
+ doNotExpectEvents);
+
+ this.invoke = function cloneAndAppendToDOM_invoke()
+ {
+ var newElm = this.DOMNode.cloneNode(true);
+ newElm.removeAttribute('id');
+
+ var targets = aTargetsFunc ?
+ aTargetsFunc.call(null, newElm) : [newElm];
+ this.setTargets(kShowEvent, targets);
+
+ if (aReorderTargetFunc) {
+ var reorderTarget = aReorderTargetFunc.call(null, this.DOMNode);
+ this.setTarget(kReorderEvent, reorderTarget);
+ }
+
+ this.DOMNode.parentNode.appendChild(newElm);
+ }
+
+ this.getID = function cloneAndAppendToDOM_getID()
+ {
+ return aNodeOrID + " clone and append to DOM.";
+ }
+ }
+
+ /**
+ * Removes the node from DOM.
+ */
+ function removeFromDOM(aNodeOrID, aEventTypes,
+ aTargetsFunc, aReorderTargetFunc)
+ {
+ var eventTypes = aEventTypes || kHideEvents;
+ var doNotExpectEvents = (aEventTypes == kNoEvents);
+
+ this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
+ doNotExpectEvents);
+
+ this.invoke = function removeFromDOM_invoke()
+ {
+ this.DOMNode.parentNode.removeChild(this.DOMNode);
+ }
+
+ this.getID = function removeFromDOM_getID()
+ {
+ return prettyName(aNodeOrID) + " remove from DOM.";
+ }
+
+ if (aTargetsFunc && (eventTypes & kHideEvent))
+ this.setTargets(kHideEvent, aTargetsFunc.call(null, this.DOMNode));
+
+ if (aReorderTargetFunc && (eventTypes & kReorderEvent))
+ this.setTarget(kReorderEvent,
+ aReorderTargetFunc.call(null, this.DOMNode));
+ }
+
+ /**
+ * Clone the node and replace the original node by cloned one.
+ */
+ function cloneAndReplaceInDOM(aNodeOrID)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, kHideAndShowEvents,
+ false);
+
+ this.invoke = function cloneAndReplaceInDOM_invoke()
+ {
+ this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
+ }
+
+ this.getID = function cloneAndReplaceInDOM_getID()
+ {
+ return aNodeOrID + " clone and replace in DOM.";
+ }
+
+ this.newElm = this.DOMNode.cloneNode(true);
+ this.newElm.removeAttribute('id');
+ this.setTarget(kShowEvent, this.newElm);
+ }
+
+ /**
+ * Trigger content insertion (flush layout), removal and insertion of
+ * the same element for the same parent.
+ */
+ function test1(aContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test1");
+ this.containerNode = getNode(aContainerID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function test1_invoke()
+ {
+ this.containerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.containerNode.removeChild(this.divNode);
+ this.containerNode.appendChild(this.divNode);
+ }
+
+ this.getID = function test1_getID()
+ {
+ return "fuzzy test #1: content insertion (flush layout), removal and" +
+ "reinsertion";
+ }
+ }
+
+ /**
+ * Trigger content insertion (flush layout), removal and insertion of
+ * the same element for the different parents.
+ */
+ function test2(aContainerID, aTmpContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test2");
+ this.containerNode = getNode(aContainerID);
+ this.tmpContainerNode = getNode(aTmpContainerID);
+ this.container = getAccessible(this.containerNode);
+ this.tmpContainer = getAccessible(this.tmpContainerNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_REORDER, this.tmpContainerNode)
+ ];
+
+ this.invoke = function test2_invoke()
+ {
+ this.tmpContainerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.tmpContainerNode.removeChild(this.divNode);
+ this.containerNode.appendChild(this.divNode);
+ }
+
+ this.getID = function test2_getID()
+ {
+ return "fuzzy test #2: content insertion (flush layout), removal and" +
+ "reinsertion under another container";
+ }
+ }
+
+ /**
+ * Content insertion (flush layout) and then removal (nothing was changed).
+ */
+ function test3(aContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test3");
+ this.containerNode = getNode(aContainerID);
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_HIDE, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function test3_invoke()
+ {
+ this.containerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.containerNode.removeChild(this.divNode);
+ }
+
+ this.getID = function test3_getID()
+ {
+ return "fuzzy test #3: content insertion (flush layout) and removal";
+ }
+ }
+
+ function insertReferredElm(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.firstChild; }, this.containerNode),
+ new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.lastChild; }, this.containerNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function insertReferredElm_invoke()
+ {
+ this.containerNode.innerHTML =
+ "<span id='insertReferredElms_span'></span><input aria-labelledby='insertReferredElms_span'>";
+ }
+
+ this.getID = function insertReferredElm_getID()
+ {
+ return "insert inaccessible element and then insert referring element to make it accessible";
+ }
+ }
+
+ function showHiddenParentOfVisibleChild()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("c4_child")),
+ new invokerChecker(EVENT_SHOW, getNode("c4_middle")),
+ new invokerChecker(EVENT_REORDER, getNode("c4"))
+ ];
+
+ this.invoke = function showHiddenParentOfVisibleChild_invoke()
+ {
+ getNode("c4_middle").style.visibility = 'visible';
+ }
+
+ this.getID = function showHiddenParentOfVisibleChild_getID()
+ {
+ return "show hidden parent of visible child";
+ }
+ }
+
+ function hideNDestroyDoc()
+ {
+ this.txt = null;
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, function() { return this.txt; }.bind(this))
+ ];
+
+ this.invoke = function hideNDestroyDoc_invoke()
+ {
+ this.txt = getAccessible('c5').firstChild.firstChild;
+ this.txt.DOMNode.parentNode.removeChild(this.txt.DOMNode);
+ }
+
+ this.check = function hideNDestroyDoc_check()
+ {
+ getNode('c5').parentNode.removeChild(getNode('c5'));
+ }
+
+ this.getID = function hideNDestroyDoc_getID()
+ {
+ return "remove text node and destroy a document on hide event";
+ }
+ }
+
+ function hideHideNDestroyDoc()
+ {
+ this.target = null;
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, function() { return this.target; }.bind(this))
+ ];
+
+ this.invoke = function hideHideNDestroyDoc_invoke()
+ {
+ var doc = getAccessible('c6').firstChild;
+ var l1 = doc.firstChild;
+ this.target = l1.firstChild;
+ var l2 = doc.lastChild;
+ l1.DOMNode.removeChild(l1.DOMNode.firstChild);
+ l2.DOMNode.removeChild(l2.DOMNode.firstChild);
+ }
+
+ this.check = function hideHideNDestroyDoc_check()
+ {
+ getNode('c6').parentNode.removeChild(getNode('c6'));
+ }
+
+ this.getID = function hideHideNDestroyDoc_getID()
+ {
+ return "remove text nodes (2 events in the queue) and destroy a document on first hide event";
+ }
+ }
+
+ /**
+ * Target getters.
+ */
+ function getFirstChild(aNode)
+ {
+ return [aNode.firstChild];
+ }
+ function getLastChild(aNode)
+ {
+ return [aNode.lastChild];
+ }
+
+ function getNEnsureFirstChild(aNode)
+ {
+ var node = aNode.firstChild;
+ getAccessible(node);
+ return [node];
+ }
+
+ function getNEnsureChildren(aNode)
+ {
+ var children = [];
+ var node = aNode.firstChild;
+ do {
+ children.push(node);
+ getAccessible(node);
+ node = node.nextSibling;
+ } while (node);
+
+ return children;
+ }
+
+ function getParent(aNode)
+ {
+ return aNode.parentNode;
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("events,verbose");
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Show/hide events by changing of display style of accessible DOM node
+ // from 'inline' to 'none', 'none' to 'inline'.
+ var id = "link1";
+ getAccessible(id); // ensure accessible is created
+ gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
+ gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
+
+ // Show/hide events by changing of visibility style of accessible DOM node
+ // from 'visible' to 'hidden', 'hidden' to 'visible'.
+ var id = "link2";
+ getAccessible(id);
+ gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
+ gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+
+ // Show/hide events by changing of display style of accessible DOM node
+ // from 'inline' to 'block', 'block' to 'inline'.
+ var id = "link3";
+ getAccessible(id); // ensure accessible is created
+ gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
+ gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
+
+ // Show/hide events by changing of visibility style of accessible DOM node
+ // from 'collapse' to 'visible', 'visible' to 'collapse'.
+ var id = "link4";
+ gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+ gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
+
+ // Show/hide events by adding new accessible DOM node and removing old one.
+ var id = "link5";
+ gQueue.push(new cloneAndAppendToDOM(id));
+ gQueue.push(new removeFromDOM(id));
+
+ // No show/hide events by adding new not accessible DOM node and removing
+ // old one, no reorder event for their parent.
+ var id = "child1";
+ gQueue.push(new cloneAndAppendToDOM(id, kNoEvents));
+ gQueue.push(new removeFromDOM(id, kNoEvents));
+
+ // Show/hide events by adding new accessible DOM node and removing
+ // old one, there is reorder event for their parent.
+ var id = "child2";
+ gQueue.push(new cloneAndAppendToDOM(id));
+ gQueue.push(new removeFromDOM(id));
+
+ // Show/hide events by adding new DOM node containing accessible DOM and
+ // removing old one, there is reorder event for their parent.
+ var id = "child3";
+ gQueue.push(new cloneAndAppendToDOM(id, kShowEvents, getFirstChild,
+ getParent));
+
+ // Hide event for accessible child of unaccessible removed DOM node and
+ // reorder event for its parent.
+ gQueue.push(new removeFromDOM(id, kHideEvents,
+ getNEnsureFirstChild, getParent));
+
+ // Hide events for accessible children of unaccessible removed DOM node
+ // and reorder event for its parent.
+ gQueue.push(new removeFromDOM("child4", kHideEvents,
+ getNEnsureChildren, getParent));
+
+ // Show/hide events by creating new accessible DOM node and replacing
+ // old one.
+ getAccessible("link6"); // ensure accessible is created
+ gQueue.push(new cloneAndReplaceInDOM("link6"));
+
+ // Show/hide events by changing class name on the parent node.
+ gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
+ gQueue.push(new changeClass("container2", "link7", "displayNone",
+ kHideEvents));
+
+ gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
+ gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
+ kHideEvents));
+
+ gQueue.push(new test1("testContainer"));
+ gQueue.push(new test2("testContainer", "testContainer2"));
+ gQueue.push(new test2("testContainer", "testNestedContainer"));
+ gQueue.push(new test3("testContainer"));
+ gQueue.push(new insertReferredElm("testContainer3"));
+ gQueue.push(new showHiddenParentOfVisibleChild());
+
+ gQueue.push(new hideNDestroyDoc());
+ gQueue.push(new hideHideNDestroyDoc());
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
+ title=" turn the test from bug 354745 into mochitest">
+ Mozilla Bug 469985</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
+ title="no reorder event when html:link display property is changed from 'none' to 'inline'">
+ Mozilla Bug 472662</a>
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275</a>
+ <a target="_blank"
+ title="Develop a way to handle visibility style"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
+ Mozilla Bug 606125</a>
+ <a target="_blank"
+ title="Update accessible tree on content insertion after layout"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
+ Mozilla Bug 498015</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="testContainer">
+ <a id="link1" href="http://www.google.com">Link #1</a>
+ <a id="link2" href="http://www.google.com">Link #2</a>
+ <a id="link3" href="http://www.google.com">Link #3</a>
+ <a id="link4" href="http://www.google.com" style="visibility:collapse">Link #4</a>
+ <a id="link5" href="http://www.google.com">Link #5</a>
+
+ <div id="container" role="list">
+ <span id="child1"></span>
+ <span id="child2" role="listitem"></span>
+ <span id="child3"><span role="listitem"></span></span>
+ <span id="child4"><span id="child4_1" role="listitem"></span><span id="child4_2" role="listitem"></span></span>
+ </div>
+
+ <a id="link6" href="http://www.google.com">Link #6</a>
+
+ <div id="container2" class="displayNone"><a id="link7">Link #7</a></div>
+ <div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div>
+ <div id="testNestedContainer"></div>
+ </div>
+ <div id="testContainer2"></div>
+ <div id="testContainer3"></div>
+
+ <div id="c4">
+ <div style="visibility:hidden" id="c4_middle">
+ <div style="visibility:visible" id="c4_child"></div>
+ </div>
+
+ <iframe id="c5" src="data:text/html,hey"></iframe>
+ <iframe id="c6" src="data:text/html,<label>l</label><label>l</label>"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_mutation.xhtml b/accessible/tests/mochitest/events/test_mutation.xhtml
new file mode 100644
index 000000000..e1aabe612
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_mutation.xhtml
@@ -0,0 +1,97 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <bindings xmlns="http://www.mozilla.org/xbl" >
+ <binding id="button">
+ <content>
+ <button xmlns="http://www.w3.org/1999/xhtml">a button</button>
+ </content>
+ </binding>
+ </bindings>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Insert a not accessible bound element containing an accessible element
+ * in anonymous content.
+ */
+ function insertBinding(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+
+ function getButtonFromBinding(aNode)
+ {
+ try { return document.getAnonymousNodes(aNode.firstChild)[0]; }
+ catch (e) { return null; }
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getButtonFromBinding, this.containerNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function insertBinding_invoke()
+ {
+ var span = document.createElement("span");
+ span.setAttribute("style", "-moz-binding:url(#button)");
+ this.containerNode.appendChild(span);
+ }
+
+ this.getID = function insertBinding_getID()
+ {
+ return "insert button binding";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new insertBinding("testContainer"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=646369"
+ title="UpdateTree should rely on accessible tree walker rather than DOM tree traversal">
+ Mozilla Bug 646369</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="testContainer"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_namechange.html b/accessible/tests/mochitest/events/test_namechange.html
new file mode 100644
index 000000000..935d865e9
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_namechange.html
@@ -0,0 +1,123 @@
+<html>
+
+<head>
+ <title>Accessible name change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function setAttr(aID, aAttr, aValue, aChecker)
+ {
+ this.eventSeq = [ aChecker ];
+ this.invoke = function setAttr_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ /**
+ * No name change on an accessible, because the accessible is recreated.
+ */
+ function setAttr_recreate(aID, aAttr, aValue)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aID)),
+ new invokerChecker(EVENT_SHOW, aID)
+ ];
+ this.invoke = function setAttr_recreate_invoke()
+ {
+ todo(false, "No accessible recreation should happen, just name change event");
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_recreate_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setAttr("tst1", "aria-label", "hi",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "aria-labelledby", "display",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "alt", "alt",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+
+ gQueue.push(new setAttr("tst2", "aria-labelledby", "display",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+ gQueue.push(new setAttr("tst2", "alt", "alt",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+ gQueue.push(new setAttr("tst2", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+ gQueue.push(new setAttr_recreate("tst3", "alt", "alt"));
+ gQueue.push(new setAttr("tst3", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst3")));
+
+ gQueue.push(new setAttr("tst4", "title", "title",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst4")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=991969"
+ title="Event not fired when description changes">
+ Bug 991969
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <img id="tst1">
+ <img id="tst2">
+ <img id="tst3">
+ <img id="tst4">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_namechange.xul b/accessible/tests/mochitest/events/test_namechange.xul
new file mode 100644
index 000000000..9d688585c
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_namechange.xul
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ /**
+ * Check name changed a11y event.
+ */
+ function nameChangeChecker(aMsg, aID)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ return getAccessible(aID);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + " name changed";
+ }
+ }
+
+ function changeRichListItemChild()
+ {
+ this.invoke = function changeRichListItemChild_invoke()
+ {
+ getNode('childcontent').setAttribute('value', 'Changed.');
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("changeRichListItemChild: ", "listitem")
+ ];
+
+ this.getID = function changeRichListItemChild_getID()
+ {
+ return "changeRichListItemChild";
+ }
+ }
+
+ function doTest()
+ {
+ var queue = new eventQueue();
+ queue.push(new changeRichListItemChild());
+ queue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=986054"
+ title="Propagate name change events">
+ Mozilla Bug 986054
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <richlistbox>
+ <richlistitem id="listitem">
+ <description id="childcontent" value="This will be changed."/>
+ </richlistitem>
+ </richlistbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_scroll.xul b/accessible/tests/mochitest/events/test_scroll.xul
new file mode 100644
index 000000000..e33161376
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -0,0 +1,131 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ function getAnchorJumpInTabDocument(aTabIdx)
+ {
+ var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+ return tabDoc.querySelector("a[name='link1']");
+ }
+
+ function loadTab(aURL)
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new asyncInvokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function loadTab_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.getID = function loadTab_getID()
+ {
+ return "load tab: " + aURL;
+ }
+ }
+
+ function loadTabInBackground(aURL)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument, 1)
+ ];
+
+ this.invoke = function loadTabInBackground_invoke()
+ {
+ tabBrowser().loadOneTab(aURL, null, "", null, true);
+ }
+
+ this.getID = function loadTabInBackground_getID()
+ {
+ return "load tab in background: " + aURL;
+ }
+ }
+
+ function switchToBackgroundTab()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function switchToBackgroundTab_invoke()
+ {
+ tabBrowser().selectTabAtIndex(1);
+ }
+
+ this.getID = function switchToBackgroundTab_getID()
+ {
+ return "switch to background tab";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ var url = "http://mochi.test:8888/a11y/accessible/tests/mochitest/events/scroll.html#link1";
+ gQueue.push(new loadTab(url));
+ gQueue.push(new loadTabInBackground(url));
+ gQueue.push(new switchToBackgroundTab());
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=691734"
+ title="Make sure scrolling start event is fired when document receive focus">
+ Mozilla Bug 691734
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_scroll_caret.xul b/accessible/tests/mochitest/events/test_scroll_caret.xul
new file mode 100644
index 000000000..57e27747f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_scroll_caret.xul
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ function getAnchorJumpInTabDocument(aTabIdx)
+ {
+ var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+ return tabDoc.querySelector("h1[id='heading_1']");
+ }
+
+ function loadTab(aURL)
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new asyncCaretMoveChecker(0, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function loadTab_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.getID = function loadTab_getID()
+ {
+ return "load tab: " + aURL;
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ var url = "http://mochi.test:8888/a11y/accessible/tests/mochitest/events/scroll.html#heading_1";
+ gQueue.push(new loadTab(url));
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1056459"
+ title="Make sure caret move event is fired when document receive focus">
+ Mozilla Bug 1056459
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_selection.html b/accessible/tests/mochitest/events/test_selection.html
new file mode 100644
index 000000000..de25fedc3
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible selection event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // open combobox
+ gQueue.push(new synthClick("combobox",
+ new invokerChecker(EVENT_FOCUS, "cb1_item1")));
+ gQueue.push(new synthDownKey("cb1_item1",
+ selChangeSeq("cb1_item1", "cb1_item2")));
+
+ // closed combobox
+ gQueue.push(new synthEscapeKey("combobox",
+ new invokerChecker(EVENT_FOCUS, "combobox")));
+ gQueue.push(new synthDownKey("cb1_item2",
+ selChangeSeq("cb1_item2", "cb1_item3")));
+
+ // listbox
+ gQueue.push(new synthClick("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+ gQueue.push(new synthDownKey("lb1_item1",
+ selChangeSeq("lb1_item1", "lb1_item2")));
+
+ // multiselectable listbox
+ gQueue.push(new synthClick("lb2_item1",
+ selChangeSeq(null, "lb2_item1")));
+ gQueue.push(new synthDownKey("lb2_item1",
+ selAddSeq("lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthUpKey("lb2_item2",
+ selRemoveSeq("lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+ selRemoveSeq("lb2_item1")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+ title="Incorrect selection events in HTML, XUL and ARIA">
+ Bug 414302
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=810268"
+ title="There's no way to know unselected item when selection in single selection was changed">
+ Bug 810268
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="combobox">
+ <option id="cb1_item1" value="mushrooms">mushrooms
+ <option id="cb1_item2" value="greenpeppers">green peppers
+ <option id="cb1_item3" value="onions" id="onions">onions
+ <option id="cb1_item4" value="tomatoes">tomatoes
+ <option id="cb1_item5" value="olives">olives
+ </select>
+
+ <select id="listbox" size=5>
+ <option id="lb1_item1" value="mushrooms">mushrooms
+ <option id="lb1_item2" value="greenpeppers">green peppers
+ <option id="lb1_item3" value="onions" id="onions">onions
+ <option id="lb1_item4" value="tomatoes">tomatoes
+ <option id="lb1_item5" value="olives">olives
+ </select>
+
+ <p>Pizza</p>
+ <select id="listbox2" multiple size=5>
+ <option id="lb2_item1" value="mushrooms">mushrooms
+ <option id="lb2_item2" value="greenpeppers">green peppers
+ <option id="lb2_item3" value="onions" id="onions">onions
+ <option id="lb2_item4" value="tomatoes">tomatoes
+ <option id="lb2_item5" value="olives">olives
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_selection.xul b/accessible/tests/mochitest/events/test_selection.xul
new file mode 100644
index 000000000..2b0c388ff
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.xul
@@ -0,0 +1,255 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Selection event tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ function advanceTab(aTabsID, aDirection, aNextTabID)
+ {
+ var eventSeq1 = [
+ new invokerChecker(EVENT_SELECTION, aNextTabID)
+ ]
+ defineScenario(this, eventSeq1);
+
+ var eventSeq2 = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aNextTabID)),
+ new invokerChecker(EVENT_SHOW, aNextTabID)
+ ];
+ defineScenario(this, eventSeq2);
+
+ this.invoke = function advanceTab_invoke()
+ {
+ todo(false, "No accessible recreation should happen, just selection event");
+ getNode(aTabsID).advanceSelectedTab(aDirection, true);
+ }
+
+ this.getID = function synthFocus_getID()
+ {
+ return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
+ }
+ }
+
+ function select4FirstItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
+ ];
+
+ this.invoke = function select4FirstItems_invoke()
+ {
+ synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
+ synthesizeKey("VK_DOWN", { shiftKey: true });
+ synthesizeKey("VK_DOWN", { shiftKey: true });
+ }
+
+ this.getID = function select4FirstItems_getID()
+ {
+ return "select 4 first items for " + prettyName(aID);
+ }
+ }
+
+ function unselect4FirstItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
+ ];
+
+ this.invoke = function unselect4FirstItems_invoke()
+ {
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey(" ", { ctrlKey: true }); // unselect first item
+ }
+
+ this.getID = function unselect4FirstItems_getID()
+ {
+ return "unselect 4 first items for " + prettyName(aID);
+ }
+ }
+
+ function selectAllItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+ ];
+
+ this.invoke = function selectAllItems_invoke()
+ {
+ synthesizeKey("VK_END", { shiftKey: true });
+ }
+
+ this.getID = function selectAllItems_getID()
+ {
+ return "select all items for " + prettyName(aID);
+ }
+ }
+
+ function unselectAllItemsButFirst(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+ ];
+
+ this.invoke = function unselectAllItemsButFirst_invoke()
+ {
+ synthesizeKey("VK_HOME", { shiftKey: true });
+ }
+
+ this.getID = function unselectAllItemsButFirst_getID()
+ {
+ return "unselect all items for " + prettyName(aID);
+ }
+ }
+
+ function unselectSelectItem(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
+ ];
+
+ this.invoke = function unselectSelectItem_invoke()
+ {
+ synthesizeKey(" ", { ctrlKey: true }); // select item
+ synthesizeKey(" ", { ctrlKey: true }); // unselect item
+ }
+
+ this.getID = function unselectSelectItem_getID()
+ {
+ return "unselect and then select first item for " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ //enableLogging("events");
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tabbox
+ gQueue.push(new advanceTab("tabs", 1, "tab3"));
+
+ //////////////////////////////////////////////////////////////////////////
+ // listbox
+ gQueue.push(new synthClick("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+ gQueue.push(new synthDownKey("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+ //////////////////////////////////////////////////////////////////////////
+ // multiselectable listbox
+ gQueue.push(new synthClick("lb2_item1",
+ new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+ gQueue.push(new synthDownKey("lb2_item1",
+ new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthUpKey("lb2_item2",
+ new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+ new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+
+ //////////////////////////////////////////////////////////////////////////
+ // selection event coalescence
+
+ // fire 4 selection_add events
+ gQueue.push(new select4FirstItems("listbox2"));
+ // fire 4 selection_remove events
+ gQueue.push(new unselect4FirstItems("listbox2"));
+ // fire selection_within event
+ gQueue.push(new selectAllItems("listbox2"));
+ // fire selection_within event
+ gQueue.push(new unselectAllItemsButFirst("listbox2"));
+ // fire selection_remove/add events
+ gQueue.push(new unselectSelectItem("listbox2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=414302"
+ title="Incorrect selection events in HTML, XUL and ARIA">
+ Mozilla Bug 414302
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <tabbox id="tabbox" selectedIndex="1">
+ <tabs id="tabs">
+ <tab id="tab1" label="tab1"/>
+ <tab id="tab2" label="tab2"/>
+ <tab id="tab3" label="tab3"/>
+ <tab id="tab4" label="tab4"/>
+ </tabs>
+ <tabpanels>
+ <tabpanel><!-- tabpanel First elements go here --></tabpanel>
+ <tabpanel><button id="b1" label="b1"/></tabpanel>
+ <tabpanel><button id="b2" label="b2"/></tabpanel>
+ <tabpanel></tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ <listbox id="listbox">
+ <listitem id="lb1_item1" label="item1"/>
+ <listitem id="lb1_item2" label="item2"/>
+ </listbox>
+
+ <listbox id="listbox2" seltype="multiple">
+ <listitem id="lb2_item1" label="item1"/>
+ <listitem id="lb2_item2" label="item2"/>
+ <listitem id="lb2_item3" label="item3"/>
+ <listitem id="lb2_item4" label="item4"/>
+ <listitem id="lb2_item5" label="item5"/>
+ <listitem id="lb2_item6" label="item6"/>
+ <listitem id="lb2_item7" label="item7"/>
+ </listbox>
+
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_selection_aria.html b/accessible/tests/mochitest/events/test_selection_aria.html
new file mode 100644
index 000000000..aabee46fd
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+ <title>ARIA selection event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function selectItem(aSelectID, aItemID)
+ {
+ this.selectNode = getNode(aSelectID);
+ this.itemNode = getNode(aItemID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION, aItemID)
+ ];
+
+ this.invoke = function selectItem_invoke() {
+ var itemNode = this.selectNode.querySelector("*[aria-selected='true']");
+ if (itemNode)
+ itemNode.removeAttribute("aria-selected");
+
+ this.itemNode.setAttribute("aria-selected", "true");
+ }
+
+ this.getID = function selectItem_getID()
+ {
+ return "select item " + prettyName(aItemID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new selectItem("tablist", "tab1"));
+ gQueue.push(new selectItem("tablist", "tab2"));
+
+ gQueue.push(new selectItem("tree", "treeitem1"));
+ gQueue.push(new selectItem("tree", "treeitem1a"));
+ gQueue.push(new selectItem("tree", "treeitem1a1"));
+
+ gQueue.push(new selectItem("tree2", "tree2item1"));
+ gQueue.push(new selectItem("tree2", "tree2item1a"));
+ gQueue.push(new selectItem("tree2", "tree2item1a1"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=569653"
+ title="Make selection events async">
+ Mozilla Bug 569653
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=804040"
+ title="Selection event not fired when selection of ARIA tab changes">
+ Mozilla Bug 804040
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="tablist" id="tablist">
+ <div role="tab" id="tab1">tab1</div>
+ <div role="tab" id="tab2">tab2</div>
+ </div>
+
+ <div id="tree" role="tree">
+ <div id="treeitem1" role="treeitem">Canada
+ <div id="treeitem1a" role="treeitem">- Ontario
+ <div id="treeitem1a1" role="treeitem">-- Toronto</div>
+ </div>
+ <div id="treeitem1b" role="treeitem">- Manitoba</div>
+ </div>
+ <div id="treeitem2" role="treeitem">Germany</div>
+ <div id="treeitem3" role="treeitem">Russia</div>
+ </div>
+
+ <div id="tree2" role="tree" aria-multiselectable="true">
+ <div id="tree2item1" role="treeitem">Canada
+ <div id="tree2item1a" role="treeitem">- Ontario
+ <div id="tree2item1a1" role="treeitem">-- Toronto</div>
+ </div>
+ <div id="tree2item1b" role="treeitem">- Manitoba</div>
+ </div>
+ <div id="tree2item2" role="treeitem">Germany</div>
+ <div id="tree2item3" role="treeitem">Russia</div>
+ </div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_statechange.html b/accessible/tests/mochitest/events/test_statechange.html
new file mode 100644
index 000000000..f4557376b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -0,0 +1,287 @@
+<html>
+
+<head>
+ <title>Accessible state change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function makeEditableDoc(aDocNode, aIsEnabled)
+ {
+ this.DOMNode = aDocNode;
+
+ this.invoke = function editabledoc_invoke() {
+ // Note: this should fire an EVENT_STATE_CHANGE
+ this.DOMNode.designMode = 'on';
+ };
+
+ this.check = function editabledoc_check(aEvent) {
+
+ testStates(aDocNode, 0, EXT_STATE_EDITABLE);
+
+ var event = null;
+ try {
+ var event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
+ } catch (e) {
+ ok(false, "State change event was expected");
+ }
+
+ if (!event) { return; }
+
+ ok(event.isExtraState, "Extra state change was expected");
+ is(event.state, EXT_STATE_EDITABLE, "Wrong state of statechange event");
+ ok(event.isEnabled, "Expected editable state to be enabled");
+ }
+
+ this.getID = function editabledoc_getID() {
+ return prettyName(aDocNode) + " editable state changed";
+ };
+ }
+
+ function invalidInput(aNodeOrID)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function invalidInput_invoke() {
+ // Note: this should fire an EVENT_STATE_CHANGE
+ this.DOMNode.value = "I am not an email";
+ };
+
+ this.check = function invalidInput_check() {
+ testStates(aNodeOrID, STATE_INVALID);
+ };
+
+ this.getID = function invalidInput_getID() {
+ return prettyName(aNodeOrID) + " became invalid";
+ };
+ }
+
+ function changeCheckInput(aID, aIsChecked)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(STATE_CHECKED, false, aIsChecked, this.DOMNode)
+ ];
+
+ this.invoke = function changeCheckInput_invoke()
+ {
+ this.DOMNode.checked = aIsChecked;
+ }
+
+ this.getID = function changeCheckInput_getID()
+ {
+ return "change checked state to '" + aIsChecked + "' for " +
+ prettyName(aID);
+ }
+ }
+
+ function stateChangeOnFileInput(aID, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.fileControlNode = getNode(aID);
+ this.fileControl = getAccessible(this.fileControlNode);
+ this.browseButton = this.fileControl.firstChild;
+ // No state change events on the label.
+
+ this.invoke = function stateChangeOnFileInput_invoke()
+ {
+ this.fileControlNode.setAttribute(aAttr, aValue);
+ }
+
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.fileControl),
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.browseButton)
+ ];
+
+ this.getID = function stateChangeOnFileInput_getID()
+ {
+ return "inherited state change on file input on attribute '" + aAttr + "' change";
+ }
+ }
+
+ function dupeStateChange(aID, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, getNode(aID))
+ ];
+
+ this.invoke = function dupeStateChange_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function dupeStateChange_getID()
+ {
+ return "duped state change events";
+ }
+ }
+
+ function oppositeStateChange(aID, aAttr, aState, aIsExtraState)
+ {
+ this.eventSeq = [ ];
+ this.unexpectedEventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, false, getNode(aID)),
+ new stateChangeChecker(aState, aIsExtraState, true, getNode(aID))
+ ];
+
+ this.invoke = function oppositeStateChange_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, "false");
+ getNode(aID).setAttribute(aAttr, "true");
+ }
+
+ this.getID = function oppositeStateChange_getID()
+ {
+ return "opposite state change events";
+ }
+ }
+
+ /**
+ * Change concomitant ARIA and native attribute at once.
+ */
+ function echoingStateChange(aID, aARIAAttr, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, getNode(aID))
+ ];
+
+ this.invoke = function echoingStateChange_invoke()
+ {
+ if (aValue == null) {
+ getNode(aID).removeAttribute(aARIAAttr);
+ getNode(aID).removeAttribute(aAttr);
+
+ } else {
+ getNode(aID).setAttribute(aARIAAttr, aValue);
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+ }
+
+ this.getID = function echoingStateChange_getID()
+ {
+ return "enchoing ARIA and native attributes change";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // var gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
+
+ // Test delayed editable state change
+ var doc = document.getElementById("iframe").contentDocument;
+ gQueue.push(new makeEditableDoc(doc));
+
+ // invalid state change
+ gQueue.push(new invalidInput("email"));
+
+ // checked state change
+ gQueue.push(new changeCheckInput("checkbox", true));
+ gQueue.push(new changeCheckInput("checkbox", false));
+ gQueue.push(new changeCheckInput("radio", true));
+ gQueue.push(new changeCheckInput("radio", false));
+
+ // file input inherited state changes
+ gQueue.push(new stateChangeOnFileInput("file", "aria-busy", "true",
+ STATE_BUSY, false, true));
+ gQueue.push(new stateChangeOnFileInput("file", "aria-required", "true",
+ STATE_REQUIRED, false, true));
+ gQueue.push(new stateChangeOnFileInput("file", "aria-invalid", "true",
+ STATE_INVALID, false, true));
+
+ gQueue.push(new dupeStateChange("div", "aria-busy", "true",
+ STATE_BUSY, false, true));
+ gQueue.push(new oppositeStateChange("div", "aria-busy",
+ STATE_BUSY, false));
+
+ gQueue.push(new echoingStateChange("text1", "aria-disabled", "disabled", "true",
+ EXT_STATE_ENABLED, true, false));
+ gQueue.push(new echoingStateChange("text1", "aria-disabled", "disabled", null,
+ EXT_STATE_ENABLED, true, true));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=564471"
+ title="Make state change events async">
+ Bug 564471
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=555728"
+ title="Fire a11y event based on HTML5 constraint validation">
+ Bug 555728
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
+ title="File input control should be propogate states to descendants">
+ Bug 699017
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=788389"
+ title="Fire statechange event whenever checked state is changed not depending on focused state">
+ Bug 788389
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=926812"
+ title="State change event not fired when both disabled and aria-disabled are toggled">
+ Bug 926812
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer">
+ <iframe id="iframe"></iframe>
+ </div>
+
+ <input id="email" type='email'>
+
+ <input id="checkbox" type="checkbox">
+ <input id="radio" type="radio">
+
+ <input id="file" type="file">
+
+ <div id="div"></div>
+
+ <input id="text1">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_text.html b/accessible/tests/mochitest/events/test_text.html
new file mode 100644
index 000000000..d992d6c64
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_text.html
@@ -0,0 +1,339 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Base text remove invoker and checker.
+ */
+ function textRemoveInvoker(aID, aStart, aEnd, aText)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false)
+ ];
+ }
+
+ function textInsertInvoker(aID, aStart, aEnd, aText)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, true)
+ ];
+ }
+
+ /**
+ * Remove inaccessible child node containing accessibles.
+ */
+ function removeChildSpan(aID)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, 0, 5, "33322");
+
+ this.invoke = function removeChildSpan_invoke()
+ {
+ // remove HTML span, a first child of the node
+ this.DOMNode.removeChild(this.DOMNode.firstChild);
+ }
+
+ this.getID = function removeChildSpan_getID()
+ {
+ return "Remove inaccessible span containing accessible nodes" + prettyName(aID);
+ }
+ }
+
+ /**
+ * Insert inaccessible child node containing accessibles.
+ */
+ function insertChildSpan(aID, aInsertAllTogether)
+ {
+ this.__proto__ = new textInsertInvoker(aID, 0, 5, "33322");
+
+ this.invoke = function insertChildSpan_invoke()
+ {
+ // <span><span>333</span><span>22</span></span>
+ if (aInsertAllTogether) {
+ var topSpan = document.createElement("span");
+ var fSpan = document.createElement("span");
+ fSpan.textContent = "333";
+ topSpan.appendChild(fSpan);
+ var sSpan = document.createElement("span");
+ sSpan.textContent = "22";
+ topSpan.appendChild(sSpan);
+
+ this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
+
+ } else {
+ var topSpan = document.createElement("span");
+ this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
+
+ var fSpan = document.createElement("span");
+ fSpan.textContent = "333";
+ topSpan.appendChild(fSpan);
+
+ var sSpan = document.createElement("span");
+ sSpan.textContent = "22";
+ topSpan.appendChild(sSpan);
+ }
+ }
+
+ this.getID = function insertChildSpan_getID()
+ {
+ return "Insert inaccessible span containing accessibles" +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove child embedded accessible.
+ */
+ function removeChildDiv(aID)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, 5, 6, kEmbedChar);
+
+ this.invoke = function removeChildDiv_invoke()
+ {
+ var childDiv = this.DOMNode.childNodes[1];
+
+ // Ensure accessible is created to get text remove event when it's
+ // removed.
+ getAccessible(childDiv);
+
+ this.DOMNode.removeChild(childDiv);
+ }
+
+ this.getID = function removeChildDiv_getID()
+ {
+ return "Remove accessible div from the middle of text accessible " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Insert child embedded accessible.
+ */
+ function insertChildDiv(aID)
+ {
+ this.__proto__ = new textInsertInvoker(aID, 5, 6, kEmbedChar);
+
+ this.invoke = function insertChildDiv_invoke()
+ {
+ var childDiv = document.createElement("div");
+ this.DOMNode.insertBefore(childDiv, this.DOMNode.childNodes[1]);
+ }
+
+ this.getID = function insertChildDiv_getID()
+ {
+ return "Insert accessible div into the middle of text accessible " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove children from text container from first to last child or vice
+ * versa.
+ */
+ function removeChildren(aID, aLastToFirst, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.invoke = function removeChildren_invoke()
+ {
+ if (aLastToFirst) {
+ while (this.DOMNode.firstChild)
+ this.DOMNode.removeChild(this.DOMNode.lastChild);
+ } else {
+ while (this.DOMNode.firstChild)
+ this.DOMNode.removeChild(this.DOMNode.firstChild);
+ }
+ }
+
+ this.getID = function removeChildren_getID()
+ {
+ return "remove children of " + prettyName(aID) +
+ (aLastToFirst ? " from last to first" : " from first to last");
+ }
+ }
+
+ /**
+ * Remove text from HTML input.
+ */
+ function removeTextFromInput(aID, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.eventSeq.push(new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode));
+
+ this.invoke = function removeTextFromInput_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.DOMNode.setSelectionRange(aStart, aEnd);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromInput_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Add text into HTML input.
+ */
+ function insertTextIntoInput(aID, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textInsertInvoker(aID, aStart, aEnd, aText);
+
+ this.eventSeq.push(new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode));
+
+ this.invoke = function insertTextIntoInput_invoke()
+ {
+ this.DOMNode.focus();
+ synthesizeKey("a", {});
+ }
+
+ this.getID = function insertTextIntoInput_getID()
+ {
+ return "Insert text to " + aStart + " for " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove text data from text node of editable area.
+ */
+ function removeTextFromEditable(aID, aStart, aEnd, aText, aTextNode)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.invoke = function removeTextFromEditable_invoke()
+ {
+ this.DOMNode.focus();
+
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.setStart(this.textNode, aStart);
+ range.setEnd(this.textNode, aEnd);
+ selection.addRange(range);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromEditable_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+
+ this.textNode = getNode(aTextNode);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Text remove event on inaccessible child HTML span removal containing
+ // accessible text nodes.
+ gQueue.push(new removeChildSpan("p"));
+ gQueue.push(new insertChildSpan("p"), true);
+ gQueue.push(new insertChildSpan("p"), false);
+
+ // Remove embedded character.
+ gQueue.push(new removeChildDiv("div"));
+ gQueue.push(new insertChildDiv("div"));
+
+ // Remove all children.
+ var text = kEmbedChar + "txt" + kEmbedChar;
+ gQueue.push(new removeChildren("div2", true, 0, 5, text));
+ gQueue.push(new removeChildren("div3", false, 0, 5, text));
+
+ // Text remove from text node within hypertext accessible.
+ gQueue.push(new removeTextFromInput("input", 1, 3, "al"));
+ gQueue.push(new insertTextIntoInput("input", 1, 2, "a"));
+
+ // bug 570691
+ todo(false, "Fix text change events from editable area, see bug 570691");
+ //var textNode = getNode("editable").firstChild;
+ //gQueue.push(new removeTextFromEditable("editable", 1, 3, "al", textNode));
+ //textNode = getNode("editable2").firstChild.firstChild;
+ //gQueue.push(new removeTextFromEditable("editable2", 1, 3, "al", textNode));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566293"
+ title=" wrong length of text remove event when inaccessible node containing accessible nodes is removed">
+ Mozilla Bug 566293
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570710"
+ title="Avoid extra array traversal during text event creation">
+ Mozilla Bug 570710
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=574003"
+ title="Coalesce text events on nodes removal">
+ Mozilla Bug 574003
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=575052"
+ title="Cache text offsets within hypertext accessible">
+ Mozilla Bug 575052
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"
+ title="Rework accessible tree update code">
+ Mozilla Bug 570275
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p"><span><span>333</span><span>22</span></span>1111</p>
+ <div id="div">hello<div>hello</div>hello</div>
+ <div id="div2"><div>txt</div>txt<div>txt</div></div>
+ <div id="div3"><div>txt</div>txt<div>txt</div></div>
+ <input id="input" value="value">
+ <div contentEditable="true" id="editable">value</div>
+ <div contentEditable="true" id="editable2"><span>value</span></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_text_alg.html b/accessible/tests/mochitest/events/test_text_alg.html
new file mode 100644
index 000000000..97428fb3b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_text_alg.html
@@ -0,0 +1,249 @@
+<html>
+
+<head>
+ <title>Accessible text update algorithm testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ const kRemoval = false;
+ const kInsertion = true;
+ const kUnexpected = true;
+
+ function changeText(aContainerID, aValue, aEventList)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.textNode = this.containerNode.firstChild;
+ this.textData = this.textNode.data;
+
+ this.eventSeq = [ ];
+ this.unexpectedEventSeq = [ ];
+
+ for (var i = 0; i < aEventList.length; i++) {
+ var event = aEventList[i];
+
+ var isInserted = event[0];
+ var str = event[1];
+ var offset = event[2];
+ var checker = new textChangeChecker(this.containerNode, offset,
+ offset + str.length, str,
+ isInserted);
+
+ if (event[3] == kUnexpected)
+ this.unexpectedEventSeq.push(checker);
+ else
+ this.eventSeq.push(checker);
+ }
+
+ this.invoke = function changeText_invoke()
+ {
+ this.textNode.data = aValue;
+ }
+
+ this.getID = function changeText_getID()
+ {
+ return "change text '" + shortenString(this.textData) + "' -> '" +
+ shortenString(this.textNode.data) + "' for " +
+ prettyName(this.containerNode);
+ }
+ }
+
+ function expStr(x, doublings)
+ {
+ for (var i = 0; i < doublings; ++i)
+ x = x + x;
+ return x;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ //////////////////////////////////////////////////////////////////////////
+ // wqrema -> tqb: substitution coalesced with removal
+
+ var events = [
+ [ kRemoval, "w", 0 ], // wqrema -> qrema
+ [ kInsertion, "t", 0], // qrema -> tqrema
+ [ kRemoval, "rema", 2 ], // tqrema -> tq
+ [ kInsertion, "b", 2] // tq -> tqb
+ ];
+ gQueue.push(new changeText("p1", "tqb", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // b -> insa: substitution coalesced with insertion (complex substitution)
+
+ events = [
+ [ kRemoval, "b", 0 ], // b ->
+ [ kInsertion, "insa", 0] // -> insa
+ ];
+ gQueue.push(new changeText("p2", "insa", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> def: coalesced substitutions
+
+ events = [
+ [ kRemoval, "abc", 0 ], // abc ->
+ [ kInsertion, "def", 0] // -> def
+ ];
+ gQueue.push(new changeText("p3", "def", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcabc -> abcDEFabc: coalesced insertions
+
+ events = [
+ [ kInsertion, "DEF", 3] // abcabc -> abcDEFabc
+ ];
+ gQueue.push(new changeText("p4", "abcDEFabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> defabc: insertion into begin
+
+ events = [
+ [ kInsertion, "def", 0] // abc -> defabc
+ ];
+ gQueue.push(new changeText("p5", "defabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> abcdef: insertion into end
+
+ events = [
+ [ kInsertion, "def", 3] // abc -> abcdef
+ ];
+ gQueue.push(new changeText("p6", "abcdef", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // defabc -> abc: removal from begin
+
+ events = [
+ [ kRemoval, "def", 0] // defabc -> abc
+ ];
+ gQueue.push(new changeText("p7", "abc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcdef -> abc: removal from the end
+
+ events = [
+ [ kRemoval, "def", 3] // abcdef -> abc
+ ];
+ gQueue.push(new changeText("p8", "abc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcDEFabc -> abcabc: coalesced removals
+
+ events = [
+ [ kRemoval, "DEF", 3] // abcDEFabc -> abcabc
+ ];
+ gQueue.push(new changeText("p9", "abcabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // !abcdef@ -> @axbcef!: insertion, deletion and substitutions
+
+ events = [
+ [ kRemoval, "!", 0 ], // !abcdef@ -> abcdef@
+ [ kInsertion, "@", 0], // abcdef@ -> @abcdef@
+ [ kInsertion, "x", 2 ], // @abcdef@ -> @axbcdef@
+ [ kRemoval, "d", 5], // @axbcdef@ -> @axbcef@
+ [ kRemoval, "@", 7 ], // @axbcef@ -> @axbcef
+ [ kInsertion, "!", 7 ], // @axbcef -> @axbcef!
+ ];
+ gQueue.push(new changeText("p10", "@axbcef!", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // meilenstein -> levenshtein: insertion, complex and simple substitutions
+
+ events = [
+ [ kRemoval, "m", 0 ], // meilenstein -> eilenstein
+ [ kInsertion, "l", 0], // eilenstein -> leilenstein
+ [ kRemoval, "il", 2 ], // leilenstein -> leenstein
+ [ kInsertion, "v", 2], // leenstein -> levenstein
+ [ kInsertion, "h", 6 ], // levenstein -> levenshtein
+ ];
+ gQueue.push(new changeText("p11", "levenshtein", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // long strings, remove/insert pair as the old string was replaced on
+ // new one
+
+ var longStr1 = expStr("x", 16);
+ var longStr2 = expStr("X", 16);
+
+ var newStr = "a" + longStr1 + "b", insStr = longStr1, rmStr = "";
+ events = [
+ [ kRemoval, rmStr, 1, kUnexpected ],
+ [ kInsertion, insStr, 1 ]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ newStr = "a" + longStr2 + "b", insStr = longStr2, rmStr = longStr1;
+ events = [
+ [ kRemoval, rmStr, 1 ],
+ [ kInsertion, insStr, 1]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ newStr = "ab", insStr = "", rmStr = longStr2;
+ events = [
+ [ kRemoval, rmStr, 1 ],
+ [ kInsertion, insStr, 1, kUnexpected ]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660"
+ title="Cache rendered text on a11y side">
+ Mozilla Bug 626660
+ </a>
+ <br>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <p id="p1">wqrema</p>
+ <p id="p2">b</p>
+ <p id="p3">abc</p>
+ <p id="p4">abcabc</p>
+ <p id="p5">abc</p>
+ <p id="p6">abc</p>
+ <p id="p7">defabc</p>
+ <p id="p8">abcdef</p>
+ <p id="p9">abcDEFabc</p>
+ <p id="p10">!abcdef@</p>
+ <p id="p11">meilenstein</p>
+ <p id="p12">ab</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_textattrchange.html b/accessible/tests/mochitest/events/test_textattrchange.html
new file mode 100644
index 000000000..a69a8df15
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textattrchange.html
@@ -0,0 +1,115 @@
+<html>
+
+<head>
+ <title>Text attribute changed event for misspelled text</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
+
+ function spelledTextInvoker(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_ATTRIBUTE_CHANGED, this.DOMNode)
+ ];
+
+ this.invoke = function spelledTextInvoker_invoke()
+ {
+ var editor = this.DOMNode.QueryInterface(nsIDOMNSEditableElement).editor;
+ var spellChecker = new InlineSpellChecker(editor);
+ spellChecker.enabled = true;
+
+ //var spellchecker = editor.getInlineSpellChecker(true);
+ //spellchecker.enableRealTimeSpell = true;
+
+ this.DOMNode.value = "valid text inalid tixt";
+ }
+
+ this.finalCheck = function spelledTextInvoker_finalCheck()
+ {
+ var defAttrs = buildDefaultTextAttrs(this.DOMNode, kInputFontSize,
+ kNormalFontWeight,
+ kInputFontFamily);
+ testDefaultTextAttrs(aID, defAttrs);
+
+ var attrs = { };
+ var misspelledAttrs = {
+ "invalid": "spelling"
+ };
+
+ testTextAttrs(aID, 0, attrs, defAttrs, 0, 11);
+ testTextAttrs(aID, 11, misspelledAttrs, defAttrs, 11, 17);
+ testTextAttrs(aID, 17, attrs, defAttrs, 17, 18);
+ testTextAttrs(aID, 18, misspelledAttrs, defAttrs, 18, 22);
+ }
+
+ this.getID = function spelledTextInvoker_getID()
+ {
+ return "text attribute change for misspelled text";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Synth focus before spellchecking turning on to make sure editor
+ // gets a time for initialization.
+
+ gQueue = new eventQueue();
+ gQueue.push(new synthFocus("input"));
+ gQueue.push(new spelledTextInvoker("input"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=345759"
+ title="Implement text attributes">
+ Mozilla Bug 345759
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input"/>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_textselchange.html b/accessible/tests/mochitest/events/test_textselchange.html
new file mode 100644
index 000000000..92ac30423
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textselchange.html
@@ -0,0 +1,86 @@
+<html>
+
+<head>
+ <title>Accessible text selection change events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function getOnclickSeq(aID)
+ {
+ return [
+ new caretMoveChecker(0, aID),
+ new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+ ];
+ }
+
+ function doTests()
+ {
+ // test caret move events and caret offsets
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthClick("c1_p1", getOnclickSeq("c1_p1")));
+ gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true }));
+ gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true }));
+
+ gQueue.push(new synthClick("ta1", getOnclickSeq("ta1")));
+ gQueue.push(new synthRightKey("ta1",
+ new textSelectionChecker("ta1", 0, 1),
+ { shiftKey: true }));
+ gQueue.push(new synthLeftKey("ta1",
+ [new textSelectionChecker("ta1", 0, 0),
+ new caretMoveChecker(0, "ta1")]));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=762934"
+ title="Text selection change event has a wrong target when selection is spanned through several objects">
+ Bug 762934
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=956032"
+ title="Text selection change event missed when selected text becomes unselected">
+ Bug 956032
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1" contentEditable="true">
+ <p id="c1_p1">paragraph</p>
+ <p id="c1_p2">paragraph</p>
+ </div>
+
+ <textarea id="ta1">Hello world</textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_tree.xul b/accessible/tests/mochitest/events/test_tree.xul
new file mode 100644
index 000000000..797e7bf97
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -0,0 +1,348 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="DOM TreeRowCountChanged and a11y name change events.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker's checkers
+
+ /**
+ * Check TreeRowCountChanged event.
+ */
+ function rowCountChangedChecker(aMsg, aIdx, aCount)
+ {
+ this.type = "TreeRowCountChanged";
+ this.target = gTree;
+ this.check = function check(aEvent)
+ {
+ var propBag = aEvent.detail.QueryInterface(Components.interfaces.nsIPropertyBag2);
+ var index = propBag.getPropertyAsInt32("index");
+ is(index, aIdx, "Wrong 'index' data of 'treeRowCountChanged' event.");
+
+ var count = propBag.getPropertyAsInt32("count");
+ is(count, aCount, "Wrong 'count' data of 'treeRowCountChanged' event.");
+ }
+ this.getID = function getID()
+ {
+ return aMsg + "TreeRowCountChanged";
+ }
+ }
+
+ /**
+ * Check TreeInvalidated event.
+ */
+ function treeInvalidatedChecker(aMsg, aStartRow, aEndRow, aStartCol, aEndCol)
+ {
+ this.type = "TreeInvalidated";
+ this.target = gTree;
+ this.check = function check(aEvent)
+ {
+ var propBag = aEvent.detail.QueryInterface(Components.interfaces.nsIPropertyBag2);
+ try {
+ var startRow = propBag.getPropertyAsInt32("startrow");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ startRow = null;
+ }
+ is(startRow, aStartRow,
+ "Wrong 'startrow' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var endRow = propBag.getPropertyAsInt32("endrow");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ endRow = null;
+ }
+ is(endRow, aEndRow,
+ "Wrong 'endrow' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var startCol = propBag.getPropertyAsInt32("startcolumn");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ startCol = null;
+ }
+ is(startCol, aStartCol,
+ "Wrong 'startcolumn' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var endCol = propBag.getPropertyAsInt32("endcolumn");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ endCol = null;
+ }
+ is(endCol, aEndCol,
+ "Wrong 'endcolumn' of 'treeInvalidated' event on " + aMsg);
+ }
+ this.getID = function getID()
+ {
+ return "TreeInvalidated on " + aMsg;
+ }
+ }
+
+ /**
+ * Check name changed a11y event.
+ */
+ function nameChangeChecker(aMsg, aRow, aCol)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ var acc = getAccessible(gTree);
+
+ var tableAcc = getAccessible(acc, [nsIAccessibleTable]);
+ return tableAcc.getCellAt(aRow, aCol);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + "name changed";
+ }
+ }
+
+ /**
+ * Check name changed a11y event for a row.
+ */
+ function rowNameChangeChecker(aMsg, aRow)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ var acc = getAccessible(gTree);
+ return acc.getChildAt(aRow + 1);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + "name changed";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Set tree view.
+ */
+ function setTreeView()
+ {
+ this.invoke = function setTreeView_invoke()
+ {
+ gTreeBox.view = gView;
+ }
+
+ this.getID = function setTreeView_getID() { return "set tree view"; }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, gTree)
+ ];
+ };
+
+ /**
+ * Insert row at 0 index and checks TreeRowCountChanged and TreeInvalidated
+ * event.
+ */
+ function insertRow()
+ {
+ this.invoke = function insertRow_invoke()
+ {
+ gView.appendItem("last");
+ gTreeBox.rowCountChanged(0, 1);
+ }
+
+ this.eventSeq =
+ [
+ new rowCountChangedChecker("insertRow: ", 0, 1),
+ new treeInvalidatedChecker("insertRow", 0, 5, null, null)
+ ];
+
+ this.getID = function insertRow_getID()
+ {
+ return "insert row";
+ }
+ }
+
+ /**
+ * Invalidates first column and checks six name changed events for each
+ * treeitem plus TreeInvalidated event.
+ */
+ function invalidateColumn()
+ {
+ this.invoke = function invalidateColumn_invoke()
+ {
+ // Make sure accessible subtree of XUL tree is created otherwise no
+ // name change events for cell accessibles are emitted.
+ var tree = getAccessible(gTree);
+ var child = tree.firstChild;
+ var walkDown = true;
+ while (child != tree) {
+ if (walkDown) {
+ var grandChild = child.firstChild;
+ if (grandChild) {
+ child = grandChild;
+ continue;
+ }
+ }
+
+ var sibling = child.nextSibling;
+ if (sibling) {
+ child = sibling;
+ walkDown = true;
+ continue;
+ }
+
+ child = child.parent;
+ walkDown = false;
+ }
+
+ // Fire 'TreeInvalidated' event by InvalidateColumn()
+ var firstCol = gTree.columns.getFirstColumn();
+ for (var i = 0; i < gView.rowCount; i++)
+ gView.setCellText(i, firstCol, "hey " + String(i) + "x0");
+
+ gTreeBox.invalidateColumn(firstCol);
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("invalidateColumn: ", 0, 0),
+ new nameChangeChecker("invalidateColumn: ", 1, 0),
+ new nameChangeChecker("invalidateColumn: ", 2, 0),
+ new nameChangeChecker("invalidateColumn: ", 3, 0),
+ new nameChangeChecker("invalidateColumn: ", 4, 0),
+ new nameChangeChecker("invalidateColumn: ", 5, 0),
+ new treeInvalidatedChecker("invalidateColumn", null, null, 0, 0)
+ ];
+
+ this.getID = function invalidateColumn_getID()
+ {
+ return "invalidate column";
+ }
+ }
+
+ /**
+ * Invalidates second row and checks name changed event for first treeitem
+ * (note, there are two name changed events on linux due to different
+ * accessible tree for xul:tree element) plus TreeInvalidated event.
+ */
+ function invalidateRow()
+ {
+ this.invoke = function invalidateRow_invoke()
+ {
+ // Fire 'TreeInvalidated' event by InvalidateRow()
+ var colCount = gTree.columns.count;
+ var column = gTree.columns.getFirstColumn();
+ while (column) {
+ gView.setCellText(1, column, "aloha 1x" + String(column.index));
+ column = column.getNext();
+ }
+
+ gTreeBox.invalidateRow(1);
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("invalidateRow: ", 1, 0),
+ new nameChangeChecker("invalidateRow: ", 1, 1),
+ new rowNameChangeChecker("invalidateRow: ", 1),
+ new treeInvalidatedChecker("invalidateRow", 1, 1, null, null)
+ ];
+
+ this.getID = function invalidateRow_getID()
+ {
+ return "invalidate row";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ var gTree = null;
+ var gTreeBox = null;
+ var gTreeView = null;
+ var gQueue = null;
+
+ // gA11yEventDumpID = "debug";
+ gA11yEventDumpToConsole = true; // debuggin
+
+ function doTest()
+ {
+ // Initialize the tree
+ gTree = document.getElementById("tree");
+ gTreeBox = gTree.treeBoxObject;
+ gView = new nsTableTreeView(5);
+
+ // Perform actions
+ gQueue = new eventQueue();
+
+ gQueue.push(new setTreeView());
+ gQueue.push(new insertRow());
+ gQueue.push(new invalidateColumn());
+ gQueue.push(new invalidateRow());
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=368835"
+ title="Fire TreeViewChanged/TreeRowCountChanged events.">
+ Mozilla Bug 368835
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=308564"
+ title="No accessibility events when data in a tree row changes.">
+ Mozilla Bug 308564
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=739524"
+ title="replace TreeViewChanged DOM event on direct call from XUL tree.">
+ Mozilla Bug 739524
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=743568"
+ title="Thunderbird message list tree emitting incorrect focus signals after message deleted.">
+ Mozilla Bug 743568
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_valuechange.html b/accessible/tests/mochitest/events/test_valuechange.html
new file mode 100644
index 000000000..3464ffdeb
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_valuechange.html
@@ -0,0 +1,255 @@
+<html>
+
+<head>
+ <title>Accessible value change events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript"
+ src="../value.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // Value change invoker
+ function changeARIAValue(aNodeOrID, aValuenow, aValuetext)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+ this.eventSeq = [ new invokerChecker(aValuetext ?
+ EVENT_TEXT_VALUE_CHANGE :
+ EVENT_VALUE_CHANGE, this.DOMNode)
+ ];
+
+ this.invoke = function changeARIAValue_invoke() {
+
+ // Note: this should not fire an EVENT_VALUE_CHANGE when aria-valuetext
+ // is not empty
+ if (aValuenow != undefined)
+ this.DOMNode.setAttribute("aria-valuenow", aValuenow);
+
+ // Note: this should always fire an EVENT_VALUE_CHANGE
+ if (aValuetext != undefined)
+ this.DOMNode.setAttribute("aria-valuetext", aValuetext);
+ }
+
+ this.check = function changeARIAValue_check() {
+ var acc = getAccessible(aNodeOrID, [nsIAccessibleValue]);
+ if (!acc)
+ return;
+
+ // Note: always test against valuetext first because the existence of
+ // aria-valuetext takes precedence over aria-valuenow in gecko.
+ is(acc.value, (aValuetext != undefined)? aValuetext : aValuenow,
+ "Wrong value of " + prettyName(aNodeOrID));
+ }
+
+ this.getID = function changeARIAValue_getID() {
+ return prettyName(aNodeOrID) + " value changed";
+ }
+ }
+
+ function changeValue(aID, aValue)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode)
+ ];
+
+ this.invoke = function changeValue_invoke()
+ {
+ this.DOMNode.value = aValue;
+ }
+
+ this.check = function changeValue_check()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, aValue, "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeValue_getID()
+ {
+ return prettyName(aID) + " value changed";
+ }
+ }
+
+ function changeProgressValue(aID, aValue)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_VALUE_CHANGE, this.DOMNode)];
+
+ this.invoke = function changeProgressValue_invoke()
+ {
+ this.DOMNode.value = aValue;
+ }
+
+ this.check = function changeProgressValue_check()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, aValue+"%", "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeProgressValue_getID()
+ {
+ return prettyName(aID) + " value changed";
+ }
+ }
+
+ function changeRangeValue(aID)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_VALUE_CHANGE, this.DOMNode)];
+
+ this.invoke = function changeRangeValue_invoke()
+ {
+ synthesizeMouse(getNode(aID), 5, 5, { });
+ }
+
+ this.finalCheck = function changeRangeValue_finalCheck()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, "0", "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeRangeValue_getID()
+ {
+ return prettyName(aID) + " range value changed";
+ }
+ }
+
+ function changeSelectValue(aID, aKey, aValue)
+ {
+ this.eventSeq =
+ [ new invokerChecker(EVENT_TEXT_VALUE_CHANGE, getAccessible(aID)) ];
+
+ this.invoke = function changeSelectValue_invoke()
+ {
+ getNode(aID).focus();
+ synthesizeKey(aKey, {}, window);
+ }
+
+ this.finalCheck = function changeSelectValue_finalCheck()
+ {
+ is(getAccessible(aID).value, aValue, "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeSelectValue_getID()
+ {
+ return `${prettyName(aID)} closed select value change on '${aKey}'' key press`;
+ }
+ }
+
+ //enableLogging("DOMEvents");
+ //gA11yEventDumpToConsole = true;
+ function doTests()
+ {
+ // Test initial values
+ testValue("slider_vn", "5", 5, 0, 1000, 0);
+ testValue("slider_vnvt", "plain", 0, 0, 5, 0);
+ testValue("slider_vt", "hi", 0, 0, 3, 0);
+ testValue("scrollbar", "5", 5, 0, 1000, 0);
+ testValue("progress", "22%", 22, 0, 100, 0);
+ testValue("range", "6", 6, 0, 10, 1);
+
+ // Test value change events
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeARIAValue("slider_vn", "6", undefined));
+ gQueue.push(new changeARIAValue("slider_vt", undefined, "hey!"));
+ gQueue.push(new changeARIAValue("slider_vnvt", "3", "sweet"));
+ gQueue.push(new changeARIAValue("scrollbar", "6", undefined));
+
+ gQueue.push(new changeValue("combobox", "hello"));
+
+ gQueue.push(new changeProgressValue("progress", "50"));
+ gQueue.push(new changeRangeValue("range"));
+
+ gQueue.push(new changeSelectValue("select", "VK_DOWN", "2nd"));
+ gQueue.push(new changeSelectValue("select", "3", "3rd"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=478032"
+ title=" Fire delayed value changed event for aria-valuetext changes">
+ Mozilla Bug 478032
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289"
+ title="We dont expose new aria role 'scrollbar' and property aria-orientation">
+ Mozilla Bug 529289
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
+ title="Make HTML5 input@type=range element accessible">
+ Mozilla Bug 559764
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=703202"
+ title="ARIA comboboxes don't fire value change events">
+ Mozilla Bug 703202
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761901"
+ title=" HTML5 progress accessible should fire value change event">
+ Mozilla Bug 761901
+ </a>
+
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- ARIA sliders -->
+ <div id="slider_vn" role="slider" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="1000">slider</div>
+
+ <div id="slider_vt" role="slider" aria-valuetext="hi"
+ aria-valuemin="0" aria-valuemax="3">greeting slider</div>
+
+ <div id="slider_vnvt" role="slider" aria-valuenow="0" aria-valuetext="plain"
+ aria-valuemin="0" aria-valuemax="5">sweetness slider</div>
+
+ <!-- ARIA scrollbar -->
+ <div id="scrollbar" role="scrollbar" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="1000">slider</div>
+
+ <!-- ARIA combobox -->
+ <input id="combobox" role="combobox" aria-autocomplete="inline">
+
+ <!-- progress bar -->
+ <progress id="progress" value="22" max="100"></progress>
+
+ <!-- input@type="range" -->
+ <input type="range" id="range" min="0" max="10" value="6">
+
+ <select id="select">
+ <option>1st</option>
+ <option>2nd</option>
+ <option>3rd</option>
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/focus/a11y.ini b/accessible/tests/mochitest/focus/a11y.ini
new file mode 100644
index 000000000..48d5e6654
--- /dev/null
+++ b/accessible/tests/mochitest/focus/a11y.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_focusedChild.html]
+skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3')) # bug 845134
+[test_takeFocus.html]
+skip-if = buildapp == 'mulet'
+[test_takeFocus.xul]
diff --git a/accessible/tests/mochitest/focus/test_focusedChild.html b/accessible/tests/mochitest/focus/test_focusedChild.html
new file mode 100644
index 000000000..e03e0469c
--- /dev/null
+++ b/accessible/tests/mochitest/focus/test_focusedChild.html
@@ -0,0 +1,87 @@
+<html>
+
+<head>
+ <title>nsIAccessible::focusedChild testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function openWnd()
+ {
+ this.eventSeq = [ new invokerChecker(EVENT_FOCUS,
+ getDialogAccessible,
+ this) ];
+
+ this.invoke = function openWnd_invoke()
+ {
+ this.dialog = window.openDialog("about:mozilla",
+ "AboutMozilla",
+ "chrome,width=600,height=600");
+ }
+
+ this.finalCheck = function openWnd_finalCheck()
+ {
+ var app = getApplicationAccessible();
+ is(app.focusedChild, getDialogAccessible(this),
+ "Wrong focused child");
+
+ this.dialog.close();
+ }
+
+ this.getID = function openWnd_getID()
+ {
+ return "focusedChild for application accessible";
+ }
+
+ function getDialogAccessible(aInvoker)
+ {
+ return getAccessible(aInvoker.dialog.document);
+ }
+ }
+
+ gA11yEventDumpToConsole = true;
+ var gQueue = null;
+
+ function doTest()
+ {
+ enableLogging("focus,doclifecycle");
+ gQueue = new eventQueue();
+
+ gQueue.push(new openWnd());
+
+ gQueue.onFinish = function() { disableLogging(); }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=677467"
+ title="focusedChild crashes on application accessible">
+ Mozilla Bug 677467
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/focus/test_takeFocus.html b/accessible/tests/mochitest/focus/test_takeFocus.html
new file mode 100644
index 000000000..23938c819
--- /dev/null
+++ b/accessible/tests/mochitest/focus/test_takeFocus.html
@@ -0,0 +1,128 @@
+<html>
+
+<head>
+ <title>nsIAccessible::takeFocus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function takeFocusInvoker(aID)
+ {
+ this.accessible = getAccessible(aID);
+
+ this.eventSeq = [ new focusChecker(this.accessible) ];
+
+ this.invoke = function takeFocusInvoker_invoke()
+ {
+ this.accessible.takeFocus();
+ }
+
+ this.getID = function takeFocusInvoker_getID()
+ {
+ return "takeFocus for " + prettyName(aID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ disableLogging(); // from test_focusedChild
+ gQueue = new eventQueue();
+
+ gQueue.push(new takeFocusInvoker("aria-link"));
+ gQueue.push(new takeFocusInvoker("aria-link2"));
+ gQueue.push(new takeFocusInvoker("link"));
+ gQueue.push(new takeFocusInvoker("item2"));
+ gQueue.push(new takeFocusInvoker("plugin"));
+ gQueue.push(new takeFocusInvoker(document));
+ gQueue.push(new takeFocusInvoker("lb_item2"));
+ gQueue.push(new takeFocusInvoker(document));
+ gQueue.push(new takeFocusInvoker("lb_item3.2"));
+ gQueue.push(new takeFocusInvoker(document));
+ gQueue.push(new takeFocusInvoker("lb_item3.1"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ function waitForPlugin()
+ {
+ window.setTimeout((isAccessible("plugin") ? doTest : waitForPlugin), 0);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+ addA11yLoadEvent(waitForPlugin);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429547"
+ title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
+ Mozilla Bug 429547
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=452710"
+ title="nsIAccessible::takeFocus testing">
+ Mozilla Bug 452710
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=646361"
+ title="No focus event fired on document when focus is set to the document while focused in a plugin">
+ Mozilla Bug 646361
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=706067"
+ title="Make takeFocus work on widget items">
+ Mozilla Bug 706067
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <span id="aria-link" role="link" tabindex="0">link</span>
+ <span id="aria-link2" role="link" tabindex="0">link</span>
+
+ <a id="link" href="">link</a>
+
+ <div role="listbox" aria-activedescendant="item1" id="container" tabindex="1">
+ <div role="option" id="item1">item1</div>
+ <div role="option" id="item2">item2</div>
+ <div role="option" id="item3">item3</div>
+ </div>
+
+ <embed id="plugin" type="application/x-test" width="200" height="200" wmode="window"></embed>
+
+ <select id="listbox" size="5">
+ <option id="lb_item1">item1</option>
+ <option id="lb_item2">item2</option>
+ <optgroup>
+ <option id="lb_item3.1">item 3.1</option>
+ <option id="lb_item3.2">item 3.2</option>
+ </optgroup>
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/focus/test_takeFocus.xul b/accessible/tests/mochitest/focus/test_takeFocus.xul
new file mode 100644
index 000000000..11f6e2a28
--- /dev/null
+++ b/accessible/tests/mochitest/focus/test_takeFocus.xul
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function takeFocusInvoker(aID, aArgConverterFunc)
+ {
+ this.targetFunc = aArgConverterFunc ? aArgConverterFunc : getAccessible;
+
+ this.eventSeq = [ new focusChecker(this.targetFunc, aID) ];
+
+ this.invoke = function takeFocusInvoker_invoke()
+ {
+ this.targetFunc.call(null, aID).takeFocus();
+ }
+
+ this.getID = function takeFocusInvoker_getID()
+ {
+ return "takeFocus for " + prettyName(aID);
+ }
+ }
+
+ function getLastChild(aID)
+ {
+ return getAccessible(aID).lastChild;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new takeFocusInvoker("tree", getLastChild));
+ gQueue.push(new takeFocusInvoker("listitem2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTests, "tree", new nsTableTreeView(5));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=706067"
+ title="Make takeFocus work on widget items">
+ Mozilla Bug 706067
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+
+ <listbox id="listbox">
+ <listitem id="listitem1">item1</listitem>
+ <listitem id="listitem2">item2</listitem>
+ </listbox>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/formimage.png b/accessible/tests/mochitest/formimage.png
new file mode 100644
index 000000000..10e44bf92
--- /dev/null
+++ b/accessible/tests/mochitest/formimage.png
Binary files differ
diff --git a/accessible/tests/mochitest/grid.js b/accessible/tests/mochitest/grid.js
new file mode 100644
index 000000000..56f2412ff
--- /dev/null
+++ b/accessible/tests/mochitest/grid.js
@@ -0,0 +1,149 @@
+const nsIDOMKeyEvent = Components.interfaces.nsIDOMKeyEvent;
+
+/**
+ * Create grid object based on HTML table.
+ */
+function grid(aTableIdentifier)
+{
+ this.getRowCount = function getRowCount()
+ {
+ return this.table.rows.length - (this.table.tHead ? 1 : 0);
+ }
+ this.getColsCount = function getColsCount()
+ {
+ return this.table.rows[0].cells.length;
+ }
+
+ this.getRowAtIndex = function getRowAtIndex(aIndex)
+ {
+ return this.table.rows[this.table.tHead ? aIndex + 1 : aIndex];
+ }
+
+ this.getMaxIndex = function getMaxIndex()
+ {
+ return this.getRowCount() * this.getColsCount() - 1;
+ }
+
+ this.getCellAtIndex = function getCellAtIndex(aIndex)
+ {
+ var rowCount = this.getRowCount();
+ var colsCount = this.getColsCount();
+
+ var rowIdx = Math.floor(aIndex / colsCount);
+ var colIdx = aIndex % colsCount;
+
+ var row = this.getRowAtIndex(rowIdx);
+ return row.cells[colIdx];
+ }
+
+ this.getIndexByCell = function getIndexByCell(aCell)
+ {
+ var colIdx = aCell.cellIndex;
+
+ var rowIdx = aCell.parentNode.rowIndex;
+ if (this.table.tHead)
+ rowIdx -= 1;
+
+ var colsCount = this.getColsCount();
+ return rowIdx * colsCount + colIdx;
+ }
+
+ this.getCurrentCell = function getCurrentCell()
+ {
+ var rowCount = this.table.rows.length;
+ var colsCount = this.getColsCount();
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var cell = this.table.rows[rowIdx].cells[colIdx];
+ if (cell.hasAttribute("tabindex"))
+ return cell;
+ }
+ }
+ return null;
+ }
+
+ this.initGrid = function initGrid()
+ {
+ this.table.addEventListener("keypress", this, false);
+ this.table.addEventListener("click", this, false);
+ }
+
+ this.handleEvent = function handleEvent(aEvent)
+ {
+ if (aEvent instanceof nsIDOMKeyEvent)
+ this.handleKeyEvent(aEvent);
+ else
+ this.handleClickEvent(aEvent);
+ }
+
+ this.handleKeyEvent = function handleKeyEvent(aEvent)
+ {
+ if (aEvent.target.localName != "td")
+ return;
+
+ var cell = aEvent.target;
+ switch(aEvent.keyCode) {
+ case nsIDOMKeyEvent.DOM_VK_UP:
+ var colsCount = this.getColsCount();
+ var idx = this.getIndexByCell(cell);
+ var upidx = idx - colsCount;
+ if (upidx >= 0) {
+ cell.removeAttribute("tabindex");
+ var upcell = this.getCellAtIndex(upidx);
+ upcell.setAttribute("tabindex", "0");
+ upcell.focus();
+ }
+ break;
+
+ case nsIDOMKeyEvent.DOM_VK_DOWN:
+ var colsCount = this.getColsCount();
+ var idx = this.getIndexByCell(cell);
+ var downidx = idx + colsCount;
+ if (downidx <= this.getMaxIndex()) {
+ cell.removeAttribute("tabindex");
+ var downcell = this.getCellAtIndex(downidx);
+ downcell.setAttribute("tabindex", "0");
+ downcell.focus();
+ }
+ break;
+
+ case nsIDOMKeyEvent.DOM_VK_LEFT:
+ var idx = this.getIndexByCell(cell);
+ if (idx > 0) {
+ cell.removeAttribute("tabindex");
+ var prevcell = this.getCellAtIndex(idx - 1);
+ prevcell.setAttribute("tabindex", "0");
+ prevcell.focus();
+ }
+ break;
+
+ case nsIDOMKeyEvent.DOM_VK_RIGHT:
+ var idx = this.getIndexByCell(cell);
+ if (idx < this.getMaxIndex()) {
+ cell.removeAttribute("tabindex");
+ var nextcell = this.getCellAtIndex(idx + 1);
+ nextcell.setAttribute("tabindex", "0");
+ nextcell.focus();
+ }
+ break;
+ }
+ }
+
+ this.handleClickEvent = function handleClickEvent(aEvent)
+ {
+ if (aEvent.target.localName != "td")
+ return;
+
+ var curCell = this.getCurrentCell();
+ var cell = aEvent.target;
+
+ if (cell != curCell) {
+ curCell.removeAttribute("tabindex");
+ cell.setAttribute("tabindex", "0");
+ cell.focus();
+ }
+ }
+
+ this.table = getNode(aTableIdentifier);
+ this.initGrid();
+}
diff --git a/accessible/tests/mochitest/hittest/a11y.ini b/accessible/tests/mochitest/hittest/a11y.ini
new file mode 100644
index 000000000..13bca54de
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/a11y.ini
@@ -0,0 +1,14 @@
+[DEFAULT]
+support-files = zoom_tree.xul
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+
+[test_browser.html]
+[test_canvas_hitregion.html]
+skip-if = (os == "android" || appname == "b2g")
+[test_general.html]
+[test_menu.xul]
+[test_shadowroot.html]
+[test_zoom.html]
+[test_zoom_text.html]
+[test_zoom_tree.xul]
diff --git a/accessible/tests/mochitest/hittest/test_browser.html b/accessible/tests/mochitest/hittest/test_browser.html
new file mode 100644
index 000000000..75e0b9a95
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_browser.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessible::childAtPoint() from browser tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Hit testing. See bug #726097
+ getNode("hittest").scrollIntoView(true);
+
+ var hititem = getAccessible("hititem");
+ var hittest = getAccessible("hittest");
+
+ var [hitX, hitY, hitWidth, hitHeight] = getBounds(hititem);
+ var tgtX = hitX + hitWidth / 2;
+ var tgtY = hitY + hitHeight / 2;
+
+ var rootAcc = getRootAccessible();
+ var docAcc = getAccessible(document);
+ var outerDocAcc = docAcc.parent;
+
+ var hitAcc = rootAcc.getDeepestChildAtPoint(tgtX, tgtY);
+ is(hitAcc, hititem, "Hit match at " + tgtX + "," + tgtY +
+ ". Found: " + prettyName(hitAcc));
+ var hitAcc2 = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
+ is(hitAcc, hitAcc2, "Hit match at " + tgtX + "," + tgtY +
+ ". Found: " + prettyName(hitAcc2));
+
+ hitAcc = outerDocAcc.getChildAtPoint(tgtX, tgtY);
+ is(hitAcc, docAcc, "Hit match at " + tgtX + "," + tgtY +
+ ". Found: " + prettyName(hitAcc));
+ hitAcc = docAcc.getChildAtPoint(tgtX, tgtY);
+ is(hitAcc, hittest, "Hit match at " + tgtX + "," + tgtY +
+ ". Found: " + prettyName(hitAcc));
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=726097"
+ title="nsIAccessible::childAtPoint() from browser tests">Mozilla Bug 726097</a>
+
+ <div id="hittest">
+ <div id="hititem"><span role="image">img</span>item</div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_canvas_hitregion.html b/accessible/tests/mochitest/hittest/test_canvas_hitregion.html
new file mode 100644
index 000000000..fc1df3d60
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_canvas_hitregion.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessible::childAtPoint() for canvas from browser tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function redrawCheckbox(context, element, x, y)
+ {
+ context.save();
+ context.font = '10px sans-serif';
+ context.textAlign = 'left';
+ context.textBaseline = 'middle';
+ var metrics = context.measureText(element.parentNode.textContent);
+ context.beginPath();
+ context.strokeStyle = 'black';
+ context.rect(x-5, y-5, 10, 10);
+ context.stroke();
+ if (element.checked) {
+ context.fillStyle = 'black';
+ context.fill();
+ }
+ context.fillText(element.parentNode.textContent, x+5, y);
+
+ context.beginPath();
+ context.rect(x-7, y-7, 12 + metrics.width+2, 14);
+
+ if (document.activeElement == element)
+ context.drawFocusIfNeeded(element);
+ context.addHitRegion({control: element});
+ context.restore();
+ }
+
+ function doTest()
+ {
+ var offsetX = 20, offsetY = 40;
+ getNode("hitcanvas").scrollIntoView(true);
+
+ var context = document.getElementById("hitcanvas").getContext('2d');
+ redrawCheckbox(context, document.getElementById('hitcheck'),
+ offsetX, offsetY);
+
+ var hitcanvas = getAccessible("hitcanvas");
+ var hitcheck = getAccessible("hitcheck");
+
+ var [hitX, hitY, hitWidth, hitHeight] = getBounds(hitcanvas);
+ var [deltaX, deltaY] = CSSToDevicePixels(window, offsetX, offsetY);
+
+ var docAcc = getAccessible(document);
+
+ // test if we hit the region associated with the shadow dom checkbox
+ var tgtX = hitX + deltaX;
+ var tgtY = hitY + deltaY;
+ hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
+ isObject(hitAcc, hitcheck, `Hit match at (${tgtX}, ${tgtY}`);
+
+ // test that we don't hit the region associated with the shadow dom checkbox
+ tgtY = hitY + deltaY * 2;
+ hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
+ isObject(hitAcc, hitcanvas, `Hit match at (${tgtX}, ${tgtY}`);
+
+ SimpleTest.finish();
+ }
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set": [['canvas.hitregions.enabled', true]]}, doTest);
+ });
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=966591"
+ title="nsIAccessible::childAtPoint() for canvas hit regions from browser tests">Mozilla Bug 966591</a>
+
+ <canvas id="hitcanvas">
+ <input id="hitcheck" type="checkbox"><label for="showA"> Show A </label>
+ </canvas>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_general.html b/accessible/tests/mochitest/hittest/test_general.html
new file mode 100644
index 000000000..74ff4fe29
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_general.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessible::childAtPoint() tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function doPreTest()
+ {
+ waitForImageMap("imgmap", doTest);
+ }
+
+ function doTest()
+ {
+ // Not specific case, child and deepchild testing.
+ var list = getAccessible("list");
+ var listitem = getAccessible("listitem");
+ var image = getAccessible("image");
+if (!MAC) {
+ testChildAtPoint(list, 1, 1, listitem, image.firstChild);
+} else {
+ todo(false, "Bug 746974 - children must match on all platforms, disable failing test on Mac");
+}
+
+ // ::MustPrune case (in this case childAtPoint doesn't look inside a
+ // textbox), point is inside of textbox.
+ var txt = getAccessible("txt");
+ testChildAtPoint(txt, 1, 1, txt, txt);
+
+ // ::MustPrune case, point is outside of textbox accessible but is in
+ // document.
+ testChildAtPoint(txt, -1, 1, null, null);
+
+ // ::MustPrune case, point is outside of root accessible.
+ testChildAtPoint(txt, -10000, 10000, null, null);
+
+ // Not specific case, point is inside of btn accessible.
+ var btn = getAccessible("btn");
+ var btnText = btn.firstChild;
+ testChildAtPoint(btn, 1, 1, btn, btn);
+
+ // Not specific case, point is outside of btn accessible.
+ testChildAtPoint(btn, -1, 1, null, null);
+
+ // Out of flow accessible testing, do not return out of flow accessible
+ // because it's not a child of the accessible even visually it is.
+ var rectArea = getNode("area").getBoundingClientRect();
+ var outOfFlow = getNode("outofflow");
+ outOfFlow.style.left = rectArea.left + "px";
+ outOfFlow.style.top = rectArea.top + "px";
+
+ testChildAtPoint("area", 1, 1, "area", "area");
+
+ // Test image maps. Their children are not in the layout tree.
+ var theLetterA = getAccessible("imgmap").firstChild;
+ hitTest("imgmap", theLetterA, theLetterA);
+ hitTest("container", "imgmap", theLetterA);
+
+ // hit testing for element contained by zero-width element
+ hitTest("container2", "container2_input", "container2_input");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=491657"
+ title="nsIAccessible::childAtPoint() tests">Mozilla Bug 491657</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="list" id="list">
+ <div role="listitem" id="listitem"><span role="image" id="image">img</span>item</div>
+ </div>
+
+ <span role="button">button1</span><span role="button" id="btn">button2</span>
+
+ <span role="textbox">textbox1</span><span role="textbox" id="txt">textbox2</span>
+
+ <div id="outofflow" style="width: 10px; height: 10px; position: absolute; left: 0px; top: 0px; background-color: yellow;">
+ </div>
+ <div id="area" style="width: 100px; height: 100px; background-color: blue;"></div>
+
+ <map name="atoz_map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,15,15" alt="thelettera" shape="rect"/>
+ </map>
+
+ <div id="container">
+ <img id="imgmap" width="447" height="15" usemap="#atoz_map" src="../letters.gif"/>
+ </div>
+
+ <div id="container2" style="width: 0px">
+ <input id="container2_input">
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_menu.xul b/accessible/tests/mochitest/hittest/test_menu.xul
new file mode 100644
index 000000000..30bf93acf
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_menu.xul
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Hit testing for XUL menus">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../layout.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function openMenu(aMenuID, aMenuPopupID, aMenuItemID)
+ {
+ this.menuNode = getNode(aMenuID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ hitTest(aMenuPopupID, aMenuItemID, aMenuItemID);
+ }
+
+ this.getID = function openMenu_invoke()
+ {
+ return "open menu '" + aMenuID + "' and do hit testing";
+ }
+ }
+
+ function closeMenu(aID, aSubID, aSub2ID)
+ {
+ this.menuNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, document)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ this.menuNode.open = false;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ testStates(aID, 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates(aSubID, STATE_INVISIBLE, 0, STATE_OFFSCREEN);
+ testStates(aSub2ID, STATE_INVISIBLE, 0, STATE_OFFSCREEN);
+ }
+
+ this.getID = function openMenu_invoke()
+ {
+ return "open menu and test states";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ if (LINUX) {
+ ok(true, "No tests is running on Linux");
+ SimpleTest.finish();
+ return;
+ }
+
+ getNode("mi_file1").scrollIntoView(true);
+
+ gQueue = new eventQueue();
+ gQueue.push(new openMenu("mi_file1", "mp_file1", "mi_file1.1"));
+ gQueue.push(new openMenu("mi_file1.2", "mp_file1.2", "mi_file1.2.1"));
+ gQueue.push(new closeMenu("mi_file1", "mi_file1.1", "mi_file1.2.1"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=670087"
+ title="AccessibleObjectFromPoint returns incorrect accessible for popup menus">
+ Bug 670087
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <menubar>
+ <menu label="File" id="mi_file1">
+ <menupopup id="mp_file1">
+ <menuitem label="SubFile" id="mi_file1.1"/>
+ <menu label="SubFile2" id="mi_file1.2">
+ <menupopup style="max-height: 5em;" id="mp_file1.2">
+ <menuitem label="SubSubFile" id="mi_file1.2.1"/>
+ <menuitem label="SubSubFile2" id="mi_file1.2.2"/>
+ <menuitem label="SubSubFile3" id="mi_file1.2.3"/>
+ <menuitem label="SubSubFile4" id="mi_file1.2.4"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/hittest/test_shadowroot.html b/accessible/tests/mochitest/hittest/test_shadowroot.html
new file mode 100644
index 000000000..41400840d
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>ShadowRoot hit tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var componentAcc = getAccessible('component1');
+ testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+ componentAcc.firstChild);
+
+ componentAcc = getAccessible('component2');
+ testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+ componentAcc.firstChild);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Test getChildAtPoint works for shadow DOM content"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027315">
+ Mozilla Bug 1027315
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="group" class="components" id="component1" style="display: inline-block;">
+ <!--
+ <div role="button" id="component-child"
+ style="width: 100px; height: 100px; background-color: pink;">
+ </div>
+ -->
+ </div>
+ <div role="group" class="components" id="component2" style="display: inline-block;">
+ <!--
+ <button>Hello world</button>
+ -->
+ </div>
+ <script>
+ // This routine adds the comment children of each 'component' to its
+ // shadow root.
+ var components = document.querySelectorAll('.components');
+ for (var i = 0; i < components.length; i++) {
+ var component = components[i];
+ var shadow = component.createShadowRoot();
+ for (var child = component.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType === 8)
+ shadow.innerHTML = child.data;
+ }
+ }
+ </script>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_zoom.html b/accessible/tests/mochitest/hittest/test_zoom.html
new file mode 100644
index 000000000..bc1cf9d55
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_zoom.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>childAtPoint when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+if (!MAC) {
+ var tabDocument = currentTabDocument();
+ var p1 = tabDocument.body.firstElementChild;
+ var p2 = tabDocument.body.lastElementChild;
+
+ hitTest(tabDocument, p1, p1.firstChild);
+ hitTest(tabDocument, p2, p2.firstChild);
+
+ zoomDocument(tabDocument, 2.0);
+
+ hitTest(tabDocument, p1, p1.firstChild);
+ hitTest(tabDocument, p2, p2.firstChild);
+
+ closeBrowserWindow();
+} else {
+ todo(false, "Bug 746974 - deepest child must be correct on all platforms, disabling on Mac!");
+}
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest,
+ "data:text/html,<html><body><p>para 1</p><p>para 2</p></body></html>",
+ { left: 100, top: 100 });
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727942"
+ title="childAtPoint may return incorrect accessibles when page zoomed">
+ Mozilla Bug 727942
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_zoom_text.html b/accessible/tests/mochitest/hittest/test_zoom_text.html
new file mode 100644
index 000000000..3a75296b2
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_zoom_text.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>getOffsetAtPoint when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var hyperText = getNode("paragraph");
+ var textNode = hyperText.firstChild;
+ var [x, y, width, height] = getBounds(textNode);
+ testOffsetAtPoint(hyperText, x + width / 2, y + height / 2,
+ COORDTYPE_SCREEN_RELATIVE,
+ hyperText.textContent.length / 2);
+
+ zoomDocument(document, 2.0);
+
+ var [x, y, width, height] = getBounds(textNode);
+ testOffsetAtPoint(hyperText, x + width / 2, y + height / 2,
+ COORDTYPE_SCREEN_RELATIVE,
+ hyperText.textContent.length / 2);
+
+ zoomDocument(document, 1.0);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727942"
+ title="getOffsetAtPoint returns incorrect value when page is zoomed">
+ Mozilla Bug 727942
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <p id="paragraph" style="font-family: monospace;">Болтали две сороки</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hittest/test_zoom_tree.xul b/accessible/tests/mochitest/hittest/test_zoom_tree.xul
new file mode 100644
index 000000000..da1628df2
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/test_zoom_tree.xul
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessible::getChildAtPoint and getDeepestChildAtPoint">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../layout.js" />
+ <script type="application/javascript"
+ src="../browser.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ var tabDocument = currentTabDocument();
+ var tabWindow = currentTabWindow();
+
+ var tree = tabDocument.getElementById("tree");
+ var treecols = tabDocument.getElementById("treecols");
+ var treecol1 = tabDocument.getElementById("treecol1");
+
+ // tree columns
+ hitTest(tree, treecols, treecol1);
+
+ // tree rows and cells
+ var treeBoxObject = tree.treeBoxObject;
+ var treeBodyBoxObj = tree.treeBoxObject.treeBody.boxObject;
+ var rect = treeBoxObject.getCoordsForCellItem(1, tree.columns[0], "cell");
+
+ var treeAcc = getAccessible(tree, [nsIAccessibleTable]);
+ var cellAcc = treeAcc.getCellAt(1, 0);
+ var rowAcc = cellAcc.parent;
+
+ var cssX = rect.x + treeBodyBoxObj.x;
+ var cssY = rect.y + treeBodyBoxObj.y;
+ var [x, y] = CSSToDevicePixels(tabWindow, cssX, cssY);
+
+ testChildAtPoint(treeAcc, x, y, rowAcc, cellAcc);
+ testChildAtPoint(rowAcc, x, y, cellAcc, cellAcc);
+
+ // do zoom
+ zoomDocument(tabDocument, 1.5);
+
+ // tree columns
+ hitTest(tree, treecols, treecol1);
+
+ // tree rows and cells
+ var [x, y] = CSSToDevicePixels(tabWindow, cssX, cssY);
+ testChildAtPoint(treeAcc, x, y, rowAcc, cellAcc);
+ testChildAtPoint(rowAcc, x, y, cellAcc, cellAcc);
+
+ closeBrowserWindow();
+ SimpleTest.finish();
+ }
+
+ function prepareTest()
+ {
+ var tabDocument = currentTabDocument();
+ var tree = tabDocument.getElementById("tree");
+ loadXULTreeAndDoTest(doTest, tree, new nsTableTreeView(5));
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(prepareTest,
+ getRootDirectory(window.location.href) + "zoom_tree.xul",
+ { left: 100, top: 100 });
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=471493"
+ title=" crash [@ nsPropertyTable::GetPropertyInternal]">
+ Mozilla Bug 471493
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/hittest/zoom_tree.xul b/accessible/tests/mochitest/hittest/zoom_tree.xul
new file mode 100644
index 000000000..52ec0932a
--- /dev/null
+++ b/accessible/tests/mochitest/hittest/zoom_tree.xul
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessible::getChildAtPoint and getDeepestChildAtPoint for XUL trees">
+
+ <tree id="tree" flex="1">
+ <treecols id="treecols">
+ <treecol id="treecol1" flex="1" primary="true" label="column"/>
+ <treecol id="treecol2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+
+</window>
+
diff --git a/accessible/tests/mochitest/hyperlink/a11y.ini b/accessible/tests/mochitest/hyperlink/a11y.ini
new file mode 100644
index 000000000..f37dca8e1
--- /dev/null
+++ b/accessible/tests/mochitest/hyperlink/a11y.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files = hyperlink.js
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+
+[test_general.html]
+[test_general.xul]
diff --git a/accessible/tests/mochitest/hyperlink/hyperlink.js b/accessible/tests/mochitest/hyperlink/hyperlink.js
new file mode 100644
index 000000000..4daa16275
--- /dev/null
+++ b/accessible/tests/mochitest/hyperlink/hyperlink.js
@@ -0,0 +1,42 @@
+/**
+ * Focus hyperlink invoker.
+ *
+ * @param aID [in] hyperlink identifier
+ * @param aSelectedAfter [in] specifies if hyperlink is selected/focused after
+ * the focus
+ */
+function focusLink(aID, aSelectedAfter)
+{
+ this.node = getNode(aID);
+ this.accessible = getAccessible(this.node);
+
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [];
+
+ var checker = new invokerChecker(EVENT_FOCUS, this.accessible);
+ if (aSelectedAfter)
+ this.eventSeq.push(checker);
+ else
+ this.unexpectedEventSeq.push(checker);
+
+ this.invoke = function focusLink_invoke()
+ {
+ var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE : 0);
+ var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE : 0) | STATE_FOCUSED;
+ testStates(aID, expectedStates, 0, unexpectedStates, 0);
+
+ this.node.focus();
+ }
+
+ this.finalCheck = function focusLink_finalCheck()
+ {
+ var expectedStates = (aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
+ var unexpectedStates = (!aSelectedAfter ? STATE_FOCUSABLE | STATE_FOCUSED : 0);
+ testStates(aID, expectedStates, 0, unexpectedStates, 0);
+ }
+
+ this.getID = function focusLink_getID()
+ {
+ return "focus hyperlink " + prettyName(aID);
+ }
+}
diff --git a/accessible/tests/mochitest/hyperlink/test_general.html b/accessible/tests/mochitest/hyperlink/test_general.html
new file mode 100644
index 000000000..e6e098ecc
--- /dev/null
+++ b/accessible/tests/mochitest/hyperlink/test_general.html
@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=418368
+-->
+<head>
+ <title>nsIHyperLinkAccessible chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript"
+ src="hyperlink.js"></script>
+
+ <script type="application/javascript">
+ function testThis(aID, aAcc, aRole, aAnchors, aName, aValid, aStartIndex,
+ aEndIndex)
+ {
+ testRole(aAcc, aRole);
+ is(aAcc.anchorCount, aAnchors, "Wrong number of anchors for ID "
+ + aID + "!");
+ is(aAcc.getAnchor(0).name, aName, "Wrong name for ID "
+ + aID + "!");
+ is(aAcc.valid, aValid, "No correct valid state for ID "
+ + aID + "!");
+ is(aAcc.startIndex, aStartIndex, "Wrong startIndex value for ID "
+ + aID + "!");
+ is(aAcc.endIndex, aEndIndex, "Wrong endIndex value for ID "
+ + aID + "!");
+ }
+
+ function testAction(aId, aAcc, aActionName)
+ {
+ var actionCount = aActionName ? 1 : 0;
+ is(aAcc.actionCount, actionCount,
+ "Wrong actions number for ID " + aId);
+ try {
+ is(aAcc.getActionName(0), aActionName,
+ "Wrong action name for ID " + aId);
+ } catch (e) {
+ if (actionCount)
+ ok(false, "Exception on action name getting for ID " + aId);
+ else
+ ok(true, "Correct action name for ID " + aId);
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ function doPreTest()
+ {
+ waitForImageMap("imgmap", doTest);
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // normal hyperlink
+ var normalHyperlinkAcc = getAccessible("NormalHyperlink",
+ [nsIAccessibleHyperLink]);
+ testThis("NormalHyperlink", normalHyperlinkAcc, ROLE_LINK, 1,
+ "Mozilla Foundation", true, 17, 18);
+ is(normalHyperlinkAcc.getURI(0).spec, "http://www.mozilla.org/",
+ "URI wrong for normalHyperlinkElement!");
+ testStates(normalHyperlinkAcc, STATE_LINKED, 0);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA hyperlink
+ var ariaHyperlinkAcc = getAccessible("AriaHyperlink",
+ [nsIAccessibleHyperLink]);
+ testThis("AriaHyperlink", ariaHyperlinkAcc, ROLE_LINK, 1,
+ "Mozilla Foundation Home", true, 30, 31);
+ testStates(ariaHyperlinkAcc, STATE_LINKED, 0);
+ testAction("AriaHyperlink", ariaHyperlinkAcc, "click");
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA hyperlink with status invalid
+ var invalidAriaHyperlinkAcc = getAccessible("InvalidAriaHyperlink",
+ [nsIAccessibleHyperLink]);
+ is(invalidAriaHyperlinkAcc.valid, false, "Should not be valid!");
+ testStates(invalidAriaHyperlinkAcc, STATE_LINKED, 0);
+
+ //////////////////////////////////////////////////////////////////////////
+ // image map and its link children
+
+ var imageMapHyperlinkAcc = getAccessible("imgmap",
+ [nsIAccessibleHyperLink]);
+ testThis("imgmap", imageMapHyperlinkAcc, ROLE_IMAGE_MAP, 2, "b", true,
+ 79, 80);
+ is(imageMapHyperlinkAcc.getURI(0).spec,
+ "http://www.bbc.co.uk/radio4/atoz/index.shtml#b", "URI wrong!");
+ is(imageMapHyperlinkAcc.getURI(1).spec,
+ "http://www.bbc.co.uk/radio4/atoz/index.shtml#a", "URI wrong!");
+ testStates(imageMapHyperlinkAcc, 0, 0);
+
+ var area1 = getAccessible(imageMapHyperlinkAcc.firstChild,
+ [nsIAccessibleHyperLink]);
+ testThis("Area1", area1, ROLE_LINK, 1, "b", true, 0, 1);
+ is(area1.getURI(0).spec,
+ "http://www.bbc.co.uk/radio4/atoz/index.shtml#b", "URI wrong!");
+ testStates(area1, (STATE_LINKED));
+
+ var area2 = getAccessible(area1.nextSibling,
+ [nsIAccessibleHyperLink]);
+ testThis("Area2", area2, ROLE_LINK, 1, "a", true, 1, 2);
+ is(area2.getURI(0).spec,
+ "http://www.bbc.co.uk/radio4/atoz/index.shtml#a", "URI wrong!");
+ testStates(area2, (STATE_LINKED));
+
+ //////////////////////////////////////////////////////////////////////////
+ // empty hyperlink
+ var EmptyHLAcc = getAccessible("emptyLink",
+ [nsIAccessibleHyperLink]);
+ testThis("emptyLink", EmptyHLAcc, ROLE_LINK, 1, null, true, 93, 94);
+ testStates(EmptyHLAcc, (STATE_FOCUSABLE | STATE_LINKED), 0);
+ testAction("emptyLink", EmptyHLAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // normal hyperlink with embedded span
+ var hyperlinkWithSpanAcc = getAccessible("LinkWithSpan",
+ [nsIAccessibleHyperLink]);
+ testThis("LinkWithSpan", hyperlinkWithSpanAcc, ROLE_LINK, 1,
+ "Heise Online", true, 119, 120);
+ is(hyperlinkWithSpanAcc.getURI(0).spec, "http://www.heise.de/",
+ "URI wrong for hyperlinkElementWithSpan!");
+ testStates(hyperlinkWithSpanAcc, STATE_LINKED, 0);
+ testAction("LinkWithSpan", hyperlinkWithSpanAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Named anchor, should never have state_linked
+ var namedAnchorAcc = getAccessible("namedAnchor",
+ [nsIAccessibleHyperLink]);
+ testThis("namedAnchor", namedAnchorAcc, ROLE_LINK, 1,
+ "This should never be of state_linked", true, 196, 197);
+ testStates(namedAnchorAcc, STATE_SELECTABLE,
+ 0, (STATE_FOCUSABLE | STATE_LINKED));
+ testAction("namedAnchor", namedAnchorAcc, "");
+
+ //////////////////////////////////////////////////////////////////////////
+ // No link (hasn't any attribute), should never have state_linked
+ var noLinkAcc = getAccessible("noLink",
+ [nsIAccessibleHyperLink]);
+ testThis("noLink", noLinkAcc, ROLE_LINK, 1,
+ "This should never be of state_linked", true, 254, 255);
+ testStates(noLinkAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED));
+ testAction("noLink", noLinkAcc, "");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with registered 'click' event, should have state_linked
+ var linkWithClickAcc = getAccessible("linkWithClick",
+ [nsIAccessibleHyperLink]);
+ testThis("linkWithClick", linkWithClickAcc, ROLE_LINK, 1,
+ "This should have state_linked", true, 292, 293);
+ testStates(linkWithClickAcc, STATE_LINKED, 0);
+ testAction("linkWithClick", linkWithClickAcc, "click");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Maps to group links (bug 431615).
+ var linksMapAcc = getAccessible("linksmap");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, no name from the subtree (bug 438325).
+ var id = "linkWithTitleNoNameFromSubtree";
+ var linkAcc = getAccessible(id, [nsIAccessibleHyperLink]);
+ testThis(id, linkAcc, ROLE_LINK, 1, "Link with title", true, 344, 345);
+ testStates(linkAcc, STATE_LINKED, 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, name from the subtree - onscreen name
+ // (bug 438325).
+ id = "linkWithTitleNameFromSubtree";
+ linkAcc = getAccessible(id, [nsIAccessibleHyperLink]);
+ testThis(id, linkAcc, ROLE_LINK, 1, "the name from subtree", true, 393,
+ 394);
+ testStates(linkAcc, STATE_LINKED, 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Link with title attribute, name from the nested html:img (bug 438325).
+ id = "linkWithTitleNameFromImg";
+ linkAcc = getAccessible(id, [nsIAccessibleHyperLink]);
+ testThis(id, linkAcc, ROLE_LINK, 1, "The title for link", true, 447,
+ 448);
+ testStates(linkAcc, STATE_LINKED, 0);
+ testAction(id, linkAcc, "jump");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Text accessible shouldn't implement nsIAccessibleHyperLink
+ var res = isAccessible(getNode("namedAnchor").firstChild,
+ [nsIAccessibleHyperLink]);
+ ok(!res, "Text accessible shouldn't implement nsIAccessibleHyperLink");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Test focus
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusLink("NormalHyperlink", true));
+ gQueue.push(new focusLink("AriaHyperlink", true));
+ gQueue.push(new focusLink("InvalidAriaHyperlink", false));
+ gQueue.push(new focusLink("LinkWithSpan", true));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418368">Mozilla Bug 418368</a
+ ><p id="display"></p
+ ><div id="content" style="display: none"></div
+ ><pre id="test">
+ </pre
+ ><br
+ >Simple link:<br
+ ><a id="NormalHyperlink" href="http://www.mozilla.org">Mozilla Foundation</a
+ ><br>ARIA link:<br
+ ><span id="AriaHyperlink" role="link"
+ onclick="window.open('http://www.mozilla.org/');"
+ tabindex="0">Mozilla Foundation Home</span
+ ><br
+ >Invalid, non-focusable hyperlink:<br
+ ><span id="InvalidAriaHyperlink" role="link" aria-invalid="true"
+ onclick="window.open('http:/www.mozilla.org/');">Invalid link</span
+ ><br>Image map:<br
+ ><map name="atoz_map"
+ ><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14"
+ alt="b"
+ shape="rect"></area
+ ><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,13,14"
+ alt="a"
+ shape="rect"></area
+ ></map
+ ><img width="447" id="imgmap"
+ height="15"
+ usemap="#atoz_map"
+ src="../letters.gif"><br>Empty link:<br
+ ><a id="emptyLink" href=""><img src=""></a
+ ><br>Link with embedded span<br
+ ><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a
+ ><br>Named anchor, must not have "linked" state for it to be exposed correctly:<br
+ ><a id="namedAnchor" name="named_anchor">This should never be of state_linked</a
+ ><br>Link having no attributes, must not have "linked" state:<a id="noLink"
+ >This should never be of state_linked</a
+ ><br>Link with registered 'click' event: <a id="linkWithClick" onclick="var clicked = true;"
+ >This should have state_linked</a
+ ><br>Link with title attribute (no name from subtree): <a
+ id="linkWithTitleNoNameFromSubtree" href="http://www.heise.de/"
+ title="Link with title"><img src=""/></a
+ ><br>Link with title attribute (name from subtree): <a
+ id="linkWithTitleNameFromSubtree" href="http://www.heise.de/"
+ title="Link with title">the name from subtree</a
+ ><br>Link with title attribute (name from nested image): <a
+ id="linkWithTitleNameFromImg" href="http://www.heise.de/"
+ title="Link with title"><img src="" alt="The title for link"/></a
+ ><br><br>Map that is used to group links (www.w3.org/TR/WCAG10-HTML-TECHS/#group-bypass), also see the bug 431615:<br
+ ><map id="linksmap" title="Site navigation"><ul
+ ><li><a href="http://mozilla.org">About the project</a></li
+ ><li><a href="http://mozilla.org">Sites and sounds</a></li
+ ></ul
+ ></map
+></body>
+</html>
diff --git a/accessible/tests/mochitest/hyperlink/test_general.xul b/accessible/tests/mochitest/hyperlink/test_general.xul
new file mode 100644
index 000000000..c7b838a15
--- /dev/null
+++ b/accessible/tests/mochitest/hyperlink/test_general.xul
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="test for nsIAccessibleHyperLink interface on XUL:label elements">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript"
+ src="hyperlink.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function testThis(aID, aAcc, aRole, aAnchorCount, aAnchorName, aURI,
+ aStartIndex, aEndIndex, aValid)
+ {
+ testRole(aID, aRole);
+ is(aAcc.anchorCount, aAnchorCount, "Wrong number of anchors for ID "
+ + aID + "!");
+ is(aAcc.getAnchor(0).name, aAnchorName, "Wrong name for ID " + aID + "!");
+ is(aAcc.getURI(0).spec, aURI, "URI wrong for ID " + aID + "!");
+ is(aAcc.startIndex, aStartIndex, "Wrong startIndex value for ID " + aID
+ + "!");
+ is(aAcc.endIndex, aEndIndex, "Wrong endIndex value for ID " + aID + "!");
+ is(aAcc.valid, aValid, "Wrong valid state for ID " + aID + "!");
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ var linkedLabelAcc = getAccessible("linkedLabel",
+ [nsIAccessibleHyperLink]);
+ testThis("linkedLabel", linkedLabelAcc, ROLE_LINK, 1,
+ "Mozilla Foundation home", "http://www.mozilla.org/", 1, 2,
+ true);
+ testStates(linkedLabelAcc, STATE_LINKED, 0);
+
+ var labelWithValueAcc = getAccessible("linkLabelWithValue",
+ [nsIAccessibleHyperLink]);
+ testThis("linkLabelWithValue", labelWithValueAcc, ROLE_LINK, 1,
+ "Mozilla Foundation", "http://www.mozilla.org/", 2, 3, true,
+ false, true);
+ testStates(labelWithValueAcc, STATE_LINKED, EXT_STATE_HORIZONTAL);
+
+ var normalLabelAcc = getAccessible("normalLabel");
+ testRole(normalLabelAcc, ROLE_LABEL);
+ is(normalLabelAcc.name, "This label should not be a link",
+ "Wrong name for normal label!");
+ testStates(normalLabelAcc, 0, 0, (STATE_FOCUSABLE | STATE_LINKED));
+
+ //////////////////////////////////////////////////////////////////////////
+ // Test focus
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusLink("linkedLabel", true));
+ gQueue.push(new focusLink("linkLabelWithValue", true));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=421066"
+ title="Implement Mochitests for the nsIAccessibleHyperLink interface on XUL:label elements">
+ Mozilla Bug 421066
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <label id="linkedLabel" class="text-link" href="http://www.mozilla.org/">
+ Mozilla Foundation home</label>
+ <label id="linkLabelWithValue" value="Mozilla Foundation" class="text-link"
+ href="http://www.mozilla.org/" />
+ <label id="normalLabel" value="This label should not be a link" />
+</window>
diff --git a/accessible/tests/mochitest/hypertext/a11y.ini b/accessible/tests/mochitest/hypertext/a11y.ini
new file mode 100644
index 000000000..27f878f74
--- /dev/null
+++ b/accessible/tests/mochitest/hypertext/a11y.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+
+[test_general.html]
+[test_update.html]
diff --git a/accessible/tests/mochitest/hypertext/test_general.html b/accessible/tests/mochitest/hypertext/test_general.html
new file mode 100644
index 000000000..68018821f
--- /dev/null
+++ b/accessible/tests/mochitest/hypertext/test_general.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428248
+-->
+<head>
+ <title>nsIHyper>TextAccessible chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ var gParagraphAcc;
+
+ function testLinkIndexAtOffset(aID, aOffset, aIndex)
+ {
+ var htAcc = getAccessible(aID, [nsIAccessibleHyperText]);
+ is(htAcc.getLinkIndexAtOffset(aOffset), aIndex,
+ "Wrong link index at offset " + aOffset + " for ID " + aID + "!");
+ }
+
+ function testThis(aID, aCharIndex, aExpectedLinkIndex, aName)
+ {
+ testLinkIndexAtOffset(gParagraphAcc, aCharIndex, aExpectedLinkIndex);
+
+ var linkAcc = gParagraphAcc.getLinkAt(aExpectedLinkIndex);
+ ok(linkAcc, "No accessible for link " + aID + "!");
+
+ var linkIndex = gParagraphAcc.getLinkIndex(linkAcc);
+ is(linkIndex, aExpectedLinkIndex, "Wrong link index for " + aID + "!");
+
+ // Just test the link's name to make sure we get the right one.
+ is(linkAcc.getAnchor(0).name, aName, "Wrong name for " + aID + "!");
+ }
+
+ //gA11yEventDumpToConsole = true;
+ function doPreTest()
+ {
+ waitForImageMap("imgmap", doTest);
+ }
+
+ function doTest()
+ {
+ // Test link count
+ gParagraphAcc = getAccessible("testParagraph", [nsIAccessibleHyperText]);
+ is(gParagraphAcc.linkCount, 7, "Wrong link count for paragraph!");
+
+ // normal hyperlink
+ testThis("NormalHyperlink", 14, 0, "Mozilla Foundation");
+
+ // ARIA hyperlink
+ testThis("AriaHyperlink", 27, 1, "Mozilla Foundation Home");
+
+ // ARIA hyperlink with status invalid
+ testThis("InvalidAriaHyperlink", 63, 2, "Invalid link");
+
+ // image map, but not its link children. They are not part of hypertext.
+ testThis("imgmap", 76, 3, "b");
+
+ // empty hyperlink
+ testThis("emptyLink", 90, 4, null);
+
+ // normal hyperlink with embedded span
+ testThis("LinkWithSpan", 116, 5, "Heise Online");
+
+ // Named anchor
+ testThis("namedAnchor", 193, 6, "This should never be of state_linked");
+
+ // Paragraph with link
+ var p2 = getAccessible("p2", [nsIAccessibleHyperText]);
+ var link = p2.getLinkAt(0);
+ is(link, p2.getChildAt(0), "Wrong link for p2");
+ is(p2.linkCount, 1, "Wrong link count for p2");
+
+ // getLinkIndexAtOffset, causes the offsets to be cached;
+ testLinkIndexAtOffset("p4", 0, 0); // 1st 'mozilla' link
+ testLinkIndexAtOffset("p4", 1, 1); // 2nd 'mozilla' link
+ testLinkIndexAtOffset("p4", 2, -1); // ' ' of ' te' text node
+ testLinkIndexAtOffset("p4", 3, -1); // 't' of ' te' text node
+ testLinkIndexAtOffset("p4", 5, -1); // 'x' of 'xt ' text node
+ testLinkIndexAtOffset("p4", 7, -1); // ' ' of 'xt ' text node
+ testLinkIndexAtOffset("p4", 8, 2); // 3d 'mozilla' link
+ testLinkIndexAtOffset("p4", 9, 2); // the end, latest link
+
+ // the second pass to make sure link indexes are calculated propertly from
+ // cached offsets.
+ testLinkIndexAtOffset("p4", 0, 0); // 1st 'mozilla' link
+ testLinkIndexAtOffset("p4", 1, 1); // 2nd 'mozilla' link
+ testLinkIndexAtOffset("p4", 2, -1); // ' ' of ' te' text node
+ testLinkIndexAtOffset("p4", 3, -1); // 't' of ' te' text node
+ testLinkIndexAtOffset("p4", 5, -1); // 'x' of 'xt ' text node
+ testLinkIndexAtOffset("p4", 7, -1); // ' ' of 'xt ' text node
+ testLinkIndexAtOffset("p4", 8, 2); // 3d 'mozilla' link
+ testLinkIndexAtOffset("p4", 9, 2); // the end, latest link
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ title="Create tests for NSIAccessibleHyperlink interface"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=418368">
+ Mozilla Bug 418368
+ </a><br>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <p id="testParagraph"><br
+ >Simple link:<br
+ ><a id="NormalHyperlink" href="http://www.mozilla.org">Mozilla Foundation</a><br
+ >ARIA link:<br
+ ><span id="AriaHyperlink" role="link"
+ onclick="window.open('http://www.mozilla.org/');"
+ tabindex="0">Mozilla Foundation Home</span><br
+ >Invalid, non-focusable hyperlink:<br
+ ><span id="InvalidAriaHyperlink" role="link" aria-invalid="true"
+ onclick="window.open('http:/www.mozilla.org/');">Invalid link</span><br
+ >Image map:<br
+ ><map name="atoz_map"><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14"
+ alt="b"
+ shape="rect"></area
+ ><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,13,14"
+ alt="a"
+ shape="rect"></area></map
+ ><img width="447" id="imgmap"
+ height="15"
+ usemap="#atoz_map"
+ src="../letters.gif"></img><br
+ >Empty link:<br
+ ><a id="emptyLink" href=""><img src=""></img></a><br
+ >Link with embedded span<br
+ ><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a><br
+ >Named anchor, must not have "linked" state for it to be exposed correctly:<br
+ ><a id="namedAnchor" name="named_anchor">This should never be of state_linked</a>
+ </p>
+ <p id="p2"><a href="http://mozilla.org">mozilla.org</a></p>
+ <p id="p4"><a href="www">mozilla</a><a href="www">mozilla</a><span> te</span><span>xt </span><a href="www">mozilla</a></p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/hypertext/test_update.html b/accessible/tests/mochitest/hypertext/test_update.html
new file mode 100644
index 000000000..23ac8b2c8
--- /dev/null
+++ b/accessible/tests/mochitest/hypertext/test_update.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIHyper>TextAccessible in dynamic tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ const kLinksCount = 128;
+ function addLinks(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function addLinks_invoke()
+ {
+ for (var jdx = 0; jdx < kLinksCount; jdx++) {
+ var a = document.createElement("a");
+ a.setAttribute("href", "mozilla.org");
+ a.textContent = "mozilla";
+ this.containerNode.appendChild(a);
+
+ var span = document.createElement("span");
+ span.textContent = " text ";
+ this.containerNode.appendChild(span);
+ }
+ }
+
+ this.finalCheck = function addLinks_finalCheck()
+ {
+ // getLinkAt and getLinkIndex.
+ var htAcc = getAccessible(this.containerNode, [nsIAccessibleHyperText]);
+ for (var jdx = 0; jdx < kLinksCount; jdx++) {
+ var link = htAcc.getLinkAt(jdx);
+ ok(link, "No link at index " + jdx + " for '" + aContainerID + "'");
+
+ var linkIdx = htAcc.getLinkIndex(link);
+ is(linkIdx, jdx, "Wrong link index for '" + aContainerID + "'!");
+ }
+ }
+
+ this.getID = function addLinks_getID()
+ {
+ return "Add links for '" + aContainerID + "'";
+ }
+ }
+
+ function updateText(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.container = getAccessible(this.containerNode, nsIAccessibleHyperText);
+ this.text = this.container.firstChild;
+ this.textNode = this.text.DOMNode;
+ this.textLen = this.textNode.data.length;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_INSERTED, this.containerNode)
+ ];
+
+ this.invoke = function updateText_invoke()
+ {
+ is(this.container.getLinkIndexAtOffset(this.textLen), 0,
+ "Wrong intial text offsets!");
+
+ this.text.DOMNode.appendData(" my");
+ }
+
+ this.finalCheck = function updateText_finalCheck()
+ {
+ is(this.container.getLinkIndexAtOffset(this.textLen), -1,
+ "Text offsets weren't updated!");
+ }
+
+ this.getID = function updateText_getID()
+ {
+ return "update text for '" + aContainerID + "'";
+ }
+ }
+
+ /**
+ * Text offsets must be updated when hypertext child is removed.
+ */
+ function removeChild(aContainerID, aChildID, aInitialText, aFinalText)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.container = getAccessible(this.containerNode, nsIAccessibleText);
+ this.childNode = getNode(aChildID);
+
+ // Call first to getText so offsets are cached
+ is(this.container.getText(0, -1), aInitialText,
+ "Wrong text before child removal");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function removeChild_invoke()
+ {
+ this.containerNode.removeChild(this.childNode);
+ }
+
+ this.finalCheck = function removeChild_finalCheck()
+ {
+ is(this.container.getText(0, -1), aFinalText,
+ "Wrong text after child removal");
+ is(this.container.characterCount, aFinalText.length,
+ "Wrong text after child removal");
+ }
+
+ this.getID = function removeChild_getID()
+ {
+ return "check text after removing child from '" + aContainerID + "'";
+ }
+ }
+
+ function removeFirstChild(aContainer)
+ {
+ this.ht = getAccessible(aContainer, [ nsIAccessibleHyperText ]);
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aContainer)
+ ];
+
+ this.invoke = function removeFirstChild_invoke()
+ {
+ is(this.ht.linkCount, 2, "Wrong embedded objects count before removal");
+
+ getNode(aContainer).removeChild(getNode(aContainer).firstElementChild);
+ }
+
+ this.finalCheck = function removeFirstChild_finalCheck()
+ {
+ // check list index before link count
+ is(this.ht.getLinkIndex(this.ht.firstChild), 0, "Wrong child index");
+ is(this.ht.linkCount, 1, "Wrong embedded objects count after removal");
+ }
+
+ this.getID = function removeFirstChild_getID()
+ {
+ return "Remove first child and check embedded object indeces";
+ }
+ }
+
+ function removeLastChild(aContainer)
+ {
+ this.ht = getAccessible(aContainer, [ nsIAccessibleHyperText ]);
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aContainer)
+ ];
+
+ this.invoke = function removeLastChild_invoke()
+ {
+ is(this.ht.linkCount, 1, "Wrong embedded objects count before removal");
+
+ getNode(aContainer).removeChild(getNode(aContainer).lastElementChild);
+ }
+
+ this.finalCheck = function removeLastChild_finalCheck()
+ {
+ is(this.ht.linkCount, 0, "Wrong embedded objects count after removal");
+
+ var link = null;
+ try {
+ link = this.ht.getLinkAt(0);
+ } catch (e) { }
+ ok(!link, "No embedded object is expected");
+ }
+
+ this.getID = function removeLastChild_getID()
+ {
+ return "Remove last child and try its embedded object";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new addLinks("p1"));
+ gQueue.push(new updateText("p2"));
+ gQueue.push(new removeChild("div1","div2",
+ "hello my good friend", "hello friend"));
+ gQueue.push(new removeFirstChild("c4"));
+ gQueue.push(new removeLastChild("c5"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Cache links within hypertext accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=572394">
+ Mozilla Bug 572394
+ </a>
+ <a target="_blank"
+ title="Text offsets don't get updated when text of first child text accessible is changed"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=625009">
+ Mozilla Bug 625009
+ </a>
+ <a target="_blank"
+ title="Crash in nsHyperTextAccessible::GetText()"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630841">
+ Mozilla Bug 630841
+ </a><br>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p1"></p>
+ <p id="p2"><b>hello</b><a>friend</a></p>
+ <div id="div1">hello<span id="div2"> my<span id="div3"> good</span></span> friend</span></div>
+ <form id="c4">
+ <label for="c4_input">label</label>
+ <input id="c4_input">
+ </form>
+ <div id="c5">TextLeaf<input id="c5_input"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/a11y.ini b/accessible/tests/mochitest/jsat/a11y.ini
new file mode 100644
index 000000000..5acc3df90
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/a11y.ini
@@ -0,0 +1,30 @@
+[DEFAULT]
+support-files =
+ dom_helper.js
+ gestures.json
+ jsatcommon.js
+ output.js
+ doc_traversal.html
+ doc_content_integration.html
+ doc_content_text.html
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+skip-if = (os == 'win' && (os_version == '5.1' || os_version == '5.2'))
+
+[test_alive.html]
+[test_content_integration.html]
+skip-if = buildapp == 'mulet'
+[test_content_text.html]
+skip-if = buildapp == 'mulet'
+[test_explicit_names.html]
+[test_gesture_tracker.html]
+[test_hints.html]
+[test_landmarks.html]
+[test_live_regions.html]
+[test_output_mathml.html]
+[test_output.html]
+[test_quicknav_modes.html]
+[test_tables.html]
+[test_pointer_relay.html]
+[test_traversal.html]
+[test_traversal_helper.html]
diff --git a/accessible/tests/mochitest/jsat/doc_content_integration.html b/accessible/tests/mochitest/jsat/doc_content_integration.html
new file mode 100644
index 000000000..d62c000cb
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Traversal Rule test document</title>
+ <meta charset="utf-8" />
+ <script>
+ var frameContents = '<html>' +
+ '<head><title>such app</title></head>' +
+ '<body>' +
+ '<h1>wow</h1>' +
+ '<ul>' +
+ '<li><label><input type="checkbox">many option</label></li>' +
+ '</ul>' +
+ '<label for="r">much range</label>' +
+ '<input min="0" max="10" value="5" type="range" id="r">' +
+ '</body>' +
+ '</html>';
+
+ function showAlert() {
+ document.getElementById('alert').hidden = false;
+ }
+
+ function hideAlert() {
+ document.getElementById('alert').hidden = true;
+ }
+
+ function ariaShowBack() {
+ document.getElementById('back').setAttribute('aria-hidden', false);
+ }
+
+ function ariaHideBack() {
+ document.getElementById('back').setAttribute('aria-hidden', true);
+ }
+
+ function ariaShowIframe() {
+ document.getElementById('iframe').setAttribute('aria-hidden', false);
+ }
+
+ function ariaHideIframe() {
+ document.getElementById('iframe').setAttribute('aria-hidden', true);
+ }
+
+ function renameFruit() {
+ document.getElementById('fruit').setAttribute('aria-label', 'banana');
+ }
+
+ function renameSlider() {
+ document.getElementById('slider').setAttribute(
+ 'aria-label', 'mover');
+ }
+
+ function changeSliderValue() {
+ document.getElementById('slider').setAttribute('aria-valuenow', '5');
+ document.getElementById('slider').setAttribute(
+ 'aria-valuetext', 'medium');
+ }
+
+ function toggleLight() {
+ var lightSwitch = document.getElementById('light');
+ lightSwitch.setAttribute('aria-checked',
+ lightSwitch.getAttribute('aria-checked') === 'true' ? 'false' : 'true');
+ }
+
+ </script>
+ <style>
+ #windows {
+ position: relative;
+ width: 320px;
+ height: 480px;
+ }
+
+ #windows > iframe {
+ z-index: 1;
+ }
+
+ #windows > div[role='dialog'] {
+ z-index: 2;
+ background-color: pink;
+ }
+
+ #windows > * {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
+
+ iframe {
+ width: 100%;
+ height: 100%;
+ }
+
+ </style>
+
+</head>
+<body>
+ <div>Phone status bar</div>
+ <div id="windows">
+ <button id="back">Back</button>
+ <div role="dialog" id="alert" hidden>
+ <h1>This is an alert!</h1>
+ <p>Do you agree?</p>
+ <button onclick="setTimeout(hideAlert, 500)">Yes</button>
+ <button onclick="hideAlert()">No</button>
+ </div>
+ <div id="appframe"></div>
+ </div>
+ <button id="home">Home</button>
+ <button id="fruit" aria-label="apple"></button>
+ <span id="light" role="switch" aria-label="Light" aria-checked="false" onclick="toggleLight()"></span>
+ <div id="live" aria-live="polite" aria-label="live">
+ <div id="slider" role="slider" aria-label="slider" aria-valuemin="0"
+ aria-valuemax="10" aria-valuenow="0"></div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/doc_content_text.html b/accessible/tests/mochitest/jsat/doc_content_text.html
new file mode 100644
index 000000000..4e73dc6e7
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_content_text.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Text content test document</title>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <p>These are my awards, Mother. From Army.
+ The seal is for marksmanship, and the gorilla is for sand racing.</p>
+ <p>You're a good guy, mon frere. That means brother in French.
+ I don't know how I know that. I took four years of Spanish.</p>
+ <textarea>Please refrain from Mayoneggs during this salmonella scare.</textarea>
+ <label>So we don't get dessert?</label><input type="text">
+ </body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/doc_traversal.html b/accessible/tests/mochitest/jsat/doc_traversal.html
new file mode 100644
index 000000000..4c104b6b7
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Traversal Rule test document</title>
+ <meta charset="utf-8" />
+ <style>
+ .content:before {
+ content: "Content";
+ }
+ </style>
+</head>
+<body>
+ <header id="header-1">
+ <h3 id="heading-1">A small first heading</h3>
+ <form>
+ <label for="input-1-1">Name:</label>
+ <input id="input-1-1">
+ <label id="label-1-2">Favourite Ice Cream Flavour:<input id="input-1-2"></label>
+ <button id="button-1-1">Magic Button</button>
+ <label for="radio-1-1">Radios are old: </label>
+ <input id="radio-1-1" type="radio">
+ <label for="radio-1-2">Radios are new: </label>
+ <input id="radio-1-2" type="radio">
+ <label for="input-1-3">Password:</label>
+ <input id="input-1-3" type="password">
+ <label for="input-1-4">Unlucky number:</label>
+ <input id="input-1-4" type="tel">
+ <input id="button-1-2" type="button" value="Fun">
+ <label for="checkbox-1-1">Check me: </label>
+ <input id="checkbox-1-1" type="checkbox">
+ <select id="select-1-1">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <select id="select-1-2" multiple="true">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <label for="checkbox-1-2">Check me too: </label>
+ <input id="checkbox-1-2" type="checkbox">
+ <label for="checkbox-1-3">But not me: </label>
+ <input id="checkbox-1-3" type="checkbox" aria-hidden="true">
+ <label for="checkbox-1-4">Or me! </label>
+ <input id="checkbox-1-4" type="checkbox" style="visibility:hidden">
+ <select id="select-1-3" size="3">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <label for="input-1-5">Electronic mailing address:</label>
+ <input id="input-1-5" type="email">
+ <input id="button-1-3" type="submit" value="Submit">
+ </form>
+ </header>
+ <main id="main-1">
+ <h2 id="heading-2">A larger second</h2>
+ <div id="heading-3" role="heading">ARIA is fun</div>
+ <input id="button-2-1" type="button" value="More Fun">
+ <div id="button-2-2" tabindex="0" role="button">ARIA fun</div>
+ <div id="button-2-3" tabindex="0" role="button" aria-pressed="false">My little togglebutton</div>
+ <div id="button-2-4" tabindex="0" role="spinbutton">ARIA fun</div>
+ </main>
+ <h1 id="heading-4" style="display:none">Invisible header</h1>
+ <dl id="list-1">
+ <dt id="listitem-1-1">Programming Language</dt>
+ <dd>A esoteric weapon wielded by only the most formidable warriors,
+ for its unrelenting strict power is unfathomable.</dd>
+ </dl>
+ <ul id="list-2" onclick="alert('hi');">
+ <li id="listitem-2-1">Lists of Programming Languages</li>
+ <li id="listitem-2-2">Lisp
+ <ol id="list-3">
+ <li id="listitem-3-1">Scheme</li>
+ <li id="listitem-3-2">Racket</li>
+ <li id="listitem-3-3">Clojure</li>
+ <li id="listitem-3-4"><strong>Standard</strong> Lisp</li>
+ <li id="listitem-3-5"><a id="link-0" href="#">Common</a> Lisp</li>
+ <li id="listitem-3-6"><input id="checkbox-1-5" type="checkbox"> LeLisp</li>
+ </ol>
+ </li>
+ <li id="listitem-2-3">JavaScript</li>
+ </ul>
+ <section>
+ <h6 id="heading-5">The last (visible) one!</h6>
+ <img id="image-1" src="http://example.com" alt="">
+ <img id="image-2" src="../moz.png" alt="stuff">
+ <div id="image-3" tabindex="0" role="img">Not actually an image</div>
+ </section>
+ <section>
+ <h4 id="heading-6" aria-hidden="true">Hidden header</h4>
+ <a id="link-1" href="http://www.mozilla.org">Link</a>
+ <a id="anchor-1">Words</a>
+ <a id="link-2" href="http://www.mozilla.org">Link the second</a>
+ <a id="anchor-2">Sentences</a>
+ <a id="link-3" href="http://www.example.com">Link the third</a>
+ </section>
+ <hr id="separator-1">
+ <h6 id="heading-6"></h6>
+ <table id="table-1">
+ <tr>
+ <td>3</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>4</td>
+ <td>1</td>
+ </tr>
+ </table>
+ <section id="grid" role="grid">
+ <ol role="row">
+ <li role="presentation"></li>
+ <li role="columnheader" aria-label="Sunday">S</li>
+ <li role="columnheader">M</li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader" aria-label="Week 1">1</li>
+ <li role="gridcell"><span>3</span><div></div></li>
+ <li role="gridcell"><span>4</span><div>7</div></li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader">2</li>
+ <li role="gridcell"><span>5</span><div role="presentation">8</div></li>
+ <li id="gridcell4" role="gridcell">
+ <span>6</span><div aria-hidden="true"><div class="content"></div></div>
+ </li>
+ </ol>
+ </section>
+ <div id="separator-2" role="separator">Just an innocuous separator</div>
+ <table id="table-2">
+ <thead>
+ <tr>
+ <th>Dirty Words</th>
+ <th>Meaning</th>
+ </tr>
+ </thead>
+ <tfoot>
+ <tr>
+ <td>Mud</td>
+ <td>Wet Dirt</td>
+ </tr>
+ </tfoot>
+ <tbody>
+ <tr>
+ <td>Dirt</td>
+ <td>Messy Stuff</td>
+ </tr>
+ </tbody>
+ </table>
+ <footer id="footer-1">
+ <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+ <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
+ </footer>
+
+ <span id="switch-1" role="switch" aria-checked="false" aria-label="Light switch"></span>
+ <p>This is a MathML formula <math id="math-1" display="block">
+ <mfrac>
+ <mrow><mi>x</mi><mo>+</mo><mn>1</mn></mrow>
+ <msqrt><mn>3</mn><mo>/</mo><mn>4</mn></msqrt>
+ </mfrac>
+ </math> with some text after.</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/dom_helper.js b/accessible/tests/mochitest/jsat/dom_helper.js
new file mode 100644
index 000000000..c95d19dc1
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/dom_helper.js
@@ -0,0 +1,209 @@
+'use strict';
+
+/* global getMainChromeWindow, AccessFuTest, GestureSettings, GestureTracker,
+ SimpleTest, getBoundsForDOMElm, Point, Utils */
+/* exported loadJSON, eventMap */
+
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import('resource://gre/modules/Geometry.jsm');
+
+var win = getMainChromeWindow(window);
+
+/**
+ * Convert inch based point coordinates into pixels.
+ * @param {Array} aPoints Array of coordinates in inches.
+ * @return {Array} Array of coordinates in pixels.
+ */
+function convertPointCoordinates(aPoints) {
+ var dpi = Utils.dpi;
+ return aPoints.map(function convert(aPoint) {
+ return {
+ x: aPoint.x * dpi,
+ y: aPoint.y * dpi,
+ identifier: aPoint.identifier
+ };
+ });
+}
+
+/**
+ * For a given list of points calculate their coordinates in relation to the
+ * document body.
+ * @param {Array} aTouchPoints An array of objects of the following format: {
+ * base: {String}, // Id of an element to server as a base for the touch.
+ * x: {Number}, // An optional x offset from the base element's geometric
+ * // centre.
+ * y: {Number} // An optional y offset from the base element's geometric
+ * // centre.
+ * }
+ * @return {JSON} An array of {x, y} coordinations.
+ */
+function calculateTouchListCoordinates(aTouchPoints) {
+ var coords = [];
+ for (var i = 0, target = aTouchPoints[i]; i < aTouchPoints.length; ++i) {
+ var bounds = getBoundsForDOMElm(target.base);
+ var parentBounds = getBoundsForDOMElm('root');
+ var point = new Point(target.x || 0, target.y || 0);
+ point.scale(Utils.dpi);
+ point.add(bounds[0], bounds[1]);
+ point.add(bounds[2] / 2, bounds[3] / 2);
+ point.subtract(parentBounds[0], parentBounds[0]);
+ coords.push({
+ x: point.x,
+ y: point.y
+ });
+ }
+ return coords;
+}
+
+/**
+ * Send a touch event with specified touchPoints.
+ * @param {Array} aTouchPoints An array of points to be associated with
+ * touches.
+ * @param {String} aName A name of the touch event.
+ */
+function sendTouchEvent(aTouchPoints, aName) {
+ var touchList = sendTouchEvent.touchList;
+ if (aName === 'touchend') {
+ sendTouchEvent.touchList = null;
+ } else {
+ var coords = calculateTouchListCoordinates(aTouchPoints);
+ var touches = [];
+ for (var i = 0; i < coords.length; ++i) {
+ var {x, y} = coords[i];
+ var node = document.elementFromPoint(x, y);
+ var touch = document.createTouch(window, node, aName === 'touchstart' ?
+ 1 : touchList.item(i).identifier, x, y, x, y);
+ touches.push(touch);
+ }
+ touchList = document.createTouchList(touches);
+ sendTouchEvent.touchList = touchList;
+ }
+ var evt = document.createEvent('TouchEvent');
+ evt.initTouchEvent(aName, true, true, window, 0, false, false, false, false,
+ touchList, touchList, touchList);
+ document.dispatchEvent(evt);
+}
+
+sendTouchEvent.touchList = null;
+
+/**
+ * A map of event names to the functions that actually send them.
+ * @type {Object}
+ */
+var eventMap = {
+ touchstart: sendTouchEvent,
+ touchend: sendTouchEvent,
+ touchmove: sendTouchEvent
+};
+
+/**
+ * Attach a listener for the mozAccessFuGesture event that tests its
+ * type.
+ * @param {Array} aExpectedGestures A stack of expected event types.
+ * @param {String} aTitle Title of this sequence, if any.
+ * Note: the listener is removed once the stack reaches 0.
+ */
+function testMozAccessFuGesture(aExpectedGestures, aTitle) {
+ var types = aExpectedGestures;
+ function handleGesture(aEvent) {
+ if (aEvent.detail.type !== types[0].type) {
+ info('Got ' + aEvent.detail.type + ' waiting for ' + types[0].type);
+ // The is not the event of interest.
+ return;
+ }
+ is(!!aEvent.detail.edge, !!types[0].edge);
+ is(aEvent.detail.touches.length, types[0].fingers || 1,
+ 'failed to count fingers: ' + types[0].type);
+ ok(true, 'Received correct mozAccessFuGesture: ' +
+ JSON.stringify(types.shift()) + '. (' + aTitle + ')');
+ if (types.length === 0) {
+ win.removeEventListener('mozAccessFuGesture', handleGesture);
+ if (AccessFuTest.sequenceCleanup) {
+ AccessFuTest.sequenceCleanup();
+ }
+ AccessFuTest.nextTest();
+ }
+ }
+ win.addEventListener('mozAccessFuGesture', handleGesture);
+}
+
+/**
+ * Reset the thresholds and max delays that affect gesture rejection.
+ * @param {Number} aTimeStamp Gesture time stamp.
+ * @param {Boolean} aRemoveDwellThreshold An optional flag to reset dwell
+ * threshold.
+ * @param {Boolean} aRemoveSwipeMaxDuration An optional flag to reset swipe max
+ * duration.
+ */
+function setTimers(aTimeStamp, aRemoveDwellThreshold, aRemoveSwipeMaxDuration) {
+ if (!aRemoveDwellThreshold && !aRemoveSwipeMaxDuration) {
+ return;
+ }
+ if (aRemoveDwellThreshold) {
+ GestureSettings.dwellThreshold = 0;
+ }
+ if (aRemoveSwipeMaxDuration) {
+ GestureSettings.swipeMaxDuration = 0;
+ }
+ GestureTracker.current.clearTimer();
+ GestureTracker.current.startTimer(aTimeStamp);
+}
+
+function resetTimers(aRemoveGestureResolveDelay) {
+ GestureSettings.dwellThreshold = AccessFuTest.dwellThreshold;
+ GestureSettings.swipeMaxDuration = AccessFuTest.swipeMaxDuration;
+ GestureSettings.maxGestureResolveTimeout = aRemoveGestureResolveDelay ?
+ 0 : AccessFuTest.maxGestureResolveTimeout;
+}
+
+/**
+ * An extention to AccessFuTest that adds an ability to test a sequence of
+ * pointer events and their expected mozAccessFuGesture events.
+ * @param {Object} aSequence An object that has a list of pointer events to be
+ * generated and the expected mozAccessFuGesture events.
+ */
+AccessFuTest.addSequence = function AccessFuTest_addSequence(aSequence) {
+ AccessFuTest.addFunc(function testSequence() {
+ testMozAccessFuGesture(aSequence.expectedGestures, aSequence.title);
+ var events = aSequence.events;
+ function fireEvent(aEvent) {
+ var event = {
+ points: convertPointCoordinates(aEvent.points),
+ type: aEvent.type
+ };
+ var timeStamp = Date.now();
+ resetTimers(aEvent.removeGestureResolveDelay);
+ GestureTracker.handle(event, timeStamp);
+ setTimers(timeStamp, aEvent.removeDwellThreshold,
+ aEvent.removeSwipeMaxDuration);
+ processEvents();
+ }
+ function processEvents() {
+ if (events.length === 0) {
+ return;
+ }
+ var event = events.shift();
+ SimpleTest.executeSoon(function() {
+ fireEvent(event);
+ });
+ }
+ processEvents();
+ });
+};
+
+/**
+ * A helper function that loads JSON files.
+ * @param {String} aPath A path to a JSON file.
+ * @param {Function} aCallback A callback to be called on success.
+ */
+function loadJSON(aPath, aCallback) {
+ var request = new XMLHttpRequest();
+ request.open('GET', aPath, true);
+ request.responseType = 'json';
+ request.onload = function onload() {
+ aCallback(request.response);
+ };
+ request.send();
+}
diff --git a/accessible/tests/mochitest/jsat/gestures.json b/accessible/tests/mochitest/jsat/gestures.json
new file mode 100644
index 000000000..111994342
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/gestures.json
@@ -0,0 +1,352 @@
+[
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "dwellend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
+ {"type": "pointerup",
+ "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]},
+ {"type": "pointerup",
+ "points": [{"x": 0.97, "y": 1.01, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "doubletap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tripletap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "doubletaphold" },
+ { "type": "doubletapholdend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "taphold" }, { "type": "tapholdend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.15, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.3, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.3, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeleft" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipedown" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeup" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 0.95, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 0.95, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeleft" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipedown" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeup" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 2 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 2 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2},
+ {"x": 1, "y": 2, "identifier": 3}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2},
+ {"x": 1.5, "y": 2, "identifier": 3}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2},
+ {"x": 1.5, "y": 2, "identifier": 3}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 3 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 2, "y": 2, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 2, "y": 2, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeSwipeMaxDuration": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeSwipeMaxDuration": true},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "explore" }, { "type": "explore" },
+ { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove",
+ "points": [{"x": 1.5, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.55, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.65, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.7, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 0.075, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "edge": true, "fingers": 2 }]
+ },
+ {
+ "title": "Bug 1182311 - 3 finger triple tap is not reliable 1/2",
+ "events": [
+ {"points": [
+ {"y": 1.88467, "x": 0.89311, "identifier": 0},
+ {"y": 2.78481, "x": 0.56259, "identifier": 1},
+ {"y": 1.35021, "x": 1.37834, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.88467, "x": 0.89311, "identifier": 0},
+ {"y": 2.78481, "x": 0.56259, "identifier": 1},
+ {"y": 1.35021, "x": 1.37834, "identifier": 2}], "type": "pointerup"},
+ {"points": [
+ {"y": 1.76512, "x": 0.98453, "identifier": 0},
+ {"y": 1.1744, "x": 1.4346, "identifier": 1},
+ {"y": 2.5879, "x": 0.61181, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.76512, "x": 0.98453, "identifier": 0},
+ {"y": 1.1744, "x": 1.4346, "identifier": 1},
+ {"y": 2.5879, "x": 0.61181, "identifier": 2}], "type": "pointerup"},
+ {"points": [
+ {"y": 1.30098, "x": 1.52602, "identifier": 0},
+ {"y": 1.94093, "x": 1.02672, "identifier": 1},
+ {"y": 2.67229, "x": 0.75246, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.30098, "x": 1.52602, "identifier": 0},
+ {"y": 1.94093, "x": 1.02672, "identifier": 1},
+ {"y": 2.67229, "x": 0.75246, "identifier": 2}], "type": "pointerup",
+ "removeGestureResolveDelay": true}],
+ "expectedGestures": [{ "type": "tripletap", "fingers": 3 }]
+ },
+ {
+ "title": "Bug 1182311 - 3 finger triple tap is not reliable 2/2",
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 2.21875, "y": 1.510417}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.479167, "y": 2.53125}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 1.072917, "y": 3.739583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.46875, "y": 2.53125}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.447917, "y": 2.46875}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 0, "x": 2.21875, "y": 1.510417}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.447917, "y": 2.489583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 2, "x": 1.072917, "y": 3.739583}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 0.927083, "y": 3.864583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 2, "x": 0.927083, "y": 3.864583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.083333, "y": 3.71875}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375},
+ {"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375},
+ {"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.083333, "y": 3.71875}],
+ "removeGestureResolveDelay": true}
+ ],
+ "expectedGestures": [{ "type": "tripletap", "fingers": 3 }]
+ }
+
+]
diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js
new file mode 100644
index 000000000..aa7ee74e4
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -0,0 +1,739 @@
+// A common module to run tests on the AccessFu module
+
+'use strict';
+
+/*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
+ AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
+
+/**
+ * A global variable holding an array of test functions.
+ */
+var gTestFuncs = [];
+/**
+ * A global Iterator for the array of test functions.
+ */
+var gIterator;
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
+Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
+Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
+
+var AccessFuTest = {
+
+ addFunc: function AccessFuTest_addFunc(aFunc) {
+ if (aFunc) {
+ gTestFuncs.push(aFunc);
+ }
+ },
+
+ _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
+ var listener = {
+ observe: function observe(aMessage) {
+ // Ignore unexpected messages.
+ if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
+ return;
+ }
+ if (aMessage.message.indexOf(aWaitForMessage) < 0) {
+ return;
+ }
+ aListenerFunc.apply(listener);
+ }
+ };
+ Services.console.registerListener(listener);
+ return listener;
+ },
+
+ on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage, aListenerFunc);
+ },
+
+ off_log: function AccessFuTest_off_log(aListener) {
+ Services.console.unregisterListener(aListener);
+ },
+
+ once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage,
+ function listenAndUnregister() {
+ Services.console.unregisterListener(this);
+ aListenerFunc();
+ });
+ },
+
+ _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
+ var listener = function listener(aSubject, aTopic, aData) {
+ var data = JSON.parse(aData)[1];
+ // Ignore non-relevant outputs.
+ if (!data) {
+ return;
+ }
+ isDeeply(data.details, aWaitForData, "Data is correct");
+ aListener.apply(listener);
+ };
+ Services.obs.addObserver(listener, 'accessibility-output', false);
+ return listener;
+ },
+
+ on: function AccessFuTest_on(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, aListener);
+ },
+
+ off: function AccessFuTest_off(aListener) {
+ Services.obs.removeObserver(aListener, 'accessibility-output');
+ },
+
+ once: function AccessFuTest_once(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, function observerAndRemove() {
+ Services.obs.removeObserver(this, 'accessibility-output');
+ aListener();
+ });
+ },
+
+ _waitForExplicitFinish: false,
+
+ waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
+ this._waitForExplicitFinish = true;
+ },
+
+ finish: function AccessFuTest_finish() {
+ // Disable the console service logging.
+ Logger.test = false;
+ Logger.logLevel = Logger.INFO;
+ // Reset Gesture Settings.
+ GestureSettings.dwellThreshold = this.dwellThreshold =
+ this.originalDwellThreshold;
+ GestureSettings.swipeMaxDuration = this.swipeMaxDuration =
+ this.originalSwipeMaxDuration;
+ GestureSettings.maxGestureResolveTimeout =
+ this.maxGestureResolveTimeout =
+ this.originalMaxGestureResolveTimeout;
+ // Finish through idle callback to let AccessFu._disable complete.
+ SimpleTest.executeSoon(function () {
+ AccessFu.detach();
+ SimpleTest.finish();
+ });
+ },
+
+ nextTest: function AccessFuTest_nextTest() {
+ var result = gIterator.next();
+ if (result.done) {
+ this.finish();
+ return;
+ }
+ var testFunc = result.value;
+ testFunc();
+ },
+
+ runTests: function AccessFuTest_runTests(aAdditionalPrefs) {
+ if (gTestFuncs.length === 0) {
+ ok(false, "No tests specified!");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Create an Iterator for gTestFuncs array.
+ gIterator = (function*() {
+ for (var testFunc of gTestFuncs) {
+ yield testFunc;
+ }
+ })();
+
+ // Start AccessFu and put it in stand-by.
+ Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
+
+ AccessFu.attach(getMainChromeWindow(window));
+
+ AccessFu.readyCallback = function readyCallback() {
+ // Enable logging to the console service.
+ Logger.test = true;
+ Logger.logLevel = Logger.DEBUG;
+ };
+
+ var prefs = [['accessibility.accessfu.notify_output', 1],
+ ['dom.mozSettings.enabled', true]];
+ prefs.push.apply(prefs, aAdditionalPrefs);
+
+ this.originalDwellThreshold = GestureSettings.dwellThreshold;
+ this.originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
+ this.originalMaxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout;
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
+ // SimpleTest.executeSoon timeout is bigger than the timer settings in
+ // GestureSettings that causes intermittents.
+ this.dwellThreshold = GestureSettings.dwellThreshold =
+ GestureSettings.dwellThreshold * 10;
+ this.swipeMaxDuration = GestureSettings.swipeMaxDuration =
+ GestureSettings.swipeMaxDuration * 10;
+ this.maxGestureResolveTimeout = GestureSettings.maxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout * 10;
+
+ SpecialPowers.pushPrefEnv({ 'set': prefs }, function () {
+ if (AccessFuTest._waitForExplicitFinish) {
+ // Run all test functions asynchronously.
+ AccessFuTest.nextTest();
+ } else {
+ // Run all test functions synchronously.
+ gTestFuncs.forEach(testFunc => testFunc());
+ AccessFuTest.finish();
+ }
+ });
+ }
+};
+
+function AccessFuContentTest(aFuncResultPairs) {
+ this.queue = aFuncResultPairs;
+}
+
+AccessFuContentTest.prototype = {
+ expected: [],
+ currentAction: null,
+ actionNum: -1,
+
+ start: function(aFinishedCallback) {
+ Logger.logLevel = Logger.DEBUG;
+ this.finishedCallback = aFinishedCallback;
+ var self = this;
+
+ // Get top content message manager, and set it up.
+ this.mms = [Utils.getMessageManager(currentBrowser())];
+ this.setupMessageManager(this.mms[0], function () {
+ // Get child message managers and set them up
+ var frames = currentTabDocument().querySelectorAll('iframe');
+ if (frames.length === 0) {
+ self.pump();
+ return;
+ }
+
+ var toSetup = 0;
+ for (var i = 0; i < frames.length; i++ ) {
+ var mm = Utils.getMessageManager(frames[i]);
+ if (mm) {
+ toSetup++;
+ self.mms.push(mm);
+ self.setupMessageManager(mm, function () {
+ if (--toSetup === 0) {
+ // All message managers are loaded and ready to go.
+ self.pump();
+ }
+ });
+ }
+ }
+ });
+ },
+
+ finish: function() {
+ Logger.logLevel = Logger.INFO;
+ for (var mm of this.mms) {
+ mm.sendAsyncMessage('AccessFu:Stop');
+ mm.removeMessageListener('AccessFu:Present', this);
+ mm.removeMessageListener('AccessFu:Input', this);
+ mm.removeMessageListener('AccessFu:CursorCleared', this);
+ mm.removeMessageListener('AccessFu:Focused', this);
+ mm.removeMessageListener('AccessFu:AriaHidden', this);
+ mm.removeMessageListener('AccessFu:Ready', this);
+ mm.removeMessageListener('AccessFu:ContentStarted', this);
+ }
+ if (this.finishedCallback) {
+ this.finishedCallback();
+ }
+ },
+
+ setupMessageManager: function (aMessageManager, aCallback) {
+ function contentScript() {
+ addMessageListener('AccessFuTest:Focus', function (aMessage) {
+ var elem = content.document.querySelector(aMessage.json.selector);
+ if (elem) {
+ if (aMessage.json.blur) {
+ elem.blur();
+ } else {
+ elem.focus();
+ }
+ }
+ });
+ }
+
+ aMessageManager.addMessageListener('AccessFu:Present', this);
+ aMessageManager.addMessageListener('AccessFu:Input', this);
+ aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
+ aMessageManager.addMessageListener('AccessFu:Focused', this);
+ aMessageManager.addMessageListener('AccessFu:AriaHidden', this);
+ aMessageManager.addMessageListener('AccessFu:Ready', function () {
+ aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
+ aMessageManager.sendAsyncMessage('AccessFu:Start',
+ { buildApp: 'browser',
+ androidSdkVersion: Utils.AndroidSdkVersion,
+ logLevel: 'DEBUG',
+ inTest: true });
+ });
+
+ aMessageManager.loadFrameScript(
+ 'chrome://global/content/accessibility/content-script.js', false);
+ aMessageManager.loadFrameScript(
+ 'data:,(' + contentScript.toString() + ')();', false);
+ },
+
+ pump: function() {
+ this.expected.shift();
+ if (this.expected.length) {
+ return;
+ }
+
+ var currentPair = this.queue.shift();
+
+ if (currentPair) {
+ this.actionNum++;
+ this.currentAction = currentPair[0];
+ if (typeof this.currentAction === 'function') {
+ this.currentAction(this.mms[0]);
+ } else if (this.currentAction) {
+ this.mms[0].sendAsyncMessage(this.currentAction.name,
+ this.currentAction.json);
+ }
+
+ this.expected = currentPair.slice(1, currentPair.length);
+
+ if (!this.expected[0]) {
+ this.pump();
+ }
+ } else {
+ this.finish();
+ }
+ },
+
+ receiveMessage: function(aMessage) {
+ var expected = this.expected[0];
+
+ if (!expected) {
+ return;
+ }
+
+ var actionsString = typeof this.currentAction === 'function' ?
+ this.currentAction.name + '()' : JSON.stringify(this.currentAction);
+
+ if (typeof expected === 'string') {
+ ok(true, 'Got ' + expected + ' after ' + actionsString);
+ this.pump();
+ } else if (expected.ignore && !expected.ignore(aMessage)) {
+ expected.is(aMessage.json, 'after ' + actionsString +
+ ' (' + this.actionNum + ')');
+ expected.is_correct_focus();
+ this.pump();
+ }
+ }
+};
+
+// Common content messages
+
+var ContentMessages = {
+ simpleMoveFirst: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveFirst',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveLast: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveLast',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveNext: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveNext',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMovePrevious: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'movePrevious',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ clearCursor: {
+ name: 'AccessFu:ClearCursor',
+ json: {
+ origin: 'top'
+ }
+ },
+
+ moveOrAdjustUp: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'movePrevious',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ moveOrAdjustDown: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'moveNext',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ androidScrollForward: function adjustUp() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'forward' }
+ };
+ },
+
+ androidScrollBackward: function adjustDown() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'backward' }
+ };
+ },
+
+ focusSelector: function focusSelector(aSelector, aBlur) {
+ return {
+ name: 'AccessFuTest:Focus',
+ json: {
+ selector: aSelector,
+ blur: aBlur
+ }
+ };
+ },
+
+ activateCurrent: function activateCurrent(aOffset) {
+ return {
+ name: 'AccessFu:Activate',
+ json: {
+ origin: 'top',
+ offset: aOffset
+ }
+ };
+ },
+
+ moveNextBy: function moveNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ movePreviousBy: function movePreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretNextBy: function moveCaretNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ _granularityMap: {
+ 'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
+ 'word': 2, // MOVEMENT_GRANULARITY_WORD
+ 'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
+ }
+};
+
+function ExpectedMessage (aName, aOptions) {
+ this.name = aName;
+ this.options = aOptions || {};
+ this.json = {};
+}
+
+ExpectedMessage.prototype.lazyCompare = function(aReceived, aExpected, aInfo) {
+ if (aExpected && !aReceived) {
+ return [false, 'Expected something but got nothing -- ' + aInfo];
+ }
+
+ var matches = true;
+ var delta = [];
+ for (var attr in aExpected) {
+ var expected = aExpected[attr];
+ var received = aReceived[attr];
+ if (typeof expected === 'object') {
+ var [childMatches, childDelta] = this.lazyCompare(received, expected);
+ if (!childMatches) {
+ delta.push(attr + ' [ ' + childDelta + ' ]');
+ matches = false;
+ }
+ } else {
+ if (received !== expected) {
+ delta.push(
+ attr + ' [ expected ' + JSON.stringify(expected) +
+ ' got ' + JSON.stringify(received) + ' ]');
+ matches = false;
+ }
+ }
+ }
+
+ var msg = delta.length ? delta.join(' ') : 'Structures lazily match';
+ return [matches, msg + ' -- ' + aInfo];
+};
+
+ExpectedMessage.prototype.is = function(aReceived, aInfo) {
+ var checkFunc = this.options.todo ? 'todo' : 'ok';
+ SimpleTest[checkFunc].apply(
+ SimpleTest, this.lazyCompare(aReceived, this.json, aInfo));
+};
+
+ExpectedMessage.prototype.is_correct_focus = function(aInfo) {
+ if (!this.options.focused) {
+ return;
+ }
+
+ var checkFunc = this.options.focused_todo ? 'todo_is' : 'is';
+ var doc = currentTabDocument();
+ SimpleTest[checkFunc].apply(SimpleTest,
+ [ doc.activeElement, doc.querySelector(this.options.focused),
+ 'Correct element is focused: ' + this.options.focused + ' -- ' + aInfo ]);
+};
+
+ExpectedMessage.prototype.ignore = function(aMessage) {
+ return aMessage.name !== this.name;
+};
+
+function ExpectedPresent(aB2g, aAndroid, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Present', aOptions);
+ if (aB2g) {
+ this.json.b2g = aB2g;
+ }
+
+ if (aAndroid) {
+ this.json.android = aAndroid;
+ }
+}
+
+ExpectedPresent.prototype = Object.create(ExpectedMessage.prototype);
+
+ExpectedPresent.prototype.is = function(aReceived, aInfo) {
+ var received = this.extract_presenters(aReceived);
+
+ for (var presenter of ['b2g', 'android']) {
+ if (!this.options['no_' + presenter]) {
+ var todo = this.options.todo || this.options[presenter + '_todo']
+ SimpleTest[todo ? 'todo' : 'ok'].apply(
+ SimpleTest, this.lazyCompare(received[presenter],
+ this.json[presenter], aInfo + ' (' + presenter + ')'));
+ }
+ }
+};
+
+ExpectedPresent.prototype.extract_presenters = function(aReceived) {
+ var received = { count: 0 };
+ for (var presenter of aReceived) {
+ if (presenter) {
+ received[presenter.type.toLowerCase()] = presenter.details;
+ received.count++;
+ }
+ }
+
+ return received
+};
+
+ExpectedPresent.prototype.ignore = function(aMessage) {
+ if (ExpectedMessage.prototype.ignore.call(this, aMessage)) {
+ return true;
+ }
+
+ var received = this.extract_presenters(aMessage.json);
+ return received.count === 0 ||
+ (received.visual && received.visual.eventType === 'viewport-change') ||
+ (received.android &&
+ received.android[0].eventType === AndroidEvent.VIEW_SCROLLED);
+};
+
+function ExpectedCursorChange(aSpeech, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: 0x8000, // VIEW_ACCESSIBILITY_FOCUSED
+ }], aOptions);
+}
+
+ExpectedCursorChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCursorTextChange(aSpeech, aStartOffset, aEndOffset, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aStartOffset,
+ toIndex: aEndOffset
+ }], aOptions);
+
+ // bug 980509
+ this.options.b2g_todo = true;
+}
+
+ExpectedCursorTextChange.prototype =
+ Object.create(ExpectedCursorChange.prototype);
+
+function ExpectedClickAction(aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: 'clickAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED
+ }], aOptions);
+}
+
+ExpectedClickAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCheckAction(aChecked, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aChecked ? 'checkAction' : 'uncheckAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aChecked
+ }], aOptions);
+}
+
+ExpectedCheckAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedSwitchAction(aSwitched, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aSwitched ? 'onAction' : 'offAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aSwitched
+ }], aOptions);
+}
+
+ExpectedSwitchAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNameChange(aName, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'name-change',
+ data: aName
+ }, null, aOptions);
+}
+
+ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedValueChange(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'value-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextChanged(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'text-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedTextChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedEditState(aEditState, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Input', aOptions);
+ this.json = aEditState;
+}
+
+ExpectedEditState.prototype = Object.create(ExpectedMessage.prototype);
+
+function ExpectedTextSelectionChanged(aStart, aEnd, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
+ brailleOutput: {
+ selectionStart: aStart,
+ selectionEnd: aEnd
+ }}], aOptions);
+}
+
+ExpectedTextSelectionChanged.prototype =
+ Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextCaretChanged(aFrom, aTo, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aFrom,
+ toIndex: aTo
+ }], aOptions);
+}
+
+ExpectedTextCaretChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedAnnouncement(aAnnouncement, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.ANNOUNCEMENT,
+ text: [ aAnnouncement],
+ addedCount: aAnnouncement.length
+ }], aOptions);
+}
+
+ExpectedAnnouncement.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNoMove(aOptions) {
+ ExpectedPresent.call(this, {eventType: 'no-move' }, null, aOptions);
+}
+
+ExpectedNoMove.prototype = Object.create(ExpectedPresent.prototype);
+
+var AndroidEvent = {
+ VIEW_CLICKED: 0x01,
+ VIEW_LONG_CLICKED: 0x02,
+ VIEW_SELECTED: 0x04,
+ VIEW_FOCUSED: 0x08,
+ VIEW_TEXT_CHANGED: 0x10,
+ WINDOW_STATE_CHANGED: 0x20,
+ VIEW_HOVER_ENTER: 0x80,
+ VIEW_HOVER_EXIT: 0x100,
+ VIEW_SCROLLED: 0x1000,
+ VIEW_TEXT_SELECTION_CHANGED: 0x2000,
+ ANNOUNCEMENT: 0x4000,
+ VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
+ VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
+};
diff --git a/accessible/tests/mochitest/jsat/output.js b/accessible/tests/mochitest/jsat/output.js
new file mode 100644
index 000000000..5afcc8a66
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/output.js
@@ -0,0 +1,114 @@
+var Cu = Components.utils;
+const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import("resource://gre/modules/accessibility/OutputGenerator.jsm", this);
+
+/**
+ * Test context output generation.
+ *
+ * @param expected {Array} expected output.
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ * the |aAccOrElmOrID|.
+ * @param aGenerator the output generator to use when generating accessible
+ * output
+ *
+ * Note: if |aOldAccOrElmOrID| is not provided, the |aAccOrElmOrID| must be
+ * scoped to the "root" element in markup.
+ */
+function testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aGenerator) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ var oldAccessible = aOldAccOrElmOrID !== null ?
+ getAccessible(aOldAccOrElmOrID || 'root') : null;
+ var context = new PivotContext(accessible, oldAccessible);
+ var output = aGenerator.genForContext(context);
+
+ // Create a version of the output that has null members where we have
+ // null members in the expected output. Those are indexes that are not testable
+ // because of the changing nature of the test (different window names), or strings
+ // that are inaccessible to us, like the title of parent documents.
+ var masked_output = [];
+ for (var i=0; i < output.length; i++) {
+ if (expected[i] === null) {
+ masked_output.push(null);
+ } else {
+ masked_output[i] = typeof output[i] === "string" ? output[i].trim() :
+ output[i];
+ }
+ }
+
+ isDeeply(masked_output, expected,
+ "Context output is correct for " + aAccOrElmOrID +
+ " (output: " + JSON.stringify(output) + ") ==" +
+ " (expected: " + JSON.stringify(expected) + ")");
+}
+
+/**
+ * Test object output generated array that includes names.
+ * Note: test ignores outputs without the name.
+ *
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aGenerator the output generator to use when generating accessible
+ * output
+ */
+function testObjectOutput(aAccOrElmOrID, aGenerator) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ if (!accessible.name || !accessible.name.trim()) {
+ return;
+ }
+ var context = new PivotContext(accessible);
+ var output = aGenerator.genForObject(accessible, context);
+ var outputOrder;
+ try {
+ outputOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
+ } catch (ex) {
+ // PREF_UTTERANCE_ORDER not set.
+ outputOrder = 0;
+ }
+ var expectedNameIndex = outputOrder === 0 ? output.length - 1 : 0;
+ var nameIndex = output.indexOf(accessible.name);
+
+ if (nameIndex > -1) {
+ ok(output.indexOf(accessible.name) === expectedNameIndex,
+ "Object output is correct for " + aAccOrElmOrID);
+ }
+}
+
+/**
+ * Test object and context output for an accessible.
+ *
+ * @param expected {Array} expected output.
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ * the |aAccOrElmOrID|.
+ * @param aOutputKind the type of output
+ */
+function testOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aOutputKind) {
+ var generator;
+ if (aOutputKind === 1) {
+ generator = UtteranceGenerator;
+ } else {
+ generator = BrailleGenerator;
+ }
+ testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, generator);
+ // Just need to test object output for individual
+ // accOrElmOrID.
+ if (aOldAccOrElmOrID) {
+ return;
+ }
+ testObjectOutput(aAccOrElmOrID, generator);
+}
+
+function testHints(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ var oldAccessible = aOldAccOrElmOrID !== null ?
+ getAccessible(aOldAccOrElmOrID || 'root') : null;
+ var context = new PivotContext(accessible, oldAccessible);
+ var hints = context.interactionHints;
+
+ isDeeply(hints, expected,
+ "Context hitns are correct for " + aAccOrElmOrID +
+ " (hints: " + JSON.stringify(hints) + ") ==" +
+ " (expected: " + JSON.stringify(expected) + ")");
+}
diff --git a/accessible/tests/mochitest/jsat/test_alive.html b/accessible/tests/mochitest/jsat/test_alive.html
new file mode 100644
index 000000000..cd4eef712
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_alive.html
@@ -0,0 +1,81 @@
+<html>
+
+<head>
+ <title>AccessFu test for enabling</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function prefStart() {
+ AccessFuTest.once_log("AccessFu:Enabled", () =>
+ ok(AccessFu._enabled, "AccessFu was enabled again."));
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ // Start AccessFu via pref.
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ }
+
+ // Listen for 'EventManager.stop' and enable AccessFu again.
+ function settingsStart() {
+ isnot(AccessFu._enabled, true, "AccessFu was disabled.");
+ // XXX: Bug 978076 - test start with SettingsManager.
+ //navigator.mozSettings.createLock().set(
+ // {'accessibility.screenreader': false});
+ AccessFuTest.once_log("EventManager.start", () => {
+ ok(AccessFu._enabled, "AccessFu was enabled again.");
+ AccessFuTest.nextTest();
+ });
+ AccessFu._enable();
+ }
+
+ // Make sure EventManager is started again.
+ function settingsStop() {
+ ok(AccessFu._enabled, "AccessFu was enabled again.");
+ // XXX: Bug 978076 - test stop with SettingsManager.
+ //navigator.mozSettings.createLock().set(
+ // {'accessibility.screenreader': false});
+ AccessFuTest.once_log("EventManager.stop", () => {
+ isnot(AccessFu._enabled, "AccessFu was disabled.");
+ AccessFuTest.finish();
+ });
+ AccessFu._disable();
+ }
+
+ // Listen for initial 'EventManager.start' and disable AccessFu.
+ function prefStop() {
+ ok(AccessFu._enabled, "AccessFu was started via preference.");
+ AccessFuTest.once_log("AccessFu:Disabled", () =>
+ isnot(AccessFu._enabled, true, "AccessFu was disabled."));
+ AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest);
+
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ }
+
+ function doTest() {
+ AccessFuTest.addFunc(prefStart);
+ AccessFuTest.addFunc(prefStop);
+ AccessFuTest.addFunc(settingsStart);
+ AccessFuTest.addFunc(settingsStop);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+ title="[AccessFu] Add mochitest for enabling">
+ Mozilla Bug 811307
+ </a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_content_integration.html b/accessible/tests/mochitest/jsat/test_content_integration.html
new file mode 100644
index 000000000..809f79726
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -0,0 +1,343 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu content integration</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+ <script type="application/javascript" src="jsatcommon.js"></script>
+
+ <script type="application/javascript">
+ function doTest() {
+ var doc = currentTabDocument();
+ var iframe = doc.createElement('iframe');
+ iframe.id = 'iframe';
+ iframe.mozbrowser = true;
+ iframe.addEventListener('mozbrowserloadend', function () {
+ var contentTest = new AccessFuContentTest(
+ [
+ // Simple traversal forward
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['Traversal Rule test document', 'Phone status bar'],
+ { focused: 'body' })],
+ [ContentMessages.simpleMovePrevious, new ExpectedNoMove()],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext, new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}],
+ { focused: 'iframe' })],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+
+ // check checkbox
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedCheckAction(true)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
+ [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Light', {"string": "stateOff"}, {'string': 'switch'}])],
+ // switch on
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedSwitchAction(true)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['slider', '0', {'string': 'slider'}])],
+
+ // Simple traversal backward
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Light', {"string": "stateOn"}, {'string': 'switch'}])],
+ // switch off
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedSwitchAction(false)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['such app', 'much range', '6', {'string': 'slider'}])],
+ [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
+ [ContentMessages.androidScrollForward(), new ExpectedValueChange('6')],
+ [ContentMessages.androidScrollBackward(), new ExpectedValueChange('5')],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['many option', {'string': 'stateChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ // uncheck checkbox
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedCheckAction(false)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Phone status bar'])],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ // Moving to the absolute last item from an embedded document
+ // fails. Bug 972035.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ // Move from an inner frame to the last element in the parent doc
+ [ContentMessages.simpleMoveLast,
+ new ExpectedCursorChange(
+ ['slider', '0', {'string': 'slider'}], { b2g_todo: true })],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['such app', 'many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
+ // Calling AdjustOrMove should adjust the range.
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedValueChange('4')],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedValueChange('5')],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Moving to the absolute first item from an embedded document
+ // fails. Bug 972035.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.simpleMoveNext, new ExpectedCursorChange(
+ ['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.simpleMoveFirst,
+ new ExpectedCursorChange(['Phone status bar'], { b2g_todo: true })],
+
+ // Reset cursors
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Current virtual cursor's position's name changes
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.focusSelector('button#fruit', false),
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
+
+ // Name and value changes inside a live-region (no cursor present)
+ [doc.defaultView.renameSlider,
+ new ExpectedNameChange('mover')],
+ [doc.defaultView.changeSliderValue,
+ new ExpectedValueChange('medium')],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#fruit', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Move cursor with focus in outside document
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.focusSelector('button#home', false),
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#home', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Set focus on element outside of embedded frame while
+ // cursor is in frame
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.focusSelector('button#home', false),
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#home', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // XXX: Set focus on iframe itself.
+ // XXX: Set focus on element in iframe when cursor is outside of it.
+ // XXX: Set focus on element in iframe when cursor is in iframe.
+
+ // aria-hidden element that the virtual cursor is positioned on
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [doc.defaultView.ariaHideBack,
+ new ExpectedCursorChange(
+ ["such app", "wow", {"string": "headingLevel","args": [1]}])],
+ // Changing aria-hidden attribute twice and making sure that the event
+ // is fired only once when the actual change happens.
+ [doc.defaultView.ariaHideBack],
+ [doc.defaultView.ariaShowBack],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // aria-hidden on the iframe that has the vc.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [doc.defaultView.ariaHideIframe,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [doc.defaultView.ariaShowIframe],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // aria-hidden element and auto Move
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [doc.defaultView.ariaHideBack],
+ [ContentMessages.focusSelector('button#back', false),
+ // Must not speak Back button as it is aria-hidden
+ new ExpectedCursorChange(
+ ["such app", "wow", {"string": "headingLevel","args": [1]}])],
+ [doc.defaultView.ariaShowBack],
+ [ContentMessages.focusSelector('button#back', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog in outer doc, while cursor is also in outer doc
+ [ContentMessages.simpleMoveLast,
+ new ExpectedCursorChange(['Traversal Rule test document', 'mover',
+ 'medium', {'string': 'slider'}])],
+ [doc.defaultView.showAlert,
+ new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]},
+ {'string': 'dialog'}])],
+
+ [doc.defaultView.hideAlert,
+ new ExpectedCursorChange(['Traversal Rule test document', 'mover',
+ 'medium', {'string': 'slider'}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog in outer doc, while cursor is in inner frame
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [doc.defaultView.showAlert, new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]},
+ {'string': 'dialog'}])],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Do you agree?'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Yes', {'string': 'pushbutton'}])],
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction(),
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog, then focus on something when closing
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [doc.defaultView.showAlert,
+ new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
+
+ [function hideAlertAndFocusHomeButton() {
+ doc.defaultView.hideAlert();
+ doc.querySelector('button#home').focus();
+ }, new ExpectedCursorChange(['Traversal Rule test document',
+ 'Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])]
+ [ContentMessages.simpleMoveNext, new ExpectedNoMove()]
+ ]);
+
+ addA11yLoadEvent(function() {
+ contentTest.start(function () {
+ closeBrowserWindow();
+ SimpleTest.finish();
+ });
+ }, doc.defaultView)
+ });
+ iframe.src = 'data:text/html;charset=utf-8,' + doc.defaultView.frameContents;
+ doc.getElementById('appframe').appendChild(iframe);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(
+ function () {
+ openBrowserWindow(
+ function () {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ // TODO: remove this as part of bug 820712
+ ['network.disable.ipc.security', true],
+
+
+ ['dom.ipc.browser_frames.oop_by_default', true],
+ ['dom.mozBrowserFramesEnabled', true],
+ ['browser.pagethumbnails.capturing_disabled', true]
+ ]
+ }, doTest) },
+ getRootDirectory(window.location.href) + 'doc_content_integration.html');
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for OOP message handling and general integration"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_content_text.html b/accessible/tests/mochitest/jsat/test_content_text.html
new file mode 100644
index 000000000..558b819e9
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_content_text.html
@@ -0,0 +1,292 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu content integration</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+ <script type="application/javascript" src="jsatcommon.js"></script>
+
+ <script type="application/javascript">
+ function doTest() {
+ var doc = currentTabDocument();
+ var textTest = new AccessFuContentTest(
+ [
+ // Read-only text tests
+ [ContentMessages.simpleMoveFirst,
+ new ExpectedCursorChange(
+ ['Text content test document', 'These are my awards, Mother. ' +
+ 'From Army. The seal is for marksmanship, and the gorilla is ' +
+ 'for sand racing.'])],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('These', 0, 5)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('are', 6, 9)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('my', 10, 12)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('awards,', 13, 20)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('Mother.', 21, 28)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('awards,', 13, 20)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('my', 10, 12)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('are', 6, 9)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('These', 0, 5)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['You\'re a good guy, mon frere. ' +
+ 'That means brother in French. ' +
+ 'I don\'t know how I know that. ' +
+ 'I took four years of Spanish.'])],
+ // XXX: Word boundary should be past the apostraphe.
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('You\'re', 0, 6,
+ { android_todo: true /* Bug 980512 */})],
+
+ // Editable text tests.
+ [ContentMessages.focusSelector('textarea'),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: true,
+ atStart: true,
+ atEnd: false
+ }),
+ new ExpectedCursorChange(
+ ['Please refrain from Mayoneggs during this salmonella scare.',
+ {string: 'textarea'}]),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [ContentMessages.activateCurrent(10),
+ new ExpectedTextCaretChanged(0, 10),
+ new ExpectedEditState({ editing: true,
+ multiline: true,
+ atStart: false,
+ atEnd: false }),
+ new ExpectedTextSelectionChanged(10, 10)],
+ [ContentMessages.activateCurrent(20),
+ new ExpectedTextCaretChanged(10, 20),
+ new ExpectedTextSelectionChanged(20, 20)
+ ],
+ [ContentMessages.moveCaretNextBy('word'),
+ new ExpectedTextCaretChanged(20, 29),
+ new ExpectedTextSelectionChanged(29, 29)
+ ],
+ [ContentMessages.moveCaretNextBy('word'),
+ new ExpectedTextCaretChanged(29, 36),
+ new ExpectedTextSelectionChanged(36, 36)
+ ],
+ [ContentMessages.moveCaretNextBy('character'),
+ new ExpectedTextCaretChanged(36, 37),
+ new ExpectedTextSelectionChanged(37, 37)
+ ],
+ [ContentMessages.moveCaretNextBy('character'),
+ new ExpectedTextCaretChanged(37, 38),
+ new ExpectedTextSelectionChanged(38, 38)
+ ],
+ [ContentMessages.moveCaretNextBy('paragraph'),
+ new ExpectedTextCaretChanged(38, 59),
+ new ExpectedTextSelectionChanged(59, 59)
+ ],
+ [ContentMessages.moveCaretPreviousBy('word'),
+ new ExpectedTextCaretChanged(53, 59),
+ new ExpectedTextSelectionChanged(53, 53)
+ ],
+
+ // bug xxx
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['So we don\'t get dessert?', {string: 'label'}],
+ { focused: 'html'}),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false })],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ [{ string : 'entry' }],
+ { focused: 'html'})],
+ [ContentMessages.activateCurrent(0),
+ new ExpectedClickAction(),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ }, { focused: 'input[type=text]' }),
+ new ExpectedTextSelectionChanged(0, 0),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(
+ ['So we don\'t get dessert?', {string: 'label'}]),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false
+ },{ focused: 'html' })
+ ],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ [{ string : 'entry' }],
+ { focused: 'html'})],
+ [ContentMessages.activateCurrent(0),
+ new ExpectedClickAction(),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ },
+ { focused: 'input[type=text]' }),
+ new ExpectedTextSelectionChanged(0, 0)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(
+ [ 'So we don\'t get dessert?', {string: 'label'} ]),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false
+ }, { focused: 'html' })],
+
+ [ContentMessages.focusSelector('input'),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ }),
+ new ExpectedCursorChange([{string: 'entry'}]),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 3]]}, typeKey('a')());
+ },
+ new ExpectedTextChanged('a'),
+ new ExpectedTextSelectionChanged(1, 1),
+ ],
+ [typeKey('b'),
+ new ExpectedTextChanged('b'),
+ new ExpectedTextSelectionChanged(2, 2),
+ ],
+ [typeKey('c'),
+ new ExpectedTextChanged('c'),
+ new ExpectedTextSelectionChanged(3, 3),
+ ],
+ [typeKey('d'),
+ new ExpectedTextChanged('d'),
+ new ExpectedTextSelectionChanged(4, 4),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' abcd'),
+ new ExpectedTextSelectionChanged(5, 5),
+ ],
+ [typeKey('e'),
+ new ExpectedTextChanged('e'),
+ new ExpectedTextSelectionChanged(6, 6),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 2]]}, typeKey('a')());
+ },
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(7, 7),
+ ],
+ [typeKey('d'),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(8, 8),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' ead'),
+ new ExpectedTextSelectionChanged(9, 9),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 1]]}, typeKey('f')());
+ },
+ new ExpectedTextChanged('f'),
+ new ExpectedTextSelectionChanged(10, 10),
+ ],
+ [typeKey('g'),
+ new ExpectedTextChanged('g'),
+ new ExpectedTextSelectionChanged(11, 11),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' '),
+ new ExpectedTextSelectionChanged(12, 12),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 0]]}, typeKey('f')());
+ },
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(13, 13),
+ ],
+ [typeKey('g'),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(14, 14),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(15, 15),
+ ],
+ ]);
+
+ const KEYBOARD_ECHO_SETTING = 'accessibility.accessfu.keyboard_echo';
+ function typeKey(key) {
+ return function() { synthesizeKey(key, {}, currentTabWindow()); };
+ }
+
+ addA11yLoadEvent(function() {
+ textTest.start(function () {
+ closeBrowserWindow();
+ SimpleTest.finish();
+ });
+ }, doc.defaultView);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(
+ function () {
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_content_text.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for text editing and navigating"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_explicit_names.html b/accessible/tests/mochitest/jsat/test_explicit_names.html
new file mode 100644
index 000000000..fb7ed2022
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -0,0 +1,191 @@
+<html>
+<head>
+ <title>[AccessFu] Trust explicitly associated names when speaking certain elements</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="output.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "anchor1",
+ expected: [{"string": "link"}, "title"]
+ }, {
+ accOrElmOrID: "anchor2",
+ expected: [{"string": "link"}, "This is a link"]
+ }, {
+ accOrElmOrID: "button1",
+ expected: [{"string": "pushbutton"}, "Press me"]
+ }, {
+ accOrElmOrID: "button2",
+ expected: [{"string": "pushbutton"}, "Press me"]
+ }, {
+ accOrElmOrID: "textarea1",
+ expected: [{"string": "textarea"}, "This is the text area text.",
+ "Test Text Area"]
+ }, {
+ accOrElmOrID: "textarea2",
+ expected: [{"string": "textarea"}, "This is the text area text."]
+ }, {
+ accOrElmOrID: "heading1",
+ expected: [{"string": "headingLevel", "args": [1]}, "Test heading",
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "heading1",
+ oldAccOrElmOrID: null,
+ expected: [null /* parent doc title */, document.title,
+ {"string": "headingLevel", "args": [1]}, "Test heading",
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "heading2",
+ expected: [{"string": "headingLevel", "args": [1]},
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "list",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list",
+ {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ accOrElmOrID: "dlist",
+ expected: [{"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}, "Test Definition List",
+ "dd one"]
+ }, {
+ accOrElmOrID: "li_one",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list"]
+ }, {
+ accOrElmOrID: "li_two",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ accOrElmOrID: "cell",
+ expected: [{"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "List of Fruits",
+ {"string": "list"}, {"string": "listItemsCount", "count": 4},
+ {"string": "listStart"}, {"string": "link"}, "Apples",
+ {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"]
+ }, {
+ accOrElmOrID: "app.net",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ {"string": "listStart"}, {"string": "link"}, "star",
+ {"string": "listEnd"}, {"string": "link"}, "repost"]
+ }, {
+ // Test pivot to list from li_one.
+ accOrElmOrID: "list",
+ oldAccOrElmOrID: "li_one",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list",
+ {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ // Test pivot to li_one from list.
+ accOrElmOrID: "li_one",
+ oldAccOrElmOrID: "list",
+ expected: [{"string": "listStart"}, "Top of the list"]
+ }, {
+ // Test pivot to "apples" link from the table cell.
+ accOrElmOrID: "apples",
+ oldAccOrElmOrID: "cell",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 4},
+ {"string": "listStart"}, {"string": "link"}, "Apples"]
+ }, {
+ // Test pivot to the table cell from the "apples" link.
+ accOrElmOrID: "cell",
+ oldAccOrElmOrID: "apples",
+ expected: ["List of Fruits", {"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"]
+ }];
+
+ SpecialPowers.pushPrefEnv({"set": [[PREF_UTTERANCE_ORDER, 0]]}, function() {
+ // Test various explicit names vs the utterance generated from subtrees.
+ tests.forEach(function run(test) {
+ testOutput(test.expected, test.accOrElmOrID, test.oldAccOrElmOrID, 1);
+ });
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=845870"
+ title="[AccessFu] Trust explicitly associated names when speaking certain elements">
+ Mozilla Bug 845870
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <button id="button1" aria-label="Press me">This is not a name</button>
+ <button id="button2">
+ Press me
+ </button>
+ <a id="anchor1" href="#test" title="title"></a>
+ <a id="anchor2" href="#test">This is a link</a>
+ <textarea id="textarea1" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
+ <textarea id="textarea2" cols="80" rows="5">
+ This is the text area text.
+ </textarea>
+ <h1 id="heading1" title="Test heading">This is the heading.</h1>
+ <h1 id="heading2">
+ This is the heading.
+ </h1>
+ <ol id="list" title="Test List">
+ <li id="li_one" aria-label="Top of the list">list one</li>
+ <li id="li_two">list two</li>
+ </ol>
+ <dl id="dlist" title="Test Definition List">
+ <dd id="dd_one">dd one</dd>
+ </dl>
+ <table>
+ <caption>Fruits and vegetables</caption>
+ <tr>
+ <td id="cell" aria-label="List of Fruits">
+ <ul style="list-style-type: none;">
+ <li><a id="apples" href="#">Apples</a></li>
+ <li><a id="bananas" href="#">Bananas</a></li>
+ <li><a href="#">Peaches</a></li>
+ <li>
+ <a href="#">
+
+ Plums
+ </a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <!-- app.net -->
+ <ul id="app.net" class="unstyled ul-horizontal yui3-u fixed-right ta-right" style="list-style-type: none;">
+ <li class="yui3-u">
+ <a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826" aria-label="star">
+ Garbage
+ </a>
+ </li>
+ <li class="yui3-u repost">
+ <a href="#repost" title="repost" data-repost-button="1" data-reposted="0" data-post-id="5098826">
+ <i aria-label="repost" class="icon-repost"></i>
+ </a>
+ </li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_gesture_tracker.html b/accessible/tests/mochitest/jsat/test_gesture_tracker.html
new file mode 100644
index 000000000..af2755455
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_gesture_tracker.html
@@ -0,0 +1,51 @@
+<html>
+
+<head>
+ <title>AccessFu tests for gesture tracker.</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+ <script type="application/javascript" src="./jsatcommon.js"></script>
+ <script type="application/javascript" src="./dom_helper.js"></script>
+ <script type="application/javascript">
+
+ function startGestureTracker() {
+ GestureTracker.reset();
+ AccessFuTest.nextTest();
+ }
+
+ function stopGestureTracker() {
+ GestureTracker.reset();
+ AccessFuTest.finish();
+ }
+
+ function doTest() {
+ loadJSON("./gestures.json", function onSuccess(gestures) {
+ AccessFuTest.addFunc(startGestureTracker);
+ AccessFuTest.sequenceCleanup = GestureTracker.reset.bind(
+ GestureTracker);
+ gestures.forEach(AccessFuTest.addSequence);
+ AccessFuTest.addFunc(stopGestureTracker);
+ AccessFuTest.waitForExplicitFinish();
+ Logger.logLevel = Logger.GESTURE;
+ AccessFuTest.runTests();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=981015"
+ title="AccessFu tests for gesture tracker.">
+ Mozilla Bug 981015
+ </a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_hints.html b/accessible/tests/mochitest/jsat/test_hints.html
new file mode 100644
index 000000000..d5691b97a
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_hints.html
@@ -0,0 +1,89 @@
+<html>
+<head>
+ <title> [AccessFu] Interaction Hints </title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="output.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ var tests = [{
+ accOrElmOrID: 'can_wheel',
+ expectedHints: ['Swipe with two fingers to move between pages']
+ }, {
+ accOrElmOrID: 'nested_link',
+ expectedHints: [{string: 'link-hint'},
+ 'Swipe with two fingers to move between pages']
+ }, {
+ accOrElmOrID: 'nested_link',
+ oldAccOrElmOrID: 'can_wheel',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'link_with_default_hint',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'link_with_hint_override',
+ expectedHints: ['Tap and hold to get to menu']
+ }, {
+ accOrElmOrID: 'button_with_default_hint',
+ expectedHints: [{string: 'pushbutton-hint'}]
+ }, {
+ accOrElmOrID: 'button_with_hint_override',
+ expectedHints: ['Tap and hold to activate']
+ }, {
+ accOrElmOrID: 'nested_link2',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'nested_link3',
+ expectedHints: [{string: 'link-hint'}, {string: 'pushbutton-hint'},
+ "Double tap and hold to activate"]
+ }, {
+ accOrElmOrID: 'menuitemradio',
+ expectedHints: [{string: 'radiomenuitem-hint'}]
+ }];
+
+ // Test hints.
+ tests.forEach(function run(test) {
+ testHints(test.expectedHints, test.accOrElmOrID, test.oldAccOrElmOrID);
+ });
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069574"
+ title="[AccessFu] Interaction Hints">
+ Mozilla Bug 1069574
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <span role="region" id="can_wheel" aria-moz-hint="Swipe with two fingers to move between pages">
+ <a href="#" id="nested_link">I can be clicked</a>
+ </span>
+ <span role="region" aria-moz-hint="">
+ <a><a href="#" id="nested_link2">I can be clicked</a></a>
+ </span>
+ <span role="region" aria-moz-hint="Double tap and hold to activate">
+ <button><a href="#" id="nested_link3">I can be clicked</a></button>
+ </span>
+ <a href="#" id="link_with_default_hint">I can be clicked</a>
+ <a href="#" id="link_with_hint_override" aria-moz-hint="Tap and hold to get to menu">I am a special link</a>
+ <button id="button_with_default_hint">Toggle</button>
+ <button id="button_with_hint_override" aria-moz-hint="Tap and hold to activate">Special</button>
+ <span id="menuitemradio" role="menuitemradio">Item 1</span>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_landmarks.html b/accessible/tests/mochitest/jsat/test_landmarks.html
new file mode 100644
index 000000000..8b1a16f83
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_landmarks.html
@@ -0,0 +1,183 @@
+<html>
+<head>
+ <title> [AccessFu] Speak landmarks</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "nav",
+ expectedUtterance: [[{"string": "navigation"}, "a nav"],
+ ["a nav", {"string": "navigation"}]],
+ expectedBraille: [[{"string": "navigation"}, "a nav"],
+ ["a nav", {"string": "navigation"}]]
+ }, {
+ accOrElmOrID: "main",
+ expectedUtterance: [[{"string": "main"}, "a main area"],
+ ["a main area", {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "a main area"],
+ ["a main area", {"string": "main"}]]
+ }, {
+ accOrElmOrID: "header",
+ expectedUtterance: [
+ [{"string": "banner"}, {"string": "header"}, "a header"],
+ ["a header", {"string": "header"}, {"string": "banner"}]],
+ expectedBraille: [
+ [{"string": "banner"}, {"string": "headerAbbr"}, "a header"],
+ ["a header", {"string": "headerAbbr"}, {"string": "banner"}]]
+ }, {
+ accOrElmOrID: "footer",
+ expectedUtterance: [
+ [{"string": "contentinfo"}, {"string": "footer"}, "a footer"],
+ ["a footer", {"string": "footer"}, {"string": "contentinfo"}]],
+ expectedBraille: [
+ [{"string": "contentinfo"}, {"string": "footerAbbr"}, "a footer"],
+ ["a footer", {"string": "footerAbbr"}, {"string": "contentinfo"}]]
+ }, {
+ accOrElmOrID: "article_header",
+ expectedUtterance: [
+ [{"string": "header"}, "a header within an article"],
+ ["a header within an article", {"string": "header"}]],
+ expectedBraille: [
+ [{"string": "headerAbbr"}, "a header within an article"],
+ ["a header within an article", {"string": "headerAbbr"}]],
+ }, {
+ accOrElmOrID: "article_footer",
+ expectedUtterance: [
+ [{"string": "footer"}, "a footer within an article"],
+ ["a footer within an article", {"string": "footer"}]],
+ expectedBraille: [
+ [{"string": "footerAbbr"}, "a footer within an article"],
+ ["a footer within an article", {"string": "footerAbbr"}]]
+ }, {
+ accOrElmOrID: "section_header",
+ expectedUtterance: [[{"string":"header"}, "a header within a section"],
+ ["a header within a section", {"string":"header"}]],
+ expectedBraille: [
+ [{"string":"headerAbbr"}, "a header within a section"],
+ ["a header within a section", {"string":"headerAbbr"}]]
+ }, {
+ accOrElmOrID: "section_footer",
+ expectedUtterance: [
+ [{"string": "footer"}, "a footer within a section"],
+ ["a footer within a section", {"string": "footer"}]],
+ expectedBraille: [
+ [{"string": "footerAbbr"}, "a footer within a section"],
+ ["a footer within a section", {"string": "footerAbbr"}]]
+ }, {
+ accOrElmOrID: "aside",
+ expectedUtterance: [
+ [{"string": "complementary"}, "by the way I am an aside"],
+ ["by the way I am an aside", {"string": "complementary"}]],
+ expectedBraille: [
+ [{"string": "complementary"}, "by the way I am an aside"],
+ ["by the way I am an aside", {"string": "complementary"}]]
+ }, {
+ accOrElmOrID: "main_element",
+ expectedUtterance: [[{"string": "main"}, "another main area"],
+ ["another main area", {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "another main area"],
+ ["another main area", {"string": "main"}]]
+ }, {
+ accOrElmOrID: "complementary",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "complementary"},
+ {"string": "listStart"}, "A complementary"], ["A complementary",
+ {"string": "listStart"}, {"string": "complementary"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]],
+ expectedBraille: [["*", {"string": "complementary"}, "A complementary"],
+ ["*", "A complementary", {"string": "complementary"}]]
+ }, {
+ accOrElmOrID: "parent_main",
+ expectedUtterance: [[{"string": "main"}, "a parent main",
+ {"string": "complementary"}, "a child complementary"],
+ ["a parent main", "a child complementary",
+ {"string": "complementary"}, {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "a parent main",
+ {"string": "complementary"}, "a child complementary"],
+ ["a parent main", "a child complementary",
+ {"string": "complementary"}, {"string": "main"}]]
+ }, {
+ accOrElmOrID: "child_complementary",
+ expectedUtterance: [[{"string": "main"}, {"string": "complementary"},
+ "a child complementary"], ["a child complementary",
+ {"string": "complementary"}, {"string": "main"}]],
+ expectedBraille: [[{"string": "complementary"},
+ "a child complementary"], ["a child complementary",
+ {"string": "complementary"}]]
+ }];
+
+ // Test outputs (utterance and braille) for landmarks.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=888256"
+ title="[AccessFu] Speak landmarks">
+ Mozilla Bug 888256
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <nav id="nav">a nav</nav>
+ <header id="header">a header</header>
+ <footer id="footer">a footer</footer>
+ <article id="article_with_header_and_footer">
+ <header id="article_header">a header within an article</header>
+ <footer id="article_footer">a footer within an article</footer>
+ </article>
+ <section id="section_with_header_and_footer">
+ <header id="section_header">a header within a section</header>
+ <footer id="section_footer">a footer within a section</footer>
+ </section>
+ <aside id="aside">by the way I am an aside</aside>
+ <article id="main" role="main">a main area</article>
+ <main id="main_element">another main area</main>
+ <ul style="list-style-type: none;">
+ <li role="complementary" id="complementary">
+ A complementary
+ </li>
+ </ul>
+ <main id="parent_main">
+ a parent main
+ <p id="child_complementary" role="complementary">a child complementary</article>
+ </main>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_live_regions.html b/accessible/tests/mochitest/jsat/test_live_regions.html
new file mode 100644
index 000000000..53828f1b1
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_live_regions.html
@@ -0,0 +1,472 @@
+<html>
+
+<head>
+ <title>AccessFu tests for live regions support</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function startAccessFu() {
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ }
+
+ function stopAccessFu() {
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
+ }
+
+ function hide(id) {
+ var element = document.getElementById(id);
+ element.style.display = "none";
+ }
+
+ function show(id) {
+ var element = document.getElementById(id);
+ element.style.display = "block";
+ }
+
+ function ariaHide(id) {
+ var element = document.getElementById(id);
+ element.setAttribute('aria-hidden', true);
+ }
+
+ function ariaShow(id) {
+ var element = document.getElementById(id);
+ element.setAttribute('aria-hidden', false);
+ }
+
+ function udpate(id, text, property) {
+ var element = document.getElementById(id);
+ element[property] = text;
+ }
+
+ function updateText(id, text) {
+ udpate(id, text, "textContent");
+ }
+
+ function updateHTML(id, text) {
+ udpate(id, text, "innerHTML");
+ }
+
+ var tests = [{
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide1", "to_hide2", "to_hide3", "to_hide4"].forEach(id => hide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide_descendant1", "to_hide_descendant2",
+ "to_hide_descendant3", "to_hide_descendant4"].forEach(id => hide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show1", "to_show2", "to_show3", "to_show4"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show_descendant1", "to_show_descendant2",
+ "to_show_descendant3", "to_show_descendant4"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide5", "to_hide6", "to_hide7", "to_hide8", "to_hide9"].forEach(id => ariaHide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide_descendant5", "to_hide_descendant6",
+ "to_hide_descendant7", "to_hide_descendant8"].forEach(id => ariaHide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show5", "to_show6", "to_show7", "to_show8", "to_show9"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show_descendant5", "to_show_descendant6",
+ "to_show_descendant7", "to_show_descendant8"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ hide("to_hide_live_assertive");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ariaHide("to_hide_live_assertive2");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ["to_show_live_off", "to_show_live_assertive"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ["to_show_live_off2", "to_show_live_assertive2"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add", "Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add", "Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add_descendant", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add_descendant", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Descendant Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove_descendant", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add_descendant2", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add_descendant2", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Descendant Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove_descendant2", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am replaced", {"string": "main"}],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ var region = document.getElementById("to_replace_region");
+ var child = document.getElementById("to_replace");
+ child.setAttribute("role", "main");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am a replaced text"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("to_replace_text", "I am a replaced text");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am a replaced text"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("to_replace_text", "I am a replaced text");
+ }
+ }];
+
+ function doTest() {
+ AccessFuTest.addFunc(startAccessFu);
+ tests.forEach(function addTest(test) {
+ AccessFuTest.addFunc(function () {
+ AccessFuTest.once(test.expected, AccessFuTest.nextTest);
+ test.action();
+ });
+ });
+ AccessFuTest.addFunc(stopAccessFu);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=795957"
+ title="[AccessFu] Support live regions">
+ Mozilla Bug 795957
+ </a>
+ <div id="root">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+
+ <p id="to_hide1">I should not be announced 1</p>
+ <p id="to_hide2" aria-live="polite">I should not be announced 2</p>
+ <p id="to_hide3" aria-live="assertive" aria-relevant="text">I should not be announced 3</p>
+ <p id="to_hide4" aria-live="polite" aria-relevant="all">I will be hidden</p>
+
+ <p id="to_hide5" aria-hidden="true">I should not be announced 5</p>
+ <p id="to_hide6">I should not be announced 6</p>
+ <p id="to_hide7" aria-live="polite">I should not be announced 7</p>
+ <p id="to_hide8" aria-live="assertive" aria-relevant="text">I should not be announced 8</p>
+ <p id="to_hide9" aria-live="polite" aria-relevant="all">I will be hidden</p>
+
+ <div>
+ <p id="to_hide_descendant1">I should not be announced 1</p>
+ </div>
+ <div aria-live="polite">
+ <p id="to_hide_descendant2">I should not be announced 2</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_hide_descendant3">I should not be announced 3</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_hide_descendant4">I will be hidden</p>
+ </div>
+
+ <div>
+ <p id="to_hide_descendant5">I should not be announced 4</p>
+ </div>
+ <div aria-live="polite">
+ <p id="to_hide_descendant6">I should not be announced 5</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_hide_descendant7">I should not be announced 6</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_hide_descendant8">I will be hidden</p>
+ </div>
+
+ <p id="to_show1" style="display: none">I should not be announced 1</p>
+ <p id="to_show2" aria-live="assertive" aria-relevant="text" style="display: none">I should not be announced 2</p>
+ <p id="to_show3" aria-live="polite" aria-relevant="removals" style="display: none">I should not be announced 3</p>
+ <p id="to_show4" aria-live="polite" aria-relevant="all" style="display: none">I will be shown</p>
+
+ <p id="to_show5" aria-hidden="false">I should not be announced 5</p>
+ <p id="to_show6" aria-hidden="true">I should not be announced 6</p>
+ <p id="to_show7" aria-hidden="true" aria-live="assertive" aria-relevant="text">I should not be announced 7</p>
+ <p id="to_show8" aria-hidden="true" aria-live="polite" aria-relevant="removals">I should not be announced 8</p>
+ <p id="to_show9" aria-hidden="true" aria-live="polite" aria-relevant="all">I will be shown</p>
+
+ <div>
+ <p id="to_show_descendant1" style="display: none">I should not be announced 1</p>
+ </div>
+ <div aria-live="polite" aria-relevant="removals">
+ <p id="to_show_descendant2" style="display: none">I should not be announced 2</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_show_descendant3" style="display: none">I should not be announced 3</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_show_descendant4" style="display: none">I will be shown</p>
+ </div>
+
+ <div>
+ <p id="to_show_descendant5" aria-hidden="true">I should not be announced 5</p>
+ </div>
+ <div aria-live="polite" aria-relevant="removals">
+ <p id="to_show_descendant6" aria-hidden="true">I should not be announced 6</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_show_descendant7" aria-hidden="true">I should not be announced 7</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_show_descendant8" aria-hidden="true">I will be shown</p>
+ </div>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="to_hide_live_assertive">I will be hidden</p>
+ </div>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="to_hide_live_assertive2">I will be hidden</p>
+ </div>
+ <p id="to_show_live_assertive" aria-live="assertive" style="display: none">I will be shown</p>
+
+ <p id="to_show_live_off" aria-live="off" style="display: none">I will not be shown</p>
+
+ <p id="to_show_live_assertive2" aria-live="assertive" aria-hidden="true">I will be shown</p>
+
+ <p id="to_show_live_off2" aria-live="off" aria-hidden="true">I will not be shown</p>
+
+ <div id="to_replace_region" aria-live="polite" aria-relevant="all">
+ <p id="to_replace">I am replaced</p>
+ </div>
+
+ <p id="to_replace_text" aria-live="assertive" aria-relevant="text">I am going to be replaced</p>
+
+ <p id="text_add" aria-live="assertive" aria-relevant="text"></p>
+ <p id="text_remove" aria-live="polite" aria-relevant="all">Text Removed</p>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="text_add_descendant"></p>
+ </div>
+ <div aria-live="polite" aria-relevant="text">
+ <p id="text_remove_descendant">Descendant Text Removed</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="text_add_descendant2"></p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="text_remove_descendant2">Descendant Text Removed</p>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_output.html b/accessible/tests/mochitest/jsat/test_output.html
new file mode 100644
index 000000000..ec2b289be
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -0,0 +1,673 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=753984
+-->
+ <head>
+ <title>[AccessFu] utterance order test</title>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="./output.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID (with optional old accOrElmOrID).
+ // Note: each accOrElmOrID entry maps to a unique object utterance
+ // generator function within the UtteranceGenerator.
+ var tests = [{
+ accOrElmOrID: "anchor",
+ expectedUtterance: [[{"string": "link"}, "title"],
+ ["title", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "title"],
+ ["title", {"string": "linkAbbr"}]]
+ }, {
+ accOrElmOrID: "anchor_titleandtext",
+ expectedUtterance: [[{"string": "link"}, "goes to the tests -",
+ "Tests"], ["Tests", "- goes to the tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "goes to the tests -",
+ "Tests"], ["Tests", "- goes to the tests", {"string": "linkAbbr"}]],
+ }, {
+ accOrElmOrID: "anchor_duplicatedtitleandtext",
+ expectedUtterance: [[{"string": "link"}, "Tests"],
+ ["Tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "Tests"],
+ ["Tests", {"string": "linkAbbr"}]]
+ }, {
+ accOrElmOrID: "anchor_arialabelandtext",
+ expectedUtterance: [[{"string": "link"}, "goes to the tests - Tests"],
+ ["Tests - goes to the tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"},
+ "goes to the tests - Tests"], ["Tests - goes to the tests",
+ {"string": "linkAbbr"}]],
+ }, {
+ accOrElmOrID: "textarea",
+ expectedUtterance: [[{"string": "textarea"},
+ "This is the text area text."], ["This is the text area text.",
+ {"string": "textarea"}]],
+ expectedBraille: [[{"string": "textareaAbbr"},
+ "This is the text area text."], ["This is the text area text.",
+ {"string": "textareaAbbr"}]],
+ }, {
+ accOrElmOrID: "heading",
+ expectedUtterance: [[{"string": "headingLevel", "args": [1]},
+ "Test heading"], ["Test heading",
+ {"string": "headingLevel", "args": [1]}]],
+ expectedBraille: [[{"string": "headingAbbr"}, "Test heading"],
+ ["Test heading", {"string": "headingAbbr"}]]
+ }, {
+ accOrElmOrID: "list",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count":1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count":1}]
+ ],
+ expectedBraille: [[{"string": "listAbbr"}, "list one"],
+ ["list one", {"string": "listAbbr"}]]
+ }, {
+ accOrElmOrID: "dlist",
+ expectedUtterance: [[{"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}, "dd one"], ["dd one",
+ {"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}]
+ ],
+ expectedBraille: [[{"string": "definitionlistAbbr"}, "dd one"],
+ ["dd one", {"string": "definitionlistAbbr"}]]
+ }, {
+ accOrElmOrID: "li_one",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [["1.", "list one"], ["1.", "list one"]]
+ },
+ {
+ accOrElmOrID: "li_two",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "list two"], ["list two", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [["*", "list two"], ["*", "list two"]]
+ }, {
+ accOrElmOrID: "cell",
+ expectedUtterance: [[{"string":"table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"], ["Apples", {"string": "link"},
+ {"string": "listStart"}, "Bananas", {"string": "link"}, "Peaches",
+ {"string": "link"}, "Plums", {"string": "link"},
+ {"string": "listEnd"}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "Fruits and vegetables",
+ {"string":"table"}, {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [ 1, 1]},
+ {"string": "listAbbr"}, {"string": "linkAbbr"}, "Apples",
+ {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+ "Peaches", {"string": "linkAbbr"}, "Plums"], ["Apples",
+ {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+ "Peaches", {"string": "linkAbbr"}, "Plums", {"string": "linkAbbr"},
+ {"string": "listAbbr"},
+ {"string": "cellInfoAbbr", "args": [ 1, 1]}]]
+ }, {
+ accOrElmOrID: "date",
+ expectedUtterance: [[{"string": "textInputType_date"},
+ {"string": "entry"}, "2011-09-29"], ["2011-09-29",
+ {"string": "textInputType_date"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_date"},
+ {"string": "entryAbbr"}, "2011-09-29"], ["2011-09-29",
+ {"string": "textInputType_date"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "email",
+ expectedUtterance: [[{"string": "textInputType_email"},
+ {"string": "entry"}, "test@example.com"], ["test@example.com",
+ {"string": "textInputType_email"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_email"},
+ {"string": "entryAbbr"}, "test@example.com"], ["test@example.com",
+ {"string": "textInputType_email"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "search",
+ expectedUtterance: [[{"string": "textInputType_search"},
+ {"string": "entry"}, "This is a search"], ["This is a search",
+ {"string": "textInputType_search"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_search"},
+ {"string": "entryAbbr"}, "This is a search"], ["This is a search",
+ {"string": "textInputType_search"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "tel",
+ expectedUtterance: [[{"string": "textInputType_tel"},
+ {"string": "entry"}, "555-5555"], ["555-5555",
+ {"string": "textInputType_tel"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_tel"},
+ {"string": "entryAbbr"}, "555-5555"], ["555-5555",
+ {"string": "textInputType_tel"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "url",
+ expectedUtterance: [[{"string": "textInputType_url"},
+ {"string": "entry"}, "http://example.com"], ["http://example.com",
+ {"string": "textInputType_url"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_url"},
+ {"string": "entryAbbr"}, "http://example.com"],
+ ["http://example.com", {"string": "textInputType_url"},
+ {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "textInput",
+ expectedUtterance: [[{"string": "entry"}, "This is text."],
+ ["This is text.", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "This is text."],
+ ["This is text.", {"string": "entryAbbr"}]]
+ }, {
+ // Test pivot to list from li_one.
+ accOrElmOrID: "list",
+ oldAccOrElmOrID: "li_one",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [[{"string": "listAbbr"}, "list one"],
+ ["list one", {"string": "listAbbr"}]]
+ }, {
+ // Test pivot to "apples" link from the table cell.
+ accOrElmOrID: "apples",
+ oldAccOrElmOrID: "cell",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples"], ["Apples", {"string": "link"},
+ {"string": "listStart"}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4}]
+ ],
+ expectedBraille: [["*", {"string": "linkAbbr"}, "Apples"],
+ ["*", "Apples", {"string": "linkAbbr"}]]
+ }, {
+ // Test pivot to "bananas" link from "apples" link.
+ accOrElmOrID: "bananas",
+ oldAccOrElmOrID: "apples",
+ expectedUtterance: [[{"string": "link"}, "Bananas"],
+ ["Bananas", {"string": "link"}]],
+ expectedBraille: [["*", {"string": "linkAbbr"}, "Bananas"],
+ ["*", "Bananas", {"string": "linkAbbr"}]]
+ }, {
+ // test unavailable state utterance
+ accOrElmOrID: "unavailableButton",
+ expectedUtterance: [[{"string": "stateUnavailable"},
+ {"string": "pushbutton"}, "I am unavailable"], ["I am unavailable",
+ {"string": "stateUnavailable"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am unavailable"],
+ ["I am unavailable", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test expanded state utterance
+ accOrElmOrID: "expandedButton",
+ expectedUtterance: [[{"string": "stateExpanded"},
+ {"string": "pushbutton"}, "I am expanded"], ["I am expanded",
+ {"string": "stateExpanded"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am expanded"],
+ ["I am expanded", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test collapsed state utterance
+ accOrElmOrID: "collapsedButton",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "pushbutton"}, "I am collapsed"], ["I am collapsed",
+ {"string": "stateCollapsed"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am collapsed"],
+ ["I am collapsed", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test required state utterance
+ accOrElmOrID: "requiredInput",
+ expectedUtterance: [[{"string": "stateRequired"}, {"string": "entry"},
+ "I am required"], ["I am required", {"string": "stateRequired"},
+ {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "I am required"],
+ ["I am required", {"string": "entryAbbr"}]]
+ }, {
+ // test unavailable state utterance on inputs
+ accOrElmOrID: "readonlyInput",
+ expectedUtterance: [[{"string": "stateReadonly"}, {"string": "entry"},
+ "No edits"], ["No edits", {"string": "stateReadonly"},
+ {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "No edits"],
+ ["No edits", {"string": "entryAbbr"}]]
+ }, {
+ // test unavailable state utterance on textareas
+ accOrElmOrID: "readonlyTextarea",
+ expectedUtterance: [[{"string": "stateReadonly"}, {"string": "textarea"},
+ "No editing"], ["No editing", {"string": "stateReadonly"},
+ {"string": "textarea"}]],
+ expectedBraille: [[{"string": "textareaAbbr"}, "No editing"],
+ ["No editing", {"string": "textareaAbbr"}]]
+ }, {
+ // test has popup state utterance
+ accOrElmOrID: "hasPopupButton",
+ expectedUtterance: [[{"string": "stateHasPopup"},
+ {"string": "buttonmenu"}, "I have a popup"], ["I have a popup",
+ {"string": "stateHasPopup"}, {"string": "buttonmenu"}]],
+ expectedBraille: [[{"string": "buttonmenuAbbr"}, "I have a popup"],
+ ["I have a popup", {"string": "buttonmenuAbbr"}]]
+ }, {
+ // Test selected tab
+ accOrElmOrID: "tab1",
+ expectedUtterance: [[{"string": "pagetablist"},
+ {"string": "stateSelected"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+ {"string": "stateSelected"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [1, 2]}, {"string": "pagetablist"}]
+ ],
+ expectedBraille: [[{"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+ {"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [1, 2]}]]
+ }, {
+ // Test unselected tab
+ accOrElmOrID: "tab2",
+ expectedUtterance: [[{"string": "pagetablist"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+ {"string": "pagetab"}, {"string": "objItemOfN", "args": [2, 2]},
+ {"string": "pagetablist"}]],
+ expectedBraille: [[{"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+ {"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [2, 2]}]]
+ }, {
+ // Landing on this label should mimic landing on the checkbox.
+ accOrElmOrID: "label1",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Orange"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+ ["Orange", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // Here we get a top-level view of the form.
+ accOrElmOrID: "form1",
+ expectedUtterance: [[{"string": "label"},
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+ "Orange", {"string": "stateNotChecked"}, {"string": "checkbutton"},
+ "Blue", {"string": "label"}, "Blue"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+ {"string": "label"}, "Blue", {"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Blue", {"string": "label"}]],
+ expectedBraille: [[{"string": "labelAbbr"},
+ {"string": "stateUncheckedAbbr"}, "Orange", "Orange",
+ {"string": "stateUncheckedAbbr"}, "Blue", {"string": "labelAbbr"},
+ "Blue"], ["Orange", {"string": "stateUncheckedAbbr"}, "Orange",
+ {"string": "labelAbbr"}, "Blue", {"string": "stateUncheckedAbbr"},
+ "Blue", {"string": "labelAbbr"}]]
+ }, {
+ // This is a non-nesting label.
+ accOrElmOrID: "label2",
+ expectedUtterance: [[{"string": "label"}, "Blue"],
+ ["Blue", {"string": "label"}]],
+ expectedBraille: [[{"string": "labelAbbr"}, "Blue"],
+ ["Blue", {"string": "labelAbbr"}]]
+ }, {
+ // This is a distinct control.
+ accOrElmOrID: "input2",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Blue"], ["Blue",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Blue"], ["Blue",
+ {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // This is a nested control.
+ accOrElmOrID: "input1",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Orange"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+ ["Orange", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // Landing on this label should mimic landing on the entry.
+ accOrElmOrID: "label3",
+ expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entryAbbr"}]]
+ }, {
+ // This is a nested control with a value.
+ accOrElmOrID: "input3",
+ expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entryAbbr"}]]
+ }, {
+ // This is a nested control with a value.
+ accOrElmOrID: "input4",
+ expectedUtterance: [[{"string": "slider"}, "3", "Points:"],
+ ["Points:", "3", {"string": "slider"}]],
+ expectedBraille: [[{"string": "sliderAbbr"}, "3", "Points:"],
+ ["Points:", "3", {"string": "sliderAbbr"}]]
+ }, {
+ accOrElmOrID: "password",
+ expectedUtterance: [[{"string": "passwordtext"}, "Secret Password"],
+ ["Secret Password", {"string": "passwordtext"}]],
+ expectedBraille: [[{"string": "passwordtextAbbr"}, "Secret Password"],
+ ["Secret Password", {"string": "passwordtextAbbr"}]]
+ }, {
+ accOrElmOrID: "input5",
+ expectedUtterance: [[{"string": "stateChecked"},
+ {"string": "checkbutton"}, "Boring label"], ["Boring label",
+ {"string": "stateChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateCheckedAbbr"}, "Boring label"],
+ ["Boring label", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "radio_unselected",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "radiobutton"}, "any old radio button"],
+ ["any old radio button", {"string": "stateNotChecked"},
+ {"string": "radiobutton"}]
+ ],
+ expectedBraille: [
+ [{"string": "stateUncheckedAbbr"}, "any old radio button"],
+ ["any old radio button", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "radio_selected",
+ expectedUtterance: [[{"string": "stateChecked"},
+ {"string": "radiobutton"}, "a unique radio button"],
+ ["a unique radio button", {"string": "stateChecked"},
+ {"string": "radiobutton"}]],
+ expectedBraille: [
+ [{"string": "stateCheckedAbbr"}, "a unique radio button"],
+ ["a unique radio button", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "togglebutton_notpressed",
+ expectedUtterance: [[{"string": "togglebutton"}, "I am not pressed"],
+ ["I am not pressed", {"string": "togglebutton"}]],
+ expectedBraille: [
+ [{"string": "stateUnpressedAbbr"}, "I am not pressed"],
+ ["I am not pressed", {"string": "stateUnpressedAbbr"}]]
+ }, {
+ accOrElmOrID: "togglebutton_pressed",
+ expectedUtterance: [[{"string": "statePressed"},
+ {"string": "togglebutton"}, "I am pressed!"], ["I am pressed!",
+ {"string": "statePressed"}, {"string": "togglebutton"}]],
+ expectedBraille: [[{"string": "statePressedAbbr"}, "I am pressed!"],
+ ["I am pressed!", {"string": "statePressedAbbr"}]]
+ }, {
+ accOrElmOrID: "listbox-option",
+ expectedUtterance: [[{"string": "listbox"},
+ {"string": "listboxoption"}, "Search suggestion"],
+ ["Search suggestion", {"string": "listboxoption"},
+ {"string": "listbox"}]
+ ],
+ expectedBraille: [
+ [{"string": "listboxoptionAbbr"}, "Search suggestion"],
+ ["Search suggestion", {"string": "listboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "listbox-option2",
+ oldAccOrElmOrID: "listbox-option",
+ expectedUtterance: [[{"string": "listboxoption"}, "555-12345"],
+ ["555-12345", {"string": "listboxoption"}]],
+ expectedBraille: [[{"string": "listboxoptionAbbr"}, "555-12345"],
+ ["555-12345", {"string": "listboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "columnheader",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args" :[1]}, "Sunday"], ["Sunday",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args" :[1]}]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 1]},
+ "Sunday"], ["Sunday", {"string": "cellInfoAbbr", "args": [1, 1]}]]
+ }, {
+ accOrElmOrID: "rowheader",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "Sunday", "Week 1"], ["Week 1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "Sunday"]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 2]},
+ "Sunday", "Week 1"], ["Week 1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "Sunday"]]
+ }, {
+ accOrElmOrID: "gridcell1",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [["3"], ["3"]],
+ expectedBraille: [["3"], ["3"]]
+ }, {
+ accOrElmOrID: "gridcell2",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [["4", "7"], ["4", "7"]],
+ expectedBraille: [["4", "7"], ["4", "7"]]
+ }, {
+ accOrElmOrID: "gridcell3",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "stateSelected"}, "5"],
+ ["5", {"string": "stateSelected"}]],
+ expectedBraille: [["5"], ["5"]],
+ }, {
+ accOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}, "15 min"], [
+ "15 min", {"string": "stateCollapsed"}, {"string": "stateHasPopup"},
+ {"string": "combobox"}]],
+ expectedBraille: [[{"string": "comboboxAbbr"}, "15 min"], ["15 min",
+ {"string": "comboboxAbbr"}]]
+ }, {
+ accOrElmOrID: "selected-combobox-option",
+ oldAccOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "stateSelected"},
+ {"string": "comboboxoption"}, "15 min"], ["15 min",
+ {"string": "stateSelected"}, {"string": "comboboxoption"}]],
+ expectedBraille: [[{"string": "comboboxoptionAbbr"}, "15 min"], [
+ "15 min", {"string": "comboboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "combobox-option",
+ oldAccOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "comboboxoption"}, "30 min"], [
+ "30 min", {"string": "comboboxoption"}]],
+ expectedBraille: [[{"string": "comboboxoptionAbbr"}, "30 min"], [
+ "30 min", {"string": "comboboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "labelled-combobox",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}, "Never",
+ "Intervals"], ["Intervals", "Never", {"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}]],
+ expectedBraille: [[{"string": "comboboxAbbr"}, "Never", "Intervals"],
+ ["Intervals", "Never", {"string": "comboboxAbbr"}]]
+ }, {
+ accOrElmOrID: "statusbar-1",
+ expectedUtterance: [["Last sync:", "2 days ago"],
+ ["Last sync:", "2 days ago"]],
+ expectedBraille: [["Last sync:", "2 days ago"],
+ ["Last sync:", "2 days ago"]]
+ }, {
+ accOrElmOrID: "statusbar-2",
+ expectedUtterance: [["Last sync: 30min ago"],
+ ["Last sync: 30min ago"]],
+ expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
+ }, {
+ accOrElmOrID: "switch-1",
+ expectedUtterance: [[{"string": "stateOn"}, {"string": "switch"},
+ "Simple switch"], ["Simple switch", {"string": "stateOn"},
+ {"string": "switch"}]],
+ expectedBraille: [[{"string": "stateCheckedAbbr"}, "Simple switch"],
+ ["Simple switch", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "switch-2",
+ expectedUtterance: [[{"string": "stateOff"},
+ {"string": "switch"}, "Another switch"], ["Another switch",
+ {"string": "stateOff"}, {"string": "switch"}]],
+ expectedBraille: [
+ [{"string": "stateUncheckedAbbr"}, "Another switch"],
+ ["Another switch", {"string": "stateUncheckedAbbr"}]]
+ }];
+
+ // Test all possible utterance order preference values.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+ </script>
+ </head>
+ <body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=753984"
+ title="[AccessFu] utterance order test">
+ Mozilla Bug 753984</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=758675"
+ title="[AccessFu] Add support for accDescription">
+ Mozilla Bug 758675</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=876475"
+ title="[AccessFu] Make braille output less verbose">
+ Mozilla Bug 876475</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=924284"
+ title="[AccessFu] Output accessible values">
+ Mozilla Bug 924284</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=925845"
+ title="[AccessFu] Unify output tests">
+ Mozilla Bug 925845</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <a id="anchor" href="#test" title="title"></a>
+ <a id="anchor_titleandtext" href="#test" title="goes to the tests">Tests</a>
+ <a id="anchor_duplicatedtitleandtext" href="#test" title="Tests">Tests</a>
+ <a id="anchor_arialabelandtext" href="#test" aria-label="Tests" title="goes to the tests">Tests</a>
+ <textarea id="textarea" cols="80" rows="5">
+ This is the text area text.
+ </textarea>
+ <h1 id="heading" title="Test heading"></h1>
+ <ol id="list">
+ <li id="li_one">list one</li>
+ </ol>
+ <ul id="unorderd_list">
+ <li id="li_two">list two</li>
+ </ul>
+ <dl id="dlist">
+ <dd id="dd_one">
+ dd one
+ </dd>
+ </dl>
+ <table>
+ <caption>Fruits and vegetables</caption>
+ <tr>
+ <td id="cell">
+ <ul style="list-style-type: none;">
+ <li><a id="apples" href="#">Apples</a></li>
+ <li><a id="bananas" href="#">Bananas</a></li>
+ <li><a href="#">Peaches</a></li>
+ <li>
+ <a href="#">
+ Plums
+ </a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <button id="unavailableButton" disabled>I am unavailable</button>
+ <button id="expandedButton" aria-expanded="true">I am expanded</button>
+ <button id="collapsedButton" aria-expanded="false">I am collapsed</button>
+ <input id="requiredInput" required placeholder="I am required" />
+ <input id="readonlyInput" readonly value="No edits" />
+ <textarea id="readonlyTextarea" readonly>No editing</textarea>
+ <button id="hasPopupButton" aria-haspopup="true">I have a popup</button>
+ <div role="tablist">
+ <a id="tab1" href="#" role="tab" aria-selected="true">Account</a>
+ <a id="tab2" href="#" role="tab" aria-selected="false">Advanced</a>
+ </div>
+ <form id="form1">
+ <label id="label1"><input id="input1" type="checkbox">Orange</label>
+ <input id="input2" type="checkbox"><label id="label2" for="input2">Blue</label>
+ </form>
+ <label id="label3">First name: <input id="input3" value="Joe"></label>
+ <label id="label4">Points:
+ <input id="input4" type="range" name="points" min="1" max="10" value="3">
+ </label>
+ <label for="input5">Boring label</label><input id="input5" type="checkbox" checked></input>
+ <label for="password">Secret Password</label><input id="password" type="password"></input>
+ <label for="radio_unselected">any old radio button</label><input id="radio_unselected" type="radio"></input>
+ <label for="radio_selected">a unique radio button</label><input id="radio_selected" type="radio" checked></input>
+ <input id="date" type="date" value="2011-09-29" />
+ <input id="email" type="email" value="test@example.com" />
+ <input id="search" type="search" value="This is a search" />
+ <input id="tel" type="tel" value="555-5555" />
+ <input id="url" type="url" value="http://example.com" />
+ <input id="textInput" type="text" value="This is text." />
+ <label>Points: <input id="range" type="range" name="points" min="1" max="10" value="3"></label>
+ <div id="togglebutton_notpressed" aria-pressed="false" role="button" tabindex="-1">I am not pressed</div>
+ <div id="togglebutton_pressed" aria-pressed="true" role="button" tabindex="-1">I am pressed!</div>
+ <ul role="listbox" style="list-style-type: none;">
+ <li role="option" id="listbox-option">Search suggestion</li>
+ <li role="option" id="listbox-option2">
+ <label aria-hidden="true">
+ <input type="checkbox" />
+ </label>
+ 555-12345
+ </li>
+ </ul>
+ <section id="grid" role="grid">
+ <ol role="row">
+ <li role="presentation"></li>
+ <li id="columnheader" role="columnheader" aria-label="Sunday">S</li>
+ <li role="columnheader">M</li>
+ </ol>
+ <ol role="row">
+ <li id="rowheader" role="rowheader" aria-label="Week 1">1</li>
+ <li id="gridcell1" role="gridcell"><span>3</span><div></div></li>
+ <li id="gridcell2" role="gridcell"><span>4</span><div>7</div></li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader">2</li>
+ <li id="gridcell3" aria-selected="true" role="gridcell">5</li>
+ <li role="gridcell">6</li>
+ </ol>
+ </section>
+ <select id="frequency">
+ <option id="selected-combobox-option" value="15">15 min</option>
+ <option id="combobox-option" value="30">30 min</option>
+ <option value="null">Manual</option>
+ </select>
+ <select id="labelled-combobox" aria-label="Intervals">
+ <option value="15">Every 15 min</option>
+ <option value="30">Every 30 min</option>
+ <option value="null" selected>Never</option>
+ </select>
+ <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+ <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status">I should be ignored</div>
+ <span id="switch-1" role="switch" aria-label="Simple switch" aria-checked="true"></span>
+ <span id="switch-2" role="switch" aria-label="Another switch" aria-checked="false"></span>
+ </div>
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_output_mathml.html b/accessible/tests/mochitest/jsat/test_output_mathml.html
new file mode 100644
index 000000000..3fe4779b2
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_output_mathml.html
@@ -0,0 +1,313 @@
+<html>
+<head>
+ <title>[AccessFu] MathML Accessibility Support</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "math-1",
+ expectedUtterance: [
+ [{"string":"open-fence"},"(","x",",","y",{"string":"close-fence"},")"],
+ ["(",{"string":"open-fence"},"x",",","y",")",{"string":"close-fence"}]
+ ],
+ expectedBraille: [
+ [{"string":"open-fenceAbbr"},"(","x",",","y",{"string":"close-fenceAbbr"},")"],
+ ["(",{"string":"open-fenceAbbr"},"x",",","y",")",{"string":"close-fenceAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mfrac-1",
+ expectedUtterance: [
+ [{"string":"mathmlfraction"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
+ ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfraction"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlfractionAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
+ ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mfrac-2",
+ expectedUtterance: [
+ [{"string":"mathmlfractionwithoutbar"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
+ ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfractionwithoutbar"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlfractionwithoutbarAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
+ ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionwithoutbarAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msub-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msup-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"superscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"superscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"superscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msubsup-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mmultiscripts-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c",{"string":"superscript"},"d",{"string":"presubscript"},"e",{"string":"presubscript"},"f",{"string":"presuperscript"},"g"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},"d",{"string":"superscript"},"e",{"string":"presubscript"},"f",{"string":"presubscript"},"g",{"string":"presuperscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"presubscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presuperscriptAbbr"},"g"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"superscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presubscriptAbbr"},"g",{"string":"presuperscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "munder-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"underscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mover-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"overscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"overscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"overscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "munderover-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b",{"string":"overscript"},"c"],
+ ["a",{"string":"base"},"b",{"string":"underscript"},"c",{"string":"overscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b",{"string":"overscriptAbbr"},"c"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},"c",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mroot-1",
+ expectedUtterance: [
+ [{"string":"mathmlroot"},{"string":"base"},"a",{"string":"root-index"},"b"],
+ ["a",{"string":"base"},"b",{"string":"root-index"},{"string":"mathmlroot"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlrootAbbr"},{"string":"baseAbbr"},"a",{"string":"root-indexAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"root-indexAbbr"},{"string":"mathmlrootAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mtable-1",
+ expectedUtterance: [
+ [{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2},{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"a",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"d",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},"f"],
+ ["a",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"d",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"f",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2},{"string":"cellInfoAbbr","args":[1,1]},"a",{"string":"cellInfoAbbr","args":[2,1]},"b",{"string":"cellInfoAbbr","args":[3,1]},"c",{"string":"cellInfoAbbr","args":[1,2]},"d",{"string":"cellInfoAbbr","args":[2,2]},"e",{"string":"cellInfoAbbr","args":[3,2]},"f"],
+ ["a",{"string":"cellInfoAbbr","args":[1,1]},"b",{"string":"cellInfoAbbr","args":[2,1]},"c",{"string":"cellInfoAbbr","args":[3,1]},"d",{"string":"cellInfoAbbr","args":[1,2]},"e",{"string":"cellInfoAbbr","args":[2,2]},"f",{"string":"cellInfoAbbr","args":[3,2]},{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-1",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-longdiv"},"a"],
+ ["a",{"string":"notation-longdiv"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-longdivAbbr"},"a"],
+ ["a",{"string":"notation-longdivAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-2",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-circle"},"a"],
+ ["a",{"string":"notation-circle"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-circleAbbr"},"a"],
+ ["a",{"string":"notation-circleAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-3",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},"a"],
+ ["a",{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},"a"],
+ ["a",{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }];
+
+ // Test all possible utterance order preference values.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1163374"
+ title="[AccessFu] MathML Accessibility Support">
+ Mozilla Bug 1163374
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+
+ <math id="math-1"><mo>(</mo><mi>x</mi><mo>,</mo><mi>y</mi><mo>)</mo></math>
+
+ <math>
+ <mfrac id="mfrac-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mfrac>
+ </math>
+
+ <math>
+ <mfrac id="mfrac-2" linethickness="0px">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mfrac>
+ </math>
+
+ <math>
+ <msub id="msub-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </msub>
+ </math>
+ <math>
+ <msup id="msup-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </msup>
+ </math>
+ <math>
+ <msubsup id="msubsup-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ </msubsup>
+ </math>
+ <math>
+ <mmultiscripts id="mmultiscripts-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ <none/>
+ <mi>d</mi>
+ <mprescripts/>
+ <mi>e</mi>
+ <none/>
+ <mi>f</mi>
+ <mi>g</mi>
+ </mmultiscripts>
+ </math>
+
+ <math>
+ <munder id="munder-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </munder>
+ </math>
+ <math>
+ <mover id="mover-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mover>
+ </math>
+ <math>
+ <munderover id="munderover-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ </munderover>
+ </math>
+
+ <math>
+ <mroot id="mroot-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mroot>
+ </math>
+
+ <math>
+ <mtable id="mtable-1">
+ <mtr>
+ <mtd><mi>a</mi></mtd>
+ <mtd><mi>b</mi></mtd>
+ <mtd><mi>c</mi></mtd>
+ </mtr>
+ <mtr>
+ <mtd><mi>d</mi></mtd>
+ <mtd><mi>e</mi></mtd>
+ <mtd><mi>f</mi></mtd>
+ </mtr>
+ </mtable>
+ </math>
+
+ <math>
+ <menclose id="menclose-1"><mi>a</mi></menclose>
+ </math>
+ <math>
+ <menclose id="menclose-2" notation="circle"><mi>a</mi></menclose>
+ </math>
+ <math>
+ <menclose id="menclose-3" notation="left top bottom"><mi>a</mi></menclose>
+ </math>
+
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_pointer_relay.html b/accessible/tests/mochitest/jsat/test_pointer_relay.html
new file mode 100644
index 000000000..cb58fe73b
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_pointer_relay.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>AccessFu tests for pointer relay.</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+ <script type="application/javascript" src="./jsatcommon.js"></script>
+ <script type="application/javascript" src="./dom_helper.js"></script>
+ <script type="application/javascript">
+
+ Components.utils.import(
+ "resource://gre/modules/accessibility/PointerAdapter.jsm");
+
+ var tests = [
+ {
+ type: 'touchstart', target: [{base: 'button'}],
+ expected: {type: 'pointerdown', length: 1}
+ },
+ {
+ type: 'touchmove', target: [{base: 'button'}],
+ expected: {type: 'pointermove', length: 1}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'}],
+ expected: {type: 'pointerup'}
+ },
+ {
+ type: 'touchstart', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3}],
+ expected: {type: 'pointerdown', length: 2}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3}],
+ expected: {type: 'pointerup'}
+ },
+ {
+ type: 'touchstart', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3},
+ {base: 'button', x: 0.5, y: -0.3}],
+ expected: {type: 'pointerdown', length: 3}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3},
+ {base: 'button', x: 0.5, y: -0.3}],
+ expected: {type: 'pointerup'}
+ }
+ ];
+
+ function makeTestFromSpec(test) {
+ return function runTest() {
+ PointerRelay.start(function onPointerEvent(aDetail) {
+ is(aDetail.type, test.expected.type,
+ 'mozAccessFuPointerEvent is correct.');
+ if (test.expected.length) {
+ is(aDetail.points.length, test.expected.length,
+ 'mozAccessFuPointerEvent points length is correct.');
+ }
+ PointerRelay.stop();
+ AccessFuTest.nextTest();
+ });
+ eventMap[test.type](test.target, test.type);
+ };
+ }
+
+ function doTest() {
+ tests.forEach(function addTest(test) {
+ AccessFuTest.addFunc(makeTestFromSpec(test));
+ });
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=976082"
+ title="[AccessFu] Provide tests for pointer relay.">
+ Mozilla Bug 981015
+ </a>
+ <div>
+ <button id="button">I am a button</button>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_quicknav_modes.html b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
new file mode 100644
index 000000000..f99b64a84
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
@@ -0,0 +1,107 @@
+<html>
+
+<head>
+ <title>AccessFu test for enabling</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function prefStart() {
+ // Start AccessFu via pref.
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ }
+
+ function nextMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.next();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function prevMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.previous();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function setMode(aModeIndex, aExpectedMode) {
+ return function() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [['accessibility.accessfu.quicknav_index', aModeIndex]]},
+ function() {
+ _expectMode(aExpectedMode, AccessFuTest.nextTest);
+ });
+ }
+ }
+
+ function reconfigureModes() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [['accessibility.accessfu.quicknav_modes', 'Landmark,Button,Entry,Graphic']]},
+ function() {
+ // When the modes are reconfigured, the current mode should
+ // be set to the first in the new list.
+ _expectMode('Landmark', AccessFuTest.nextTest);
+ });
+ }
+
+ function _expectMode(aExpectedMode, aCallback) {
+ if (AccessFu.Input.quickNavMode.current === aExpectedMode) {
+ ok(true, 'correct mode');
+ aCallback();
+ } else {
+ AccessFuTest.once_log('Quicknav mode: ' + aExpectedMode, function() {
+ ok(true, 'correct mode');
+ aCallback();
+ });
+ }
+ }
+
+ // Listen for initial 'EventManager.start' and disable AccessFu.
+ function prefStop() {
+ ok(AccessFu._enabled, "AccessFu was started via preference.");
+ AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ }
+
+ function doTest() {
+ AccessFuTest.addFunc(prefStart);
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(nextMode('Heading', 'FormElement'));
+ AccessFuTest.addFunc(nextMode('FormElement', 'Link'));
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(prevMode('Heading', 'Link'));
+ AccessFuTest.addFunc(prevMode('Link', 'FormElement'));
+ AccessFuTest.addFunc(setMode(1, 'Heading'));
+ AccessFuTest.addFunc(reconfigureModes);
+ AccessFuTest.addFunc(prefStop);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests([ // Will call SimpleTest.finish();
+ ['accessibility.accessfu.quicknav_modes', 'Link,Heading,FormElement']]);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+ title="[AccessFu] Add mochitest for enabling">
+ Mozilla Bug 811307
+ </a>
+</body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/test_tables.html b/accessible/tests/mochitest/jsat/test_tables.html
new file mode 100644
index 000000000..aa7f482e9
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_tables.html
@@ -0,0 +1,579 @@
+<html>
+<head>
+ <title>[AccessFu] Improve reading of table semantics</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1", "cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"], ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "cell1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell2",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"], ["cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2", "col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[{"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"], ["cell1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2", "col1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 2]},
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table3",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table4",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 4},
+ {"string": "tblRowInfo", "count": 3},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "col3",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "row1",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell1",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansRows", "args": [2]}, "row1", "cell2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [3]}, "col1", "row2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [3]}, "col3", "row2", "cell4"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "row1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell2",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansRows", "args": [2]}, "row1", "row2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [3]}, "col1", "cell3",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell4",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [3]}, "col3", "row2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 4},
+ {"string": "tblRowInfo", "count": 3}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 4},
+ {"string": "tblRowInfoAbbr", "count": 3},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [3, 1]}, "col3",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1",
+ {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell1",
+ {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "cell2",
+ {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "row2",
+ {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3",
+ {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2", "cell4"],
+ ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col3",
+ {"string": "cellInfoAbbr", "args": [3, 1]}, "row1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell2",
+ {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "row2",
+ {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "cell3",
+ {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell4",
+ {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 4},
+ {"string": "tblRowInfoAbbr", "count": 3}]]
+ }, {
+ accOrElmOrID: "table5",
+ expectedUtterance: [["Row1", "Row2"], ["Row1", "Row2"]],
+ expectedBraille: [["Row1", "Row2"], ["Row1", "Row2"]]
+ }, {
+ // Test pivot to table1_th1 from table1.
+ accOrElmOrID: "table1_th1",
+ oldAccOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}]],
+ expectedBraille: [[
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"], ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}]]
+ }, {
+ // Test pivot to table1_td2 from table1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table1_td2 from table1_th1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1_th1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table1_td2 from table1_td1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1_td1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table2_cell_1 from table2.
+ accOrElmOrID: "table2_cell_1",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+ ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
+ }, {
+ // Test pivot to table2_cell_2 from table2.
+ accOrElmOrID: "table2_cell_2",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [[
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2"]]
+ }, {
+ // Test pivot to table2_cell_1 from table2_cell_2.
+ accOrElmOrID: "table2_cell_1",
+ oldAccOrElmOrID: "table2_cell_2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+ {"string": "columnInfo", "args": [1]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+ ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
+ }, {
+ // Test pivot to table3_cell from table2.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table3_cell from table2_cell_1.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table2_cell_1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table3_cell from table3_ch.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table3_ch",
+ expectedUtterance: [[
+ {"string": "rowInfo", "args": [2]}, "bla"], ["bla",
+ {"string": "rowInfo", "args": [2]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}]]
+ }, {
+ // Test pivot to table3_cell from table1_td1.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table1_td1",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table4_ch_3 from table4.
+ accOrElmOrID: "table4_ch_3",
+ oldAccOrElmOrID: "table4",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "col3"], ["col3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [3, 1]}, "col3"],
+ ["col3", {"string": "cellInfoAbbr", "args": [3, 1]}]]
+ }, {
+ // Test pivot to table4_rh_1 from table4_ch_3.
+ accOrElmOrID: "table4_rh_1",
+ oldAccOrElmOrID: "table4_ch_3",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "row1"], ["row1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1"],
+ ["row1", {"string": "cellInfoAbbr", "args": [1, 2]}, "col1"]]
+ }, {
+ // Test pivot to table4_cell_3 from table4_rh_1.
+ accOrElmOrID: "table4_cell_3",
+ oldAccOrElmOrID: "table4_rh_1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [4]},
+ {"string": "spansRows", "args": [2]}, "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "spansRows", "args": [2]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [4, 2]}, "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [4, 2]}]]
+ }, {
+ // Test pivot to table4_cell_5 from table4_cell_3.
+ accOrElmOrID: "table4_cell_5",
+ oldAccOrElmOrID: "table4_cell_3",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3"],
+ ["cell3",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3"],
+ ["cell3", {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2"]]
+ }];
+
+ // Test outputs (utterance and braille) for tables including their
+ // headers and cells.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=830748"
+ title="[AccessFu] Improve reading of table semantics">
+ Mozilla Bug 830748
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <table id="table1">
+ <thead>
+ <tr>
+ <th id="table1_th1">col1</th>
+ <th>col2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table1_td1">cell1</td>
+ <td id="table1_td2">cell2</td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="table2" border="1">
+ <tr>
+ <td id="table2_cell_1" headers="table2_ch_1">cell1</td>
+ <td id="table2_cell_2" headers="table2_ch_2">
+ <table id="table3">
+ <thead>
+ <tr>
+ <th id="table3_ch">colheader</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table3_cell">bla</td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="table2_ch_1" scope="col">col1</td>
+ <td id="table2_ch_2" scope="col">col2</td>
+ </tr>
+ </table>
+ <table id="table4" border="1">
+ <thead>
+ <tr>
+ <th id="table4_ch_1">col1</th>
+ <th id="table4_ch_2">col2</th>
+ <td id="table4_ch_3" scope="col">col3</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="table4_rh_1" colspan="2">row1</th>
+ <td id="table4_cell_2">cell1</td>
+ <td id="table4_cell_3" rowspan="2">cell2</td>
+ </tr>
+ <tr>
+ <td id="table4_rh_2" scope="row">row2</td>
+ <td id="table4_cell_5">cell3</td>
+ <td id="table4_cell_6">cell4</td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="table5">
+ <tr><td>Row1</td></tr>
+ <tr><td>Row2</td></tr>
+ </table>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/test_traversal.html b/accessible/tests/mochitest/jsat/test_traversal.html
new file mode 100644
index 000000000..533e9d89f
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu TraversalRules</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ Components.utils.import("resource://gre/modules/accessibility/Traversal.jsm");
+ var gBrowserWnd = null;
+ var gQueue = null;
+
+ function doTest()
+ {
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+
+ gQueue = new eventQueue();
+
+ gQueue.onFinish = function onFinish()
+ {
+ closeBrowserWindow();
+ }
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Heading, null,
+ ['heading-1', 'heading-2', 'heading-3', 'heading-5']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Entry, null,
+ ['input-1-1', 'label-1-2', 'input-1-3',
+ 'input-1-4', 'input-1-5']);
+
+ // move back an element to hit all the form elements, because the VC is
+ // currently at the first input element
+ gQueue.push(new setVCPosInvoker(docAcc, "movePrevious",
+ TraversalRules.Heading, "heading-1"));
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.FormElement, null,
+ ['input-1-1', 'label-1-2', 'button-1-1',
+ 'radio-1-1', 'radio-1-2', 'input-1-3',
+ 'input-1-4', 'button-1-2', 'checkbox-1-1',
+ 'select-1-1', 'select-1-2', 'checkbox-1-2',
+ 'select-1-3', 'input-1-5', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'checkbox-1-5', 'switch-1']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Button, null,
+ ['button-1-1', 'button-1-2', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.RadioButton, null,
+ ['radio-1-1', 'radio-1-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Checkbox, null,
+ ['checkbox-1-1', 'checkbox-1-2', 'checkbox-1-5',
+ 'switch-1']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Combobox, null,
+ ['select-1-1', 'select-1-2', 'select-1-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.List, null,
+ ['list-1', 'list-2', 'list-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.ListItem, null,
+ ['listitem-1-1', 'listitem-2-1', 'listitem-2-2',
+ 'listitem-3-1', 'listitem-3-2', 'listitem-3-3',
+ 'listitem-3-4', 'listitem-3-5', 'listitem-3-6',
+ 'listitem-2-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Graphic, null,
+ ['image-2', 'image-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Link, null,
+ ['link-0', 'link-1', 'link-2', 'link-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Anchor, null,
+ ['anchor-1', 'anchor-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Separator, null,
+ ['separator-1', 'separator-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Table, null,
+ ['table-1', 'grid', 'table-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Simple, null,
+ ['heading-1', 'Name:', 'input-1-1', 'label-1-2',
+ 'button-1-1', 'Radios are old: ', 'radio-1-1',
+ 'Radios are new: ', 'radio-1-2', 'Password:',
+ 'input-1-3', 'Unlucky number:', 'input-1-4',
+ 'button-1-2', 'Check me: ', 'checkbox-1-1',
+ 'select-1-1', 'Value 1', 'Value 2', 'Value 3',
+ 'Check me too: ', 'checkbox-1-2', 'But not me: ',
+ 'Or me! ', 'Value 1', 'Value 2', 'Value 3',
+ 'Electronic mailing address:', 'input-1-5',
+ 'button-1-3', 'heading-2', 'heading-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'Programming Language',
+ 'A esoteric weapon wielded by only the most ' +
+ 'formidable warriors, for its unrelenting strict' +
+ ' power is unfathomable.',
+ '• Lists of Programming Languages', 'Lisp ',
+ '1. Scheme', '2. Racket', '3. Clojure',
+ '4. Standard Lisp', 'link-0', ' Lisp',
+ 'checkbox-1-5', ' LeLisp', '• JavaScript',
+ 'heading-5', 'image-2', 'image-3',
+ 'Not actually an image', 'link-1', 'anchor-1',
+ 'link-2', 'anchor-2', 'link-3', '3', '1', '4',
+ '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
+ '5 8', 'gridcell4', 'Just an innocuous separator',
+ 'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
+ 'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
+ 'switch-1', 'This is a MathML formula ', 'math-1',
+ 'with some text after.']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,
+ ['header-1', 'main-1', 'footer-1']);
+
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Control, null,
+ ['input-1-1', 'label-1-2', 'button-1-1',
+ 'radio-1-1', 'radio-1-2', 'input-1-3',
+ 'input-1-4', 'button-1-2', 'checkbox-1-1',
+ 'select-1-1', 'select-1-2', 'checkbox-1-2',
+ 'select-1-3', 'input-1-5', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'link-0', 'checkbox-1-5',
+ 'link-1', 'link-2', 'link-3', 'switch-1']);
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_traversal.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for AccessFu TraversalRules"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=933808">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_traversal_helper.html b/accessible/tests/mochitest/jsat/test_traversal_helper.html
new file mode 100644
index 000000000..f6a347ed0
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_traversal_helper.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu TraversalRules</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ Components.utils.import("resource://gre/modules/accessibility/Traversal.jsm");
+
+ var vc;
+
+ function accessibleIs(aAccessible, aExpected, aMessage) {
+ if (!aAccessible && aAccessible == aExpected) {
+ ok(true, "Accessible is null. " + aMessage);
+ } else {
+ ok(aAccessible.DOMNode.id == aExpected || aAccessible.name == aExpected,
+ "expected '" + aExpected + "', got " + prettyName(vc.position) +
+ ". " + aMessage);
+ }
+ }
+
+ function walkSequence(aMethod, aRule, aExpectedSequence) {
+ for (var expected of aExpectedSequence) {
+ ok(TraversalHelper.move(vc, aMethod, aRule),
+ "successfully did " + aMethod + " with " + aRule);
+ accessibleIs(vc.position, expected, "landed on correct accessible");
+ }
+ }
+
+ function testTraversalHelper(aRule, aExpectedSequence) {
+ vc.position = null;
+
+ walkSequence('moveNext', aRule, aExpectedSequence);
+
+ ok(!TraversalHelper.move(vc, 'moveNext', aRule), "reached end");
+
+ TraversalHelper.move(vc, 'moveLast', 'Simple');
+
+ walkSequence('movePrevious', aRule,
+ Array.from(aExpectedSequence).reverse());
+
+ ok(!TraversalHelper.move(vc, 'movePrevious', aRule), "reached start");
+
+ vc.position = null;
+
+ ok(TraversalHelper.move(vc, 'moveFirst', aRule), "moveFirst");
+
+ accessibleIs(vc.position, aExpectedSequence[0],
+ "moveFirst to correct accessible");
+
+ ok(TraversalHelper.move(vc, 'moveLast', aRule), "moveLast");
+
+ accessibleIs(vc.position, aExpectedSequence[aExpectedSequence.length - 1],
+ "moveLast to correct accessible");
+ }
+
+
+ function doTest()
+ {
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+ vc = docAcc.virtualCursor;
+
+ testTraversalHelper('Landmark',
+ ['heading-1', 'heading-2', 'statusbar-1']);
+
+ testTraversalHelper('List',
+ ['Programming Language', 'listitem-2-1', 'listitem-3-1']);
+
+ testTraversalHelper('Section',
+ ['heading-1', 'heading-2', 'heading-3',
+ 'heading-5', 'link-1', 'statusbar-1']);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_traversal.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for AccessFu TraversalRules"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=xxx">Mozilla Bug xxx</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/layout.js b/accessible/tests/mochitest/layout.js
new file mode 100644
index 000000000..e1fb14670
--- /dev/null
+++ b/accessible/tests/mochitest/layout.js
@@ -0,0 +1,258 @@
+/**
+ * Tests if the given child and grand child accessibles at the given point are
+ * expected.
+ *
+ * @param aID [in] accessible identifier
+ * @param aX [in] x coordinate of the point relative accessible
+ * @param aY [in] y coordinate of the point relative accessible
+ * @param aChildID [in] expected child accessible
+ * @param aGrandChildID [in] expected child accessible
+ */
+function testChildAtPoint(aID, aX, aY, aChildID, aGrandChildID)
+{
+ var child = getChildAtPoint(aID, aX, aY, false);
+ var expectedChild = getAccessible(aChildID);
+
+ var msg = "Wrong direct child accessible at the point (" + aX + ", " + aY +
+ ") of " + prettyName(aID);
+ isObject(child, expectedChild, msg);
+
+ var grandChild = getChildAtPoint(aID, aX, aY, true);
+ var expectedGrandChild = getAccessible(aGrandChildID);
+
+ msg = "Wrong deepest child accessible at the point (" + aX + ", " + aY +
+ ") of " + prettyName(aID);
+ isObject(grandChild, expectedGrandChild, msg);
+}
+
+/**
+ * Test if getChildAtPoint returns the given child and grand child accessibles
+ * at coordinates of child accessible (direct and deep hit test).
+ */
+function hitTest(aContainerID, aChildID, aGrandChildID)
+{
+ var container = getAccessible(aContainerID);
+ var child = getAccessible(aChildID);
+ var grandChild = getAccessible(aGrandChildID);
+
+ var [x, y] = getBoundsForDOMElm(child);
+
+ var actualChild = container.getChildAtPoint(x + 1, y + 1);
+ isObject(actualChild, child,
+ "Wrong direct child of " + prettyName(aContainerID));
+
+ var actualGrandChild = container.getDeepestChildAtPoint(x + 1, y + 1);
+ isObject(actualGrandChild, grandChild,
+ "Wrong deepest child of " + prettyName(aContainerID));
+}
+
+/**
+ * Test if getOffsetAtPoint returns the given text offset at given coordinates.
+ */
+function testOffsetAtPoint(aHyperTextID, aX, aY, aCoordType, aExpectedOffset)
+{
+ var hyperText = getAccessible(aHyperTextID, [nsIAccessibleText]);
+ var offset = hyperText.getOffsetAtPoint(aX, aY, aCoordType);
+ is(offset, aExpectedOffset,
+ "Wrong offset at given point (" + aX + ", " + aY + ") for " +
+ prettyName(aHyperTextID));
+}
+
+/**
+ * Zoom the given document.
+ */
+function zoomDocument(aDocument, aZoom)
+{
+ var docShell = aDocument.defaultView.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIWebNavigation).
+ QueryInterface(Components.interfaces.nsIDocShell);
+ var docViewer = docShell.contentViewer;
+
+ docViewer.fullZoom = aZoom;
+}
+
+/**
+ * Return child accessible at the given point.
+ *
+ * @param aIdentifier [in] accessible identifier
+ * @param aX [in] x coordinate of the point relative accessible
+ * @param aY [in] y coordinate of the point relative accessible
+ * @param aFindDeepestChild [in] points whether deepest or nearest child should
+ * be returned
+ * @return the child accessible at the given point
+ */
+function getChildAtPoint(aIdentifier, aX, aY, aFindDeepestChild)
+{
+ var acc = getAccessible(aIdentifier);
+ if (!acc)
+ return;
+
+ var [screenX, screenY] = getBoundsForDOMElm(acc.DOMNode);
+
+ var x = screenX + aX;
+ var y = screenY + aY;
+
+ try {
+ if (aFindDeepestChild)
+ return acc.getDeepestChildAtPoint(x, y);
+ return acc.getChildAtPoint(x, y);
+ } catch (e) { }
+
+ return null;
+}
+
+/**
+ * Test the accessible position.
+ */
+function testPos(aID, aPoint)
+{
+ var [expectedX, expectedY] =
+ (aPoint != undefined) ? aPoint : getBoundsForDOMElm(aID);
+
+ var [x, y] = getBounds(aID);
+ is(x, expectedX, "Wrong x coordinate of " + prettyName(aID));
+ is(y, expectedY, "Wrong y coordinate of " + prettyName(aID));
+}
+
+/**
+ * Test the accessible boundaries.
+ */
+function testBounds(aID, aRect)
+{
+ var [expectedX, expectedY, expectedWidth, expectedHeight] =
+ (aRect != undefined) ? aRect : getBoundsForDOMElm(aID);
+
+ var [x, y, width, height] = getBounds(aID);
+ is(x, expectedX, "Wrong x coordinate of " + prettyName(aID));
+ is(y, expectedY, "Wrong y coordinate of " + prettyName(aID));
+ is(width, expectedWidth, "Wrong width of " + prettyName(aID));
+ is(height, expectedHeight, "Wrong height of " + prettyName(aID));
+}
+
+/**
+ * Test text position at the given offset.
+ */
+function testTextPos(aID, aOffset, aPoint, aCoordOrigin)
+{
+ var [expectedX, expectedY] = aPoint;
+
+ var xObj = {}, yObj = {};
+ var hyperText = getAccessible(aID, [nsIAccessibleText]);
+ hyperText.getCharacterExtents(aOffset, xObj, yObj, {}, {}, aCoordOrigin);
+ is(xObj.value, expectedX,
+ "Wrong x coordinate at offset " + aOffset + " for " + prettyName(aID));
+ ok(yObj.value - expectedY < 2 && expectedY - yObj.value < 2,
+ "Wrong y coordinate at offset " + aOffset + " for " + prettyName(aID) +
+ " - got " + yObj.value + ", expected " + expectedY +
+ "The difference doesn't exceed 1.");
+}
+
+/**
+ * Test text bounds that is enclosed betwene the given offsets.
+ */
+function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin)
+{
+ var [expectedX, expectedY, expectedWidth, expectedHeight] = aRect;
+
+ var xObj = {}, yObj = {}, widthObj = {}, heightObj = {};
+ var hyperText = getAccessible(aID, [nsIAccessibleText]);
+ hyperText.getRangeExtents(aStartOffset, aEndOffset,
+ xObj, yObj, widthObj, heightObj, aCoordOrigin);
+ is(xObj.value, expectedX,
+ "Wrong x coordinate of text between offsets (" + aStartOffset + ", " +
+ aEndOffset + ") for " + prettyName(aID));
+ is(yObj.value, expectedY,
+ "Wrong y coordinate of text between offsets (" + aStartOffset + ", " +
+ aEndOffset + ") for " + prettyName(aID));
+
+ var msg = "Wrong width of text between offsets (" + aStartOffset + ", " +
+ aEndOffset + ") for " + prettyName(aID);
+ if (widthObj.value == expectedWidth)
+ ok(true, msg);
+ else
+ todo(false, msg); // fails on some windows machines
+
+ is(heightObj.value, expectedHeight,
+ "Wrong height of text between offsets (" + aStartOffset + ", " +
+ aEndOffset + ") for " + prettyName(aID));
+}
+
+/**
+ * Return the accessible coordinates relative to the screen in device pixels.
+ */
+function getPos(aID)
+{
+ var accessible = getAccessible(aID);
+ var x = {}, y = {};
+ accessible.getBounds(x, y, {}, {});
+ return [x.value, y.value];
+}
+
+/**
+ * Return the accessible coordinates and size relative to the screen in device
+ * pixels.
+ */
+function getBounds(aID)
+{
+ var accessible = getAccessible(aID);
+ var x = {}, y = {}, width = {}, height = {};
+ accessible.getBounds(x, y, width, height);
+ return [x.value, y.value, width.value, height.value];
+}
+
+/**
+ * Return DOM node coordinates relative the screen and its size in device
+ * pixels.
+ */
+function getBoundsForDOMElm(aID)
+{
+ var x = 0, y = 0, width = 0, height = 0;
+
+ var elm = getNode(aID);
+ if (elm.localName == "area") {
+ var mapName = elm.parentNode.getAttribute("name");
+ var selector = "[usemap='#" + mapName + "']";
+ var img = elm.ownerDocument.querySelector(selector);
+
+ var areaCoords = elm.coords.split(",");
+ var areaX = parseInt(areaCoords[0]);
+ var areaY = parseInt(areaCoords[1]);
+ var areaWidth = parseInt(areaCoords[2]) - areaX;
+ var areaHeight = parseInt(areaCoords[3]) - areaY;
+
+ var rect = img.getBoundingClientRect();
+ x = rect.left + areaX;
+ y = rect.top + areaY;
+ width = areaWidth;
+ height = areaHeight;
+ }
+ else {
+ var rect = elm.getBoundingClientRect();
+ x = rect.left;
+ y = rect.top;
+ width = rect.width;
+ height = rect.height;
+ }
+
+ var elmWindow = elm.ownerDocument.defaultView;
+ return CSSToDevicePixels(elmWindow,
+ x + elmWindow.mozInnerScreenX,
+ y + elmWindow.mozInnerScreenY,
+ width,
+ height);
+}
+
+function CSSToDevicePixels(aWindow, aX, aY, aWidth, aHeight)
+{
+ var winUtil = aWindow.
+ QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+ var 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(aX * ratio), Math.round(aY * ratio),
+ Math.round(aWidth * ratio), Math.round(aHeight * ratio) ];
+}
diff --git a/accessible/tests/mochitest/letters.gif b/accessible/tests/mochitest/letters.gif
new file mode 100644
index 000000000..299b91784
--- /dev/null
+++ b/accessible/tests/mochitest/letters.gif
Binary files differ
diff --git a/accessible/tests/mochitest/longdesc_src.html b/accessible/tests/mochitest/longdesc_src.html
new file mode 100644
index 000000000..37248795d
--- /dev/null
+++ b/accessible/tests/mochitest/longdesc_src.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>Mozilla logo</title>
+</head>
+<body>
+<p>This file would contain a longer description of the Mozilla logo, if I knew what it looked like.</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/moz.build b/accessible/tests/mochitest/moz.build
new file mode 100644
index 000000000..4fb277037
--- /dev/null
+++ b/accessible/tests/mochitest/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+A11Y_MANIFESTS += [
+ 'a11y.ini',
+ 'actions/a11y.ini',
+ 'aom/a11y.ini',
+ 'attributes/a11y.ini',
+ 'bounds/a11y.ini',
+ 'editabletext/a11y.ini',
+ 'elm/a11y.ini',
+ 'events/a11y.ini',
+ 'focus/a11y.ini',
+ 'hittest/a11y.ini',
+ 'hyperlink/a11y.ini',
+ 'hypertext/a11y.ini',
+ 'jsat/a11y.ini',
+ 'name/a11y.ini',
+ 'pivot/a11y.ini',
+ 'relations/a11y.ini',
+ 'role/a11y.ini',
+ 'scroll/a11y.ini',
+ 'selectable/a11y.ini',
+ 'states/a11y.ini',
+ 'table/a11y.ini',
+ 'text/a11y.ini',
+ 'textattrs/a11y.ini',
+ 'textcaret/a11y.ini',
+ 'textrange/a11y.ini',
+ 'textselection/a11y.ini',
+ 'tree/a11y.ini',
+ 'treeupdate/a11y.ini',
+ 'value/a11y.ini',
+]
diff --git a/accessible/tests/mochitest/moz.png b/accessible/tests/mochitest/moz.png
new file mode 100644
index 000000000..743292dc6
--- /dev/null
+++ b/accessible/tests/mochitest/moz.png
Binary files differ
diff --git a/accessible/tests/mochitest/name.js b/accessible/tests/mochitest/name.js
new file mode 100644
index 000000000..8d8290905
--- /dev/null
+++ b/accessible/tests/mochitest/name.js
@@ -0,0 +1,33 @@
+/**
+ * Test accessible name for the given accessible identifier.
+ */
+function testName(aAccOrElmOrID, aName, aMsg, aTodo)
+{
+ var msg = aMsg ? aMsg : "";
+
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return;
+
+ var func = aTodo ? todo_is : is;
+ var txtID = prettyName(aAccOrElmOrID);
+ try {
+ func(acc.name, aName, msg + "Wrong name of the accessible for " + txtID);
+ } catch (e) {
+ ok(false, msg + "Can't get name of the accessible for " + txtID);
+ }
+ return acc;
+}
+
+/**
+ * Test accessible description for the given accessible.
+ */
+function testDescr(aAccOrElmOrID, aDescr)
+{
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return;
+
+ is(acc.description, aDescr,
+ "Wrong description for " + prettyName(aAccOrElmOrID));
+}
diff --git a/accessible/tests/mochitest/name/a11y.ini b/accessible/tests/mochitest/name/a11y.ini
new file mode 100644
index 000000000..4d743f1f5
--- /dev/null
+++ b/accessible/tests/mochitest/name/a11y.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+support-files =
+ general.css
+ general.xbl
+ markup.js
+ markuprules.xml
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+
+[test_browserui.xul]
+[test_counterstyle.html]
+[test_general.html]
+[test_general.xul]
+[test_link.html]
+[test_list.html]
+[test_markup.html]
+skip-if = (debug && os == 'win') # Bug 1296784
+[test_svg.html]
+[test_toolbaritem.xul]
+[test_tree.xul]
diff --git a/accessible/tests/mochitest/name/general.css b/accessible/tests/mochitest/name/general.css
new file mode 100644
index 000000000..5f750c4dc
--- /dev/null
+++ b/accessible/tests/mochitest/name/general.css
@@ -0,0 +1,11 @@
+box.first {
+ -moz-binding: url('general.xbl#first');
+}
+
+.second {
+ -moz-binding: url('general.xbl#second');
+}
+
+.third {
+ -moz-binding: url('general.xbl#third');
+}
diff --git a/accessible/tests/mochitest/name/general.xbl b/accessible/tests/mochitest/name/general.xbl
new file mode 100644
index 000000000..07489f5f4
--- /dev/null
+++ b/accessible/tests/mochitest/name/general.xbl
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+
+<bindings xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="first">
+ <content>
+ <xul:textbox anonid="labeled" class="bottom"/>
+ <xul:label control="labeled" value="Label"/>
+ <children/>
+ </content>
+ </binding>
+
+ <binding id="second">
+ <content>
+ <xul:box class="first">
+ <xul:label control="toplabeled" value="Top textbox"/>
+ <xul:textbox anonid="toplabeled" class="top"/>
+ </xul:box>
+ <children/>
+ </content>
+ </binding>
+
+ <binding id="third">
+ <content>
+ <xul:description anonid="label" value="It's a " />
+ <xul:description anonid="label2" value="cool button" />
+ <xul:button anonid="button" aria-labelledby="label label2"
+ value="button" />
+ </content>
+ </binding>
+</bindings>
diff --git a/accessible/tests/mochitest/name/markup.js b/accessible/tests/mochitest/name/markup.js
new file mode 100644
index 000000000..d3ecd8982
--- /dev/null
+++ b/accessible/tests/mochitest/name/markup.js
@@ -0,0 +1,382 @@
+////////////////////////////////////////////////////////////////////////////////
+// Name tests described by "markuprules.xml" file.
+
+var gNameRulesFileURL = "markuprules.xml";
+
+var gRuleDoc = null;
+
+// Debuggin stuff.
+var gDumpToConsole = false;
+
+/**
+ * Start name tests. Run through markup elements and test names for test
+ * element (see namerules.xml for details).
+ */
+function testNames()
+{
+ //enableLogging("tree,stack"); // debugging
+
+ var request = new XMLHttpRequest();
+ request.open("get", gNameRulesFileURL, false);
+ request.send();
+
+ gRuleDoc = request.responseXML;
+
+ var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup");
+ gTestIterator.iterateMarkups(markupElms);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private section.
+
+/**
+ * Helper class to interate through name tests.
+ */
+var gTestIterator =
+{
+ iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms)
+ {
+ this.markupElms = aMarkupElms;
+
+ this.iterateNext();
+ },
+
+ iterateRules: function gTestIterator_iterateRules(aElm, aContainer,
+ aRuleSetElm, aRuleElms,
+ aTestID)
+ {
+ this.ruleSetElm = aRuleSetElm;
+ this.ruleElms = aRuleElms;
+ this.elm = aElm;
+ this.container = aContainer;
+ this.testID = aTestID;
+
+ this.iterateNext();
+ },
+
+ iterateNext: function gTestIterator_iterateNext()
+ {
+ if (this.markupIdx == -1) {
+ this.markupIdx++;
+ testNamesForMarkup(this.markupElms[this.markupIdx]);
+ return;
+ }
+
+ this.ruleIdx++;
+ if (this.ruleIdx == this.ruleElms.length) {
+ // When test is finished then name is empty and no explict-name.
+ var defaultName = this.ruleSetElm.hasAttribute("defaultName") ?
+ this.ruleSetElm.getAttribute("defaultName") : null;
+ testName(this.elm, defaultName,
+ "Default name test (" + gTestIterator.testID + "). ");
+ testAbsentAttrs(this.elm, {"explicit-name" : "true"});
+
+ this.markupIdx++;
+ if (this.markupIdx == this.markupElms.length) {
+ //disableLogging("tree"); // debugging
+ SimpleTest.finish();
+ return;
+ }
+
+ this.ruleIdx = -1;
+
+ if (gDumpToConsole) {
+ dump("\nPend next markup processing. Wait for reorder event on " +
+ prettyName(document) + "'\n");
+ }
+ waitForEvent(EVENT_REORDER, document, testNamesForMarkup,
+ null, this.markupElms[this.markupIdx]);
+
+ document.body.removeChild(this.container);
+ return;
+ }
+
+ testNameForRule(this.elm, this.ruleElms[this.ruleIdx]);
+ },
+
+ markupElms: null,
+ markupIdx: -1,
+ rulesetElm: null,
+ ruleElms: null,
+ ruleIdx: -1,
+ elm: null,
+ container: null,
+ testID: ""
+};
+
+/**
+ * Process every 'markup' element and test names for it. Used by testNames
+ * function.
+ */
+function testNamesForMarkup(aMarkupElm)
+{
+ if (gDumpToConsole)
+ dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n");
+
+ var div = document.createElement("div");
+ div.setAttribute("id", "test");
+
+ var child = aMarkupElm.firstChild;
+ while (child) {
+ var newChild = document.importNode(child, true);
+ div.appendChild(newChild);
+ child = child.nextSibling;
+ }
+
+ if (gDumpToConsole) {
+ dump("\nProcessing markup. Wait for reorder event on " +
+ prettyName(document) + "'\n");
+ }
+ waitForEvent(EVENT_REORDER, document, testNamesForMarkupRules,
+ null, aMarkupElm, div);
+
+ document.body.appendChild(div);
+}
+
+function testNamesForMarkupRules(aMarkupElm, aContainer)
+{
+ var testID = aMarkupElm.getAttribute("id");
+ if (gDumpToConsole)
+ dump("\nProcessing markup rules '" + testID + "'\n");
+
+ var serializer = new XMLSerializer();
+
+ var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
+ var elm = evaluateXPath(document, expr, htmlDocResolver)[0];
+
+ var ruleId = aMarkupElm.getAttribute("ruleset");
+ var ruleElm = gRuleDoc.querySelector("[id='" + ruleId + "']");
+ var ruleElms = getRuleElmsByRulesetId(ruleId);
+
+ var processMarkupRules =
+ gTestIterator.iterateRules.bind(gTestIterator, elm, aContainer,
+ ruleElm, ruleElms, testID);
+
+ // Images may be recreated after we append them into subtree. We need to wait
+ // in this case. If we are on profiling enabled build then stack tracing
+ // works and thus let's log instead. Note, that works if you enabled logging
+ // (refer to testNames() function).
+ if (isAccessible(elm) || isLogged("stack"))
+ processMarkupRules();
+ else
+ waitForEvent(EVENT_SHOW, elm, processMarkupRules);
+}
+
+/**
+ * Test name for current rule and current 'markup' element. Used by
+ * testNamesForMarkup function.
+ */
+function testNameForRule(aElm, aRuleElm)
+{
+ if (aRuleElm.hasAttribute("attr")) {
+ if (gDumpToConsole) {
+ dump("\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") +" }\n");
+ }
+
+ testNameForAttrRule(aElm, aRuleElm);
+
+ } else if (aRuleElm.hasAttribute("elm")) {
+ if (gDumpToConsole) {
+ dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") +
+ ", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n");
+ }
+
+ testNameForElmRule(aElm, aRuleElm);
+
+ } else if (aRuleElm.getAttribute("fromsubtree") == "true") {
+ if (gDumpToConsole) {
+ dump("\nProcessing rule { fromsubtree: " +
+ aRuleElm.getAttribute("fromsubtree") +" }\n");
+ }
+
+ testNameForSubtreeRule(aElm, aRuleElm);
+ }
+}
+
+function testNameForAttrRule(aElm, aRule)
+{
+ var name = "";
+
+ var attr = aRule.getAttribute("attr");
+ var attrValue = aElm.getAttribute(attr);
+
+ var type = aRule.getAttribute("type");
+ if (type == "string") {
+ name = attrValue;
+
+ } else if (type == "ref" && attrValue) {
+ var ids = attrValue.split(/\s+/);
+ for (var idx = 0; idx < ids.length; idx++) {
+ var labelElm = getNode(ids[idx]);
+ if (name != "")
+ name += " ";
+
+ name += labelElm.getAttribute("textequiv");
+ }
+ }
+
+ var msg = "Attribute '" + attr + "' test (" + gTestIterator.testID + "). ";
+ testName(aElm, name, msg);
+
+ if (aRule.getAttribute("explict-name") != "false")
+ testAttrs(aElm, {"explicit-name" : "true"}, true);
+ else
+ testAbsentAttrs(aElm, {"explicit-name" : "true"});
+
+ // If @recreated attribute is used then this attribute change recreates an
+ // accessible. Wait for reorder event in this case or otherwise proceed next
+ // test immediately.
+ if (aRule.hasAttribute("recreated")) {
+ waitForEvent(EVENT_REORDER, aElm.parentNode,
+ gTestIterator.iterateNext, gTestIterator);
+ aElm.removeAttribute(attr);
+
+ } else if (aRule.hasAttribute("textchanged")) {
+ waitForEvent(EVENT_TEXT_INSERTED, aElm,
+ gTestIterator.iterateNext, gTestIterator);
+ aElm.removeAttribute(attr);
+
+ } else if (aRule.hasAttribute("contentchanged")) {
+ waitForEvent(EVENT_REORDER, aElm,
+ gTestIterator.iterateNext, gTestIterator);
+ aElm.removeAttribute(attr);
+
+ } else {
+ aElm.removeAttribute(attr);
+ gTestIterator.iterateNext();
+ }
+}
+
+function testNameForElmRule(aElm, aRule)
+{
+ var labelElm;
+
+ var tagname = aRule.getAttribute("elm");
+ var attrname = aRule.getAttribute("elmattr");
+ if (attrname) {
+ var filter = {
+ acceptNode: function filter_acceptNode(aNode)
+ {
+ if (aNode.localName == this.mLocalName &&
+ aNode.getAttribute(this.mAttrName) == this.mAttrValue)
+ return NodeFilter.FILTER_ACCEPT;
+
+ return NodeFilter.FILTER_SKIP;
+ },
+
+ mLocalName: tagname,
+ mAttrName: attrname,
+ mAttrValue: aElm.getAttribute("id")
+ };
+
+ var treeWalker = document.createTreeWalker(document.body,
+ NodeFilter.SHOW_ELEMENT,
+ filter);
+ labelElm = treeWalker.nextNode();
+
+ } else {
+ // if attrname is empty then look for the element in subtree.
+ labelElm = aElm.getElementsByTagName(tagname)[0];
+ if (!labelElm)
+ labelElm = aElm.getElementsByTagName("html:" + tagname)[0];
+ }
+
+ if (!labelElm) {
+ ok(false, msg + " Failed to find '" + tagname + "' element.");
+ gTestIterator.iterateNext();
+ return;
+ }
+
+ var msg = "Element '" + tagname + "' test (" + gTestIterator.testID + ").";
+ testName(aElm, labelElm.getAttribute("textequiv"), msg);
+ testAttrs(aElm, {"explicit-name" : "true"}, true);
+
+ var parentNode = labelElm.parentNode;
+
+ if (gDumpToConsole) {
+ dump("\nProcessed elm rule. Wait for reorder event on " +
+ prettyName(parentNode) + "\n");
+ }
+ waitForEvent(EVENT_REORDER, parentNode,
+ gTestIterator.iterateNext, gTestIterator);
+
+ parentNode.removeChild(labelElm);
+}
+
+function testNameForSubtreeRule(aElm, aRule)
+{
+ var msg = "From subtree test (" + gTestIterator.testID + ").";
+ testName(aElm, aElm.getAttribute("textequiv"), msg);
+ testAbsentAttrs(aElm, {"explicit-name" : "true"});
+
+ if (gDumpToConsole) {
+ dump("\nProcessed from subtree rule. Wait for reorder event on " +
+ prettyName(aElm) + "\n");
+ }
+ waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator);
+
+ while (aElm.firstChild)
+ aElm.removeChild(aElm.firstChild);
+}
+
+/**
+ * Return array of 'rule' elements. Used in conjunction with
+ * getRuleElmsFromRulesetElm() function.
+ */
+function getRuleElmsByRulesetId(aRulesetId)
+{
+ var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']";
+ var rulesetElm = evaluateXPath(gRuleDoc, expr);
+ return getRuleElmsFromRulesetElm(rulesetElm[0]);
+}
+
+function getRuleElmsFromRulesetElm(aRulesetElm)
+{
+ var rulesetId = aRulesetElm.getAttribute("ref");
+ if (rulesetId)
+ return getRuleElmsByRulesetId(rulesetId);
+
+ var ruleElms = [];
+
+ var child = aRulesetElm.firstChild;
+ while (child) {
+ if (child.localName == "ruleset")
+ ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child));
+ if (child.localName == "rule")
+ ruleElms.push(child);
+
+ child = child.nextSibling;
+ }
+
+ return ruleElms;
+}
+
+/**
+ * Helper method to evaluate xpath expression.
+ */
+function evaluateXPath(aNode, aExpr, aResolver)
+{
+ var xpe = new XPathEvaluator();
+
+ var resolver = aResolver;
+ if (!resolver) {
+ var node = aNode.ownerDocument == null ?
+ aNode.documentElement : aNode.ownerDocument.documentElement;
+ resolver = xpe.createNSResolver(node);
+ }
+
+ var result = xpe.evaluate(aExpr, aNode, resolver, 0, null);
+ var found = [];
+ var res;
+ while (res = result.iterateNext())
+ found.push(res);
+
+ return found;
+}
+
+function htmlDocResolver(aPrefix) {
+ var ns = {
+ 'html' : 'http://www.w3.org/1999/xhtml'
+ };
+ return ns[aPrefix] || null;
+}
diff --git a/accessible/tests/mochitest/name/markuprules.xml b/accessible/tests/mochitest/name/markuprules.xml
new file mode 100644
index 000000000..7f64ada34
--- /dev/null
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -0,0 +1,373 @@
+<?xml version="1.0"?>
+
+<!--
+ This XML file is used to create sequence of accessible name tests. It consist
+ of two sections. The first section 'ruledfn' declares name computation rules.
+ The second section 'rulesample' defines markup samples we need to check name
+ computation rules for.
+
+ <ruledfn>
+ <ruleset>
+ <rule>
+
+ Section 'ruledfn' contains 'ruleset' elements. Every 'ruleset' element is
+ presented by 'rule' elements so that sequence of 'rule' elements gives the
+ sequence of name computations rules. Every 'rule' element can be one of four
+ types.
+
+ * <rule attr='' type='string'/> used when name is equal to the value of
+ attribute presented on the element.
+
+ Example, 'aria-label' attribute. In this case 'rule' element has 'attr'
+ attribute pointing to attribute name and 'type' attribute with 'string'
+ value. For example, <rule attr="aria-label" type="string"/>.
+
+ * <rule attr='' type='ref'/> used when name is calculated from elements that
+ are pointed to by attribute value on the element.
+
+ Example is 'aria-labelledby'. In this case 'rule' element has 'attr'
+ attribute holding the sequence of IDs of elements used to compute the name,
+ in addition the 'rule' element has 'type' attribute with 'ref' value.
+ For example, <rule attr="aria-labelledby" type="ref"/>.
+
+ * <rule elm='' elmattr=''/> used when name is calculated from another
+ element. These attributes are used to find an element by tagname and
+ attribute with value equaled to ID of the element. If 'elmattr' is missed
+ then element from subtree with the given tagname is used.
+
+ Example, html:label@for element, <rule elm="label" elmattr="for"/>.
+ Example, html:caption element, <rule elm="caption"/>
+
+ * <rule fromsubtree='true'/> used when name is computed from subtree.
+
+ Example, html:button. In this case 'rule' element has 'fromsubtree'
+ attribute with 'true' value.
+
+ <rulesample>
+ <markup ruleset=''>
+
+ Section 'rulesample' provides set of markup samples ('markup' elements). Every
+ 'markup' element contains an element that accessible name will be computed for
+ (let's call it test element). In addition the 'markup' element contains some
+ other elements from native markup used in name calculation process for test
+ element. Test element is pointed to by 'ref' attribute on 'markup' element.
+ Also 'markup' element has 'ruleset' attribute to indicate ruleset for the test
+ element.
+
+ How does it work? Let's consider simple example:
+ <ruledfn>
+ <ruleset id="aria">
+ <rule attr="aria-label" type="string"/>
+ <rule attr="aria-labelledby" type="ref"/>
+ </ruleset>
+ </ruledfn>
+ <rulesample>
+ <markup ref="html:div" ruleset="aria">
+ <html:span id="label" textequiv="test2">test2</html:span>
+ <html:div aria-label="test1"
+ aria-labelledby="label">it's a div</html:div>
+ </markup>
+ </rulesample>
+
+ Initially 'markup' element holds markup for all rules specified by 'ruleset'
+ attribute. This allows us to check if the sequence of name computation rules
+ is correct. Here 'ruleset' element defines two rules. We get the first rule
+ which means accesible name is computed from value of 'aria-label' attribute.
+ Then we check accessible name for the test element and remove 'aria-label'
+ attribute. After we get the second rule which means we should get IDs from
+ 'aria-labelledby' attribute and compose accessible name from values of
+ 'textequiv' attributes (that are supposed to give the desired name for each
+ element that is being pointed to by aria-labelledby). Check accessible name
+ and finish test.
+-->
+
+<rules xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <ruledfn>
+
+ <!-- bricks -->
+ <ruleset id="ARIA">
+ <rule attr="aria-labelledby" type="ref"/>
+ <rule attr="aria-label" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLControl:Head">
+ <ruleset ref="ARIA"/>
+ <rule elm="label" elmattr="for"/>
+ </ruleset>
+
+ <!-- general -->
+ <ruleset id="HTMLControl">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule fromsubtree="true"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLElm">
+ <ruleset ref="ARIA"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <!-- specific -->
+ <ruleset id="HTMLARIAGridCell">
+ <ruleset ref="ARIA"/>
+ <rule fromsubtree="true"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLInputButton">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule attr="value" type="string" explict-name="false" reordered="true"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLInputSubmit" defaultName="Submit Query">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule attr="value" type="string" explict-name="false" textchanged="true"/>
+ </ruleset>
+
+ <ruleset id="HTMLInputReset" defaultName="Reset">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule attr="value" type="string" explict-name="false" textchanged="true"/>
+ </ruleset>
+
+ <ruleset id="HTMLInputImage">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule attr="alt" type="string" recreated="true"/>
+ <rule attr="value" type="string" recreated="true"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLInputImageNoValidSrc" defaultName="Submit Query">
+ <ruleset ref="HTMLControl:Head"/>
+ <rule attr="alt" type="string" explict-name="false" recreated="true"/>
+ <rule attr="value" type="string" explict-name="false" recreated="true"/>
+ </ruleset>
+
+ <ruleset id="HTMLOption">
+ <ruleset ref="ARIA"/>
+ <rule attr="label" type="string"/>
+ <rule fromsubtree="true"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLImg">
+ <ruleset ref="ARIA"/>
+ <rule attr="alt" type="string"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLImgEmptyAlt">
+ <ruleset ref="ARIA"/>
+ <rule attr="title" type="string"/>
+ <rule attr="alt" type="string"/>
+ </ruleset>
+
+ <ruleset id="HTMLTable">
+ <ruleset ref="ARIA"/>
+ <rule elm="caption"/>
+ <rule attr="summary" type="string"/>
+ <rule attr="title" type="string"/>
+ </ruleset>
+ </ruledfn>
+
+ <rulesample>
+
+ <markup id="HTMLButtonTest"
+ ref="html:button" ruleset="HTMLControl">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn" textequiv="test4">test4</html:label>
+ <html:button id="btn"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5"
+ textequiv="press me">press me</html:button>
+ </markup>
+
+ <markup id="HTMLInputButtonTest"
+ ref="html:input" ruleset="HTMLInputButton">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn" textequiv="test4">test4</html:label>
+ <html:input id="btn"
+ type="button"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from al"
+ src="no name from src"
+ data="no name from data"
+ title="name from title"/>
+ </markup>
+
+ <markup id="HTMLInputSubmitTest"
+ ref="html:input" ruleset="HTMLInputSubmit">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn-submit" textequiv="test4">test4</html:label>
+ <html:input id="btn-submit"
+ type="submit"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from atl"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>
+ </markup>
+
+ <markup id="HTMLInputResetTest"
+ ref="html:input" ruleset="HTMLInputReset">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn-reset" textequiv="test4">test4</html:label>
+ <html:input id="btn-reset"
+ type="reset"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from alt"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>
+ </markup>
+
+ <markup id="HTMLInputImageTest"
+ ref="html:input" ruleset="HTMLInputImage">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn-image" textequiv="test4">test4</html:label>
+ <html:input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ src="../moz.png"
+ data="no name from data"
+ title="name from title"/>
+ </markup>
+
+ <markup id="HTMLInputImageNoValidSrcTest"
+ ref="html:input" ruleset="HTMLInputImageNoValidSrc">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="btn-image" textequiv="test4">test4</html:label>
+ <html:input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ data="no name from data"
+ title="no name from title"/>
+ </markup>
+
+ <markup id="HTMLOptionTest"
+ ref="html:select/html:option[1]" ruleset="HTMLOption">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:select>
+ <html:option id="opt"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ label="test4"
+ title="test5"
+ textequiv="option1">option1</html:option>
+ <html:option>option2</html:option>
+ </html:select>
+ </markup>
+
+ <markup id="HTMLImageTest"
+ ref="html:img" ruleset="HTMLImg">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:img id="img"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ alt="Mozilla logo"
+ title="This is a logo"
+ src="../moz.png"/>
+ </markup>
+
+ <markup id="HTMLImageEmptyAltTest"
+ ref="html:img" ruleset="HTMLImgEmptyAlt">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:img id="imgemptyalt"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ title="This is a logo"
+ alt=""
+ src="../moz.png"/>
+ </markup>
+
+ <markup id="HTMLTdTest"
+ ref="html:table/html:tr/html:td" ruleset="HTMLElm">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="tc" textequiv="test4">test4</html:label>
+ <html:table>
+ <html:tr>
+ <html:td id="tc"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5">
+ <html:p>This is a paragraph</html:p>
+ <html:a href="#">This is a link</html:a>
+ <html:ul>
+ <html:li>This is a list</html:li>
+ </html:ul>
+ </html:td>
+ </html:tr>
+ </html:table>
+ </markup>
+
+ <markup id="HTMLTdARIAGridCellTest"
+ ref="html:table/html:tr/html:td" ruleset="HTMLARIAGridCell">
+ <html:span id="l1" textequiv="test2">test2</html:span>
+ <html:span id="l2" textequiv="test3">test3</html:span>
+ <html:label for="gc" textequiv="test4">test4</html:label>
+ <html:table>
+ <html:tr>
+ <html:td id="gc"
+ role="gridcell"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ textequiv="This is a paragraph This is a link • Listitem1 • Listitem2"
+ title="This is a paragraph This is a link This is a list">
+ <html:p>This is a paragraph</html:p>
+ <html:a href="#">This is a link</html:a>
+ <html:ul>
+ <html:li>Listitem1</html:li>
+ <html:li>Listitem2</html:li>
+ </html:ul>
+ </html:td>
+ </html:tr>
+ </html:table>
+ </markup>
+
+ <markup id="HTMLTableTest"
+ ref="html:table" ruleset="HTMLTable">
+ <html:span id="l1" textequiv="lby_tst6_1">lby_tst6_1</html:span>
+ <html:span id="l2" textequiv="lby_tst6_2">lby_tst6_2</html:span>
+ <html:label for="t" textequiv="label_tst6">label_tst6</html:label>
+ <!-- layout frame are recreated due to varous reasons, here's text frame
+ placed after caption frame triggres table frame recreation when
+ caption element is removed from DOM; get rid text node after caption
+ node to make the test working -->
+ <html:table id="t" aria-label="arialabel_tst6"
+ aria-labelledby="l1 l2"
+ summary="summary_tst6"
+ title="title_tst6">
+ <html:caption textequiv="caption_tst6">caption_tst6</html:caption><html:tr>
+ <html:td>cell1</html:td>
+ <html:td>cell2</html:td>
+ </html:tr>
+ </html:table>
+ </markup>
+
+ </rulesample>
+</rules>
diff --git a/accessible/tests/mochitest/name/test_browserui.xul b/accessible/tests/mochitest/name/test_browserui.xul
new file mode 100644
index 000000000..ec21708fd
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_browserui.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Name Calculating Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function addTab(aURL)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.invoke = function addTab_invoke()
+ {
+ tabBrowser().addTab(aURL);
+ }
+
+ this.getID = function addTab_getID()
+ {
+ return "add tab: " + aURL;
+ }
+ }
+
+ function switchTab(aTabBrowser, aWindow)
+ {
+ this.invoke = function switchTab_invoke()
+ {
+ synthesizeKey("VK_TAB", { ctrlKey: true }, browserWindow());
+ }
+
+ this.eventSeq = [
+ new focusChecker(tabDocumentAt, 1)
+ ];
+
+ this.check = function switchTab_check(aEvent)
+ {
+ var title = getAccessible(browserDocument()).name;
+ isnot(title.indexOf(aEvent.accessible.name), -1,
+ "Window title contains the name of active tab document" +
+ " (Is '" + aEvent.accessible.name + "' in '" + title + "'?)");
+ }
+
+ this.getID = function switchTab_getID() { return "switch tab"; }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new addTab("about:mozilla"));
+ gQueue.push(new switchTab());
+ gQueue.onFinish = function()
+ {
+ closeBrowserWindow();
+ }
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, "about:");
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=507382"
+ title="focus is fired earlier than root accessible name is changed when switching between tabs">
+ Mozilla Bug
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/name/test_counterstyle.html b/accessible/tests/mochitest/name/test_counterstyle.html
new file mode 100644
index 000000000..506cea69a
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_counterstyle.html
@@ -0,0 +1,153 @@
+<html>
+
+<head>
+ <title>nsIAccessible::name calculation for @counter-style</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <style id="counterstyles" type="text/css">
+ @counter-style system-alphabetic {
+ system: alphabetic;
+ symbols: x y z;
+ }
+ @counter-style system-cyclic {
+ system: cyclic;
+ symbols: x y z;
+ }
+ @counter-style system-numeric {
+ system: numeric;
+ symbols: x y z;
+ }
+ @counter-style speak-as-bullets {
+ system: extends decimal;
+ speak-as: bullets;
+ }
+ @counter-style speak-as-numbers {
+ system: extends system-alphabetic;
+ speak-as: numbers;
+ }
+ @counter-style speak-as-words {
+ system: additive;
+ additive-symbols: 20 "twenty ", 9 "nine", 7 "seven", 1 "one";
+ speak-as: words;
+ }
+ @counter-style speak-as-spell-out {
+ system: extends system-alphabetic;
+ speak-as: spell-out;
+ }
+ @counter-style speak-as-other {
+ system: extends decimal;
+ speak-as: speak-as-words;
+ }
+ @counter-style speak-as-loop {
+ system: extends upper-latin;
+ speak-as: speak-as-loop0;
+ }
+ @counter-style speak-as-loop0 {
+ system: extends disc;
+ speak-as: speak-as-loop1;
+ }
+ @counter-style speak-as-loop1 {
+ system: extends decimal;
+ speak-as: speak-as-loop0;
+ }
+ @counter-style speak-as-extended0 {
+ system: extends decimal;
+ speak-as: speak-as-extended1;
+ }
+ @counter-style speak-as-extended1 {
+ system: extends speak-as-extended0;
+ speak-as: disc;
+ }
+ @counter-style speak-as-extended2 {
+ system: extends decimal;
+ speak-as: speak-as-extended3;
+ }
+ @counter-style speak-as-extended3 {
+ system: extends speak-as-extended2;
+ }
+ </style>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ function testRule(aRule, aNames, aTodo)
+ {
+ testName(aRule + "-1", aNames[0], null, aTodo);
+ testName(aRule + "-7", aNames[1], null, aTodo);
+ testName(aRule + "-29", aNames[2], null, aTodo);
+ }
+
+ var spellOutNames = ["X. 1", "Y X. 7", "Y Z Y. 29"];
+ var bulletsNames = [kDiscBulletText + "1",
+ kDiscBulletText + "7",
+ kDiscBulletText + "29"];
+ var numbersNames = ["1. 1", "7. 7", "29. 29"];
+ var wordsNames = ["one. 1", "seven. 7", "twenty nine. 29"];
+
+ testRule("system-alphabetic", spellOutNames, true); // bug 1024178
+ testRule("system-cyclic", bulletsNames);
+ testRule("system-numeric", numbersNames);
+
+ testRule("speak-as-bullets", bulletsNames);
+ testRule("speak-as-numbers", numbersNames);
+ testRule("speak-as-words", wordsNames);
+ testRule("speak-as-spell-out", spellOutNames, true); // bug 1024178
+ testRule("speak-as-other", wordsNames);
+
+ testRule("speak-as-loop", bulletsNames);
+ testRule("speak-as-loop0", bulletsNames);
+ testRule("speak-as-loop1", numbersNames);
+
+ testRule("speak-as-extended0", bulletsNames);
+ testRule("speak-as-extended1", bulletsNames);
+ testRule("speak-as-extended2", numbersNames);
+ testRule("speak-as-extended3", numbersNames);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=966166"
+ title="Bug 966166 - Implement @counter-style rule">
+ Bug 966166
+ </a>
+
+ <ol id="list"></ol>
+
+ <script type="application/javascript">
+ var list = getNode("list");
+ var rules = getNode("counterstyles").sheet.cssRules;
+ var values = [1, 7, 29];
+ for (var i = 0; i < rules.length; i++) {
+ var rule = rules[i];
+ for (var j = 0; j < values.length; j++) {
+ var item = document.createElement("li");
+ item.id = rule.name + '-' + values[j];
+ item.value = values[j];
+ item.textContent = values[j];
+ item.setAttribute("style", "list-style-type: " + rule.name);
+ list.appendChild(item);
+ }
+ }
+ </script>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_general.html b/accessible/tests/mochitest/name/test_general.html
new file mode 100644
index 000000000..28e7a11a9
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -0,0 +1,631 @@
+<html>
+
+<head>
+ <title>nsIAccessible::name calculation</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // aria-label
+
+ // Simple label provided via ARIA
+ testName("btn_simple_aria_label", "I am a button");
+
+ // aria-label and aria-labelledby, expect aria-labelledby
+ testName("btn_both_aria_labels", "text I am a button, two");
+
+ //////////////////////////////////////////////////////////////////////////
+ // aria-labelledby
+
+ // Single relation. The value of 'aria-labelledby' contains the ID of
+ // an element. Gets the name from text node of that element.
+ testName("btn_labelledby_text", "text");
+
+ // Multiple relations. The value of 'aria-labelledby' contains the IDs
+ // of elements. Gets the name from text nodes of those elements.
+ testName("btn_labelledby_texts", "text1 text2");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from named accessible
+
+ testName("input_labelledby_namedacc", "Data");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from subtree (single relation labelled_by).
+
+ // Gets the name from text nodes contained by nested elements
+ testName("btn_labelledby_mixed", "nomore text");
+
+ // Gets the name from text nodes contained by nested elements, ignores
+ // hidden elements (bug 443081).
+ testName("btn_labelledby_mixed_hidden_child", "nomore text2");
+
+ // Gets the name from hidden text nodes contained by nested elements,
+ // (label element is hidden entirely), (bug 443081).
+ testName("btn_labelledby_mixed_hidden", "lala more hidden text");
+
+ // Gets the name from text nodes contained by nested elements having block
+ // representation (every text node value in the name should be devided by
+ // spaces)
+ testName("btn_labelledby_mixed_block", "text more text");
+
+ // Gets the name from text nodes contained by html:td (every text node
+ // value in the name should be devided by spaces).
+ // XXX: this case is rather a feature than strong wanted behaviour.
+ testName("btn_labelledby_mixed_table", "text space text");
+
+ // Gets the name from image accessible.
+ testName("btn_labelledby_mixed_img", "text image");
+
+ // Gets the name from input accessibles
+ // Note: if input have label elements then the name isn't calculated
+ // from them.
+ testName("btn_labelledby_mixed_input",
+ "input button Submit Query Reset Submit Query");
+
+ // Gets the name from the title of object element.
+ testName("btn_labelledby_mixed_object", "object");
+
+ // Gets the name from text nodes. Element br adds space between them.
+ testName("btn_labelledby_mixed_br", "text text");
+
+ // Gets the name from label content which allows name from subtree,
+ // ignore @title attribute on label
+ testName("from_label_ignoretitle", "Country:");
+
+ // Gets the name from html:p content, which doesn't allow name from
+ // subtree, ignore @title attribute on label
+ testName("from_p_ignoretitle", "Choose country from.");
+
+ // Gets the name from html:input value, ignore @title attribute on input
+ testName("from_input_ignoretitle", "Custom country");
+
+ // Insert spaces around the control's value to not jamm sibling text nodes
+ testName("insert_spaces_around_control", "start value end");
+
+ // Gets the name from @title, ignore whitespace content
+ testName("from_label_ignore_ws_subtree", "about");
+
+ //////////////////////////////////////////////////////////////////////////
+ // label element
+
+ // The label element contains the button. The name is calculated from
+ // this button.
+ // Note: the name contains the content of the button.
+ testName("btn_label_inside", "text10text");
+
+ // The label element and the button are placed in the same form. Gets
+ // the name from the label subtree.
+ testName("btn_label_inform", "in form");
+
+ // The label element is placed outside of form where the button is.
+ // Take into account the label.
+ testName("btn_label_outform", "out form");
+
+ // The label element and the button are in the same document. Gets the
+ // name from the label subtree.
+ testName("btn_label_indocument", "in document");
+
+ // Multiple label elements for single button
+ testName("btn_label_multi", "label1label2");
+
+ // Multiple controls inside a label element
+ testName("ctrl_in_label_1", "Enable a button control");
+ testName("ctrl_in_label_2", "button");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // name from children
+
+ // ARIA role button is presented allowing the name calculation from
+ // children.
+ testName("btn_children", "14");
+
+ // html:button, no name from content
+ testName("btn_nonamefromcontent", null);
+
+ // ARIA role option is presented allowing the name calculation from
+ // visible children (bug 443081).
+ testName("lb_opt1_children_hidden", "i am visible");
+
+ // Get the name from subtree of menuitem crossing role nothing to get
+ // the name from its children.
+ testName("tablemenuitem", "menuitem 1");
+
+ // Get the name from child acronym title attribute rather than from
+ // acronym content.
+ testName("label_with_acronym", "O A T F World Wide Web");
+
+ testName("testArticle", "Test article");
+
+ //////////////////////////////////////////////////////////////////////////
+ // title attribute
+
+ // If nothing is left. Let's try title attribute.
+ testName("btn_title", "title");
+
+ //////////////////////////////////////////////////////////////////////////
+ // textarea name
+
+ // textarea's name should have the value, which initially is specified by
+ // a text child.
+ testName("textareawithchild", "Story Foo is ended.");
+
+ // new textarea name should reflect the value change.
+ var elem = document.getElementById("textareawithchild");
+ elem.value = "Bar";
+
+ testName("textareawithchild", "Story Bar is ended.");
+
+ //////////////////////////////////////////////////////////////////////////
+ // controls having a value used as a part of computed name
+
+ testName("ctrlvalue_progressbar:input", "foo 5 baz");
+ testName("ctrlvalue_scrollbar:input", "foo 5 baz");
+ testName("ctrlvalue_slider:input", "foo 5 baz");
+ testName("ctrlvalue_spinbutton:input", "foo 5 baz");
+ testName("ctrlvalue_combobox:input", "foo 5 baz");
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // label with nested combobox (test for 'f' item of name computation guide)
+
+ testName("comboinstart", "One day(s).");
+ testName("combo3", "day(s).");
+
+ testName("textboxinstart", "Two days.");
+ testName("textbox1", "days.");
+
+ testName("comboinmiddle", "Subscribe to ATOM feed.");
+ testName("combo4", "Subscribe to ATOM feed.");
+
+ testName("comboinmiddle2", "Play the Haliluya sound when new mail arrives");
+ testName("combo5", null); // label isn't used as a name for control
+ testName("checkbox", "Play the Haliluya sound when new mail arrives");
+ testName("comboinmiddle3", "Play the Haliluya sound when new mail arrives");
+ testName("combo6", "Play the Haliluya sound when new mail arrives");
+
+ testName("comboinend", "This day was sunny");
+ testName("combo7", "This day was");
+
+ testName("textboxinend", "This day was sunny");
+ testName("textbox2", "This day was");
+
+ // placeholder
+ testName("ph_password", "a placeholder");
+ testName("ph_text", "a placeholder");
+ testName("ph_textarea", "a placeholder");
+ testName("ph_text2", "a label");
+ testName("ph_textarea2", "a label");
+ testName("ph_text3", "a label");
+
+ // Test equation image
+ testName("img_eq", "x^2 + y^2 + z^2")
+ testName("input_img_eq", "x^2 + y^2 + z^2")
+ testName("txt_eq", "x^2 + y^2 + z^2")
+
+ ////////////////////////////////////////////////////////////////////////
+ // tests for duplicate announcement of content
+
+ testName("test_note", null);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=428479"
+ title="Bug 428479 - Support ARIA role=math">
+ Bug 428479
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429666"
+ title="Expose ROLE_DOCUMENT for ARIA landmarks that inherit from document">
+ Bug 429666
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=444279"
+ title="mochitest for accessible name calculating">
+ Bug 444279
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=459635"
+ title="nsIAccessible::name calculation for HTML buttons">
+ Bug 459635
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=530081"
+ title="Clean up our tree walker">
+ Bug 530081
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=604391"
+ title="Use placeholder as name if name is otherwise empty">
+ Bug 604391
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=669312"
+ title="Accessible name is duplicated when input has a label associated uisng for/id and is wrapped around the input">
+ Bug 669312
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=704416"
+ title="HTML acronym and abbr names should be provided by @title">
+ Bug 704416
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=812041"
+ title="ARIA slider and spinbutton don't provide a value for name computation">
+ Bug 812041
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=823927"
+ title="Text is jammed with control's text in name computation">
+ Bug 823927
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=835666"
+ title="ARIA combobox selected value is not a part of name computation">
+ Bug 835666
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=833256"
+ title="role note shouldn't pick up the name from subtree">
+ Mozilla Bug 833256
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- aria-label, simple label -->
+ <span id="btn_simple_aria_label" role="button" aria-label="I am a button"/>
+ <br/>
+ <!-- aria-label plus aria-labelledby -->
+ <span id="btn_both_aria_labels" role="button" aria-label="I am a button, two"
+ aria-labelledby="labelledby_text btn_both_aria_labels"/>
+ <br/>
+
+ <!-- aria-labelledby, single relation -->
+ <span id="labelledby_text">text</span>
+ <button id="btn_labelledby_text"
+ aria-labelledby="labelledby_text">1</button>
+ <br/>
+
+ <!-- aria-labelledby, multiple relations -->
+ <span id="labelledby_text1">text1</span>
+ <span id="labelledby_text2">text2</span>
+ <button id="btn_labelledby_texts"
+ aria-labelledby="labelledby_text1 labelledby_text2">2</button>
+ <br/>
+
+ <!-- name from named accessible -->
+ <input id="labelledby_namedacc" type="checkbox"
+ aria-label="Data" />
+ <input id="input_labelledby_namedacc"
+ aria-labelledby="labelledby_namedacc" />
+
+ <!-- the name from subtree, mixed content -->
+ <span id="labelledby_mixed">no<span>more text</span></span>
+ <button id="btn_labelledby_mixed"
+ aria-labelledby="labelledby_mixed">3</button>
+ <br/>
+
+ <!-- the name from subtree, mixed/hidden content -->
+ <span id="labelledby_mixed_hidden_child">
+ no<span>more
+ <span style="display: none;">hidden</span>
+ text2
+ <span style="visibility: hidden">hidden2</span>
+ </span>
+ </span>
+ <button id="btn_labelledby_mixed_hidden_child"
+ aria-labelledby="labelledby_mixed_hidden_child">3.1</button>
+ <br/>
+
+ <!-- the name from subtree, mixed/completely hidden content -->
+ <span id="labelledby_mixed_hidden"
+ style="display: none;">lala <span>more hidden </span>text</span></span>
+ <button id="btn_labelledby_mixed_hidden"
+ aria-labelledby="labelledby_mixed_hidden">3.2</button>
+ <br/>
+
+ <!-- the name from subtree, mixed content, block structure -->
+ <div id="labelledby_mixed_block"><div>text</div>more text</div></div>
+ <button id="btn_labelledby_mixed_block"
+ aria-labelledby="labelledby_mixed_block">4</button>
+ <br/>
+
+ <!-- the name from subtree, mixed content, table structure -->
+ <table><tr>
+ <td id="labelledby_mixed_table">text<span>space</span>text</td>
+ </tr></table>
+ <button id="btn_labelledby_mixed_table"
+ aria-labelledby="labelledby_mixed_table">5</button>
+ <br/>
+
+ <!-- the name from subtree, child img -->
+ <span id="labelledby_mixed_img">text<img alt="image"/></span>
+ <button id="btn_labelledby_mixed_img"
+ aria-labelledby="labelledby_mixed_img">6</button>
+ <br/>
+
+ <!-- the name from subtree, child inputs -->
+ <span id="labelledby_mixed_input">
+ <input type="button" id="input_button" title="input button"/>
+ <input type="submit" id="input_submit"/>
+ <input type="reset" id="input_reset"/>
+ <input type="image" id="input_image" title="input image"/>
+ </span>
+ <button id="btn_labelledby_mixed_input"
+ aria-labelledby="labelledby_mixed_input">7</button>
+ <br/>
+
+ <!-- the name from subtree, child object -->
+ <span id="labelledby_mixed_object">
+ <object data="about:blank" title="object"></object>
+ </span>
+ <button id="btn_labelledby_mixed_object"
+ aria-labelledby="labelledby_mixed_object">8</button>
+ <br/>
+
+ <!-- the name from subtree, child br -->
+ <span id="labelledby_mixed_br">text<br/>text</span>
+ <button id="btn_labelledby_mixed_br"
+ aria-labelledby="labelledby_mixed_br">9</button>
+ <br/>
+
+ <!-- the name from subtree, name from label content rather than from its title
+ attribute -->
+ <label for="from_label_ignoretitle"
+ title="Select your country of origin">Country:</label>
+ <select id="from_label_ignoretitle">
+ <option>Germany</option>
+ <option>Russia</option>
+ </select>
+
+ <!-- the name from subtree, name from html:p content rather than from its
+ title attribute -->
+ <p id="p_ignoretitle"
+ title="Select your country of origin">Choose country from.</p>
+ <select id="from_p_ignoretitle" aria-labelledby="p_ignoretitle">
+ <option>Germany</option>
+ <option>Russia</option>
+ </select>
+
+ <!-- the name from subtree, name from html:input value rather than from its
+ title attribute -->
+ <p id="from_input_ignoretitle" aria-labelledby="input_ignoretitle">Country</p>
+ <input id="input_ignoretitle"
+ value="Custom country"
+ title="Input your country of origin"/ >
+
+ <!-- name from subtree, surround control by spaces to not jamm the text -->
+ <label id="insert_spaces_around_control">
+ start<input value="value">end
+ </label>
+
+ <!-- no name from subtree because it holds whitespaces only -->
+ <a id="from_label_ignore_ws_subtree" href="about:" title="about">&nbsp;&nbsp; </a>
+
+ <!-- label element, label contains control -->
+ <label>text<button id="btn_label_inside">10</button>text</label>
+ <br/>
+
+ <!-- label element, label and the button are in the same form -->
+ <form>
+ <label for="btn_label_inform">in form</label>
+ <button id="btn_label_inform">11</button>
+ </form>
+
+ <!-- label element, label is outside of the form of the button -->
+ <label for="btn_label_outform">out form</label>
+ <form>
+ <button id="btn_label_outform">12</button>
+ </form>
+
+ <!-- label element, label and the button are in the same document -->
+ <label for="btn_label_indocument">in document</label>
+ <button id="btn_label_indocument">13</button>
+
+ <!-- multiple label elements for single button -->
+ <label for="btn_label_multi">label1</label>
+ <label for="btn_label_multi">label2</label>
+ <button id="btn_label_multi">button</button>
+
+ <!-- a label containing more than one controls -->
+ <label>
+ Enable <input id="ctrl_in_label_1" type="checkbox"> a
+ <input id="ctrl_in_label_2" type="button" value="button"> control
+ </label>
+
+ <!-- name from children -->
+ <span id="btn_children" role="button">14</span>
+
+ <!-- no name from content, ARIA role overrides this rule -->
+ <button id="btn_nonamefromcontent" role="img">1</button>
+
+ <!-- name from children, hidden children -->
+ <div role="listbox" tabindex="0">
+ <div id="lb_opt1_children_hidden" role="option" tabindex="0">
+ <span>i am visible</span>
+ <span style="display:none">i am hidden</span>
+ </div>
+ </div>
+
+ <table role="menu">
+ <tr role="menuitem" id="tablemenuitem">
+ <td>menuitem 1</td>
+ </tr>
+ <tr role="menuitem">
+ <td>menuitem 2</td>
+ </tr>
+ </table>
+
+ <label id="label_with_acronym">
+ <acronym title="O A T F">OATF</acronym>
+ <abbr title="World Wide Web">WWW</abbr>
+ </label>
+
+ <div id="testArticle" role="article" title="Test article">
+ <p>This is a paragraph inside the article.</p>
+ </div>
+
+ <!-- name from title attribute -->
+ <span id="btn_title" role="group" title="title">15</span>
+
+ <!-- A textarea nested in a label with a text child (bug #453371). -->
+ <form>
+ <label>Story
+ <textarea id="textareawithchild" name="name">Foo</textarea>
+ is ended.
+ </label>
+ </form>
+
+ <!-- controls having a value used as part of computed name -->
+ <input type="checkbox" id="ctrlvalue_progressbar:input">
+ <label for="ctrlvalue_progressbar:input">
+ foo <span role="progressbar"
+ aria-valuenow="5" aria-valuemin="1"
+ aria-valuemax="10">5</span> baz
+ </label>
+
+ <input type="checkbox" id="ctrlvalue_scrollbar:input" />
+ <label for="ctrlvalue_scrollbar:input">
+ foo <span role="scrollbar"
+ aria-valuenow="5" aria-valuemin="1"
+ aria-valuemax="10">5</span> baz
+ </label>
+
+ <input type="checkbox" id="ctrlvalue_slider:input">
+ <label for="ctrlvalue_slider:input">
+ foo <input role="slider" type="range"
+ value="5" min="1" max="10"
+ aria-valuenow="5" aria-valuemin="1"
+ aria-valuemax="10"> baz
+ </label>
+
+ <input type="checkbox" id="ctrlvalue_spinbutton:input">
+ <label for="ctrlvalue_spinbutton:input">
+ foo <input role="spinbutton" type="number"
+ value="5" min="1" max="10"
+ aria-valuenow="5" aria-valuemin="1"
+ aria-valuemax="10">
+ baz
+ </label>
+
+ <input type="checkbox" id="ctrlvalue_combobox:input">
+ <label for="ctrlvalue_combobox:input">
+ foo
+ <div role="combobox">
+ <div role="textbox"></div>
+ <ul role="listbox" style="list-style-type: none;">
+ <li role="option">1</li>
+ <li role="option" aria-selected="true">5</li>
+ <li role="option">3</li>
+ </ul>
+ </div>
+ baz
+ </label>
+
+ <!-- a label with a nested control in the start, middle and end -->
+ <form>
+ <!-- at the start (without and with whitespaces) -->
+ <label id="comboinstart"><select id="combo3">
+ <option>One</option>
+ <option>Two</option>
+ </select>
+ day(s).
+ </label>
+
+ <label id="textboxinstart">
+ <input id="textbox1" value="Two">
+ days.
+ </label>
+
+ <!-- in the middle -->
+ <label id="comboinmiddle">
+ Subscribe to
+ <select id="combo4" name="occupation">
+ <option>ATOM</option>
+ <option>RSS</option>
+ </select>
+ feed.
+ </label>
+
+ <label id="comboinmiddle2" for="checkbox">Play the
+ <select id="combo5">
+ <option>Haliluya</option>
+ <option>Hurra</option>
+ </select>
+ sound when new mail arrives
+ </label>
+ <input id="checkbox" type="checkbox" />
+
+ <label id="comboinmiddle3" for="combo6">Play the
+ <select id="combo6">
+ <option>Haliluya</option>
+ <option>Hurra</option>
+ </select>
+ sound when new mail arrives
+ </label>
+
+ <!-- at the end (without and with whitespaces) -->
+ <label id="comboinend">
+ This day was
+ <select id="combo7" name="occupation">
+ <option>sunny</option>
+ <option>rainy</option>
+ </select></label>
+
+ <label id="textboxinend">
+ This day was
+ <input id="textbox2" value="sunny">
+ </label>
+ </form>
+
+ <!-- placeholder -->
+ <input id="ph_password" type="password" value="" placeholder="a placeholder" />
+ <input id="ph_text" type="text" placeholder="a placeholder" />
+ <textarea id="ph_textarea" cols="5" placeholder="a placeholder"></textarea>
+
+ <!-- placeholder does not win -->
+ <input id="ph_text2" type="text" aria-label="a label" placeholder="meh" />
+ <textarea id="ph_textarea2" cols="5" aria-labelledby="ph_text2"
+ placeholder="meh"></textarea>
+
+ <label for="ph_text3">a label</label>
+ <input id="ph_text3" placeholder="meh" />
+
+ <p>Image:
+ <img id="img_eq" role="math" src="foo" alt="x^2 + y^2 + z^2">
+ <input type="image" id="input_img_eq" src="foo" alt="x^2 + y^2 + z^2">
+ </p>
+
+ <p>Text:
+ <span id="txt_eq" role="math" title="x^2 + y^2 + z^2">x<sup>2</sup> +
+ y<sup>2</sup> + z<sup>2</sup></span>
+
+ <!-- duplicate announcement -->
+ <div id="test_note" role="note">subtree</div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_general.xul b/accessible/tests/mochitest/name/test_general.xul
new file mode 100644
index 000000000..c144e6f4f
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -0,0 +1,382 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="general.css"
+ type="text/css"?>
+
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Name Calculating Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // aria-label
+
+ // Simple label provided via ARIA
+ testName("btn_simple_aria_label", "I am a button");
+
+ // aria-label and aria-labelledby, expect aria-labelledby
+ testName("btn_both_aria_labels", "text I am a button, two");
+
+ //////////////////////////////////////////////////////////////////////////
+ // aria-labelledby
+
+ // Single relation. The value of 'aria-labelledby' contains the ID of
+ // an element. Gets the name from text node of that element.
+ testName("btn_labelledby_text", "text");
+
+ // Multiple relations. The value of 'aria-labelledby' contains the IDs
+ // of elements. Gets the name from text nodes of those elements.
+ testName("btn_labelledby_texts", "text1 text2");
+
+ // Trick cases. Self and recursive referencing.
+ testName("rememberHistoryDays", "Remember 3 days");
+ testName("historyDays", "Remember 3 days");
+ testName("rememberAfter", "days");
+
+ // Anonymous content (see name.xbl#third)
+ var anonBtn = getAccessible("labelledby_box_anon").lastChild;
+ testName(anonBtn, "It's a cool button");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from subtree (single relation labelled_by).
+
+ // Gets the name from text nodes contained by nested elements.
+ testName("btn_labelledby_mixed", "nomore text");
+
+ // Gets the name from text nodes and selected item of menulist
+ // (other items are ignored).
+ testName("btn_labelledby_mixed_menulist",
+ "nomore text selected item more text");
+
+ // Gets the name from text nodes contained by nested elements, ignores
+ // hidden elements (bug 443081).
+ testName("btn_labelledby_mixed_hidden_child", "nomore text2");
+
+ // Gets the name from hidden text nodes contained by nested elements,
+ // (label element is hidden entirely), (bug 443081)
+ testName("btn_labelledby_mixed_hidden", "lala more hidden text");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name for nsIDOMXULLabeledControlElement.
+
+ // Gets the name from @label attribute.
+ testName("btn_nsIDOMXULLabeledControlElement", "labeled element");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name for nsIDOMXULSelectControlItemElement.
+
+ // Gets the name from @label attribute.
+ testName("li_nsIDOMXULSelectControlItemElement", "select control item");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name if the XUL element doesn't implement nsIDOMXULSelectControlElement
+ // and has @label attribute.
+
+ testName("box_not_nsIDOMXULSelectControlElement", "box");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from the label element.
+
+ // The label and button are placed on 2nd level relative common parent.
+ testName("btn_label_1", "label1");
+
+ // The label is on 1st, the button is on 5th level relative common parent.
+ testName("btn_label_2", "label2");
+
+ // The label and button are siblings.
+ testName("btn_label_3", "label3");
+
+ // Multiple labels for single button: XUL button takes the last one.
+ testName("btn_label_4", "label5");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from the label element in anonymous content (see bug 362365).
+
+ // Get the name from anonymous label element for anonymous textbox
+ // (@anonid is used).
+ var ID = "box_label_anon1";
+ var box1Acc = testName(ID, null);
+ if (box1Acc) {
+ var textboxAcc = box1Acc.firstChild;
+ is(textboxAcc.name, "Label",
+ "Wrong label for anonymous textbox of " + ID);
+ }
+
+ // Get the name from anonymous label element for anonymous textbox
+ // (@anonid is used). Nested bindings.
+ ID = "box_label_anon2";
+ var box2Acc = testName(ID, null);
+ if (box2Acc) {
+ var textboxAcc = box2Acc.firstChild;
+ is(textboxAcc.name, "Label",
+ "Wrong label for anonymous textbox of " + ID);
+
+ var topTextboxAcc = box2Acc.lastChild;
+ is(topTextboxAcc.name, "Top textbox",
+ "Wrong label for anonymous textbox of " + ID);
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // tooltiptext (if nothing above isn't presented then tooltiptext is used)
+ testName("box_tooltiptext", "tooltiptext label");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from the @title attribute of <toolbaritem/> (original bug 237249).
+
+ // Direct child of toolbaritem.
+ var textboxAcc = testName("toolbaritem_textbox", "ooospspss");
+
+ // Element from anonymous content of direct child of toolbaritem.
+ var entryAcc = textboxAcc.firstChild;
+ testRole(entryAcc, ROLE_ENTRY);
+ is(entryAcc.name, "ooospspss",
+ "Wrong name for text entry of autocomplete textbox 'toolbaritem_textbox'.");
+
+ // Child from subtree of toolbaritem.
+ testName("toolbaritem_hboxbutton", "ooospspss");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from children
+
+ // ARIA role button is presented allowing the name calculation from
+ // children.
+ testName("box_children", "14");
+
+ // ARIA role option is presented allowing the name calculation from
+ // the visible children (bug 443081)
+ testName("lb_opt1_children_hidden", "i am visible");
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Name from aria-labelledby: menuitem label+ listitem label
+ testName("li_labelledby", "Show an Alert The moment the event starts");
+
+ //////////////////////////////////////////////////////////////////////////
+ // groupbox labeling from caption label or its sub tree
+ testName("groupbox", "Some caption");
+ testName("groupbox2", "Some caption");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=444279"
+ title="mochitest for accessible name calculating">
+ Mozilla Bug 444279
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441991"
+ title="nsXULListitemAccessible::GetName prefers label \
+ attribute over aria-labelledby and doesn't allow recursion">
+ Mozilla Bug 441991
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <!-- aria-label, simple label -->
+ <button id="btn_simple_aria_label" aria-label="I am a button"/>
+
+ <!-- aria-label plus aria-labelledby -->
+ <button id="btn_both_aria_labels" aria-label="I am a button, two"
+ aria-labelledby="labelledby_text btn_both_aria_labels"/>
+
+ <!-- aria-labelledby, single relation -->
+ <description id="labelledby_text">text</description>
+ <button id="btn_labelledby_text"
+ aria-labelledby="labelledby_text"/>
+
+ <!-- aria-labelledby, multiple relations -->
+ <description id="labelledby_text1">text1</description>
+ <description id="labelledby_text2">text2</description>
+ <button id="btn_labelledby_texts"
+ aria-labelledby="labelledby_text1 labelledby_text2"/>
+
+ <!-- aria-labelledby, multiple relations -->
+ <box class="third" id="labelledby_box_anon" role="group" />
+
+ <!-- trick aria-labelledby -->
+ <checkbox id="rememberHistoryDays"
+ label="Remember "
+ aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
+ <textbox id="historyDays" type="number" size="3" value="3"
+ aria-labelledby="rememberHistoryDays historyDays rememberAfter"/>
+ <label id="rememberAfter">days</label>
+
+ <!-- the name from subtree, mixed content -->
+ <description id="labelledby_mixed">
+ no<description>more text</description>
+ </description>
+ <button id="btn_labelledby_mixed"
+ aria-labelledby="labelledby_mixed"/>
+
+ <!-- the name from subtree, mixed/hidden content -->
+ <description id="labelledby_mixed_hidden_child">no<description>more <description hidden="true">hidden</description>text2</description></description>
+ <button id="btn_labelledby_mixed_hidden_child"
+ aria-labelledby="labelledby_mixed_hidden_child"/>
+
+ <!-- the name from subtree, mixed/completely hidden content -->
+ <description id="labelledby_mixed_hidden"
+ hidden="true">lala <description>more hidden </description>text</description>
+ <button id="btn_labelledby_mixed_hidden"
+ aria-labelledby="labelledby_mixed_hidden"/>
+ <br/>
+
+ <!-- the name from subtree, mixed content, ignore items of menulist -->
+ <description id="labelledby_mixed_menulist">
+ no<description>more text</description>
+ <menulist>
+ <menupopup>
+ <menuitem label="selected item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menulist>
+ more text
+ </description>
+ <button id="btn_labelledby_mixed_menulist"
+ aria-labelledby="labelledby_mixed_menulist"/>
+
+ <!-- nsIDOMXULLabeledControlElement -->
+ <button id="btn_nsIDOMXULLabeledControlElement"
+ label="labeled element"/>
+
+ <!-- nsIDOMXULSelectControlItemElement -->
+ <listbox>
+ <listitem id="li_nsIDOMXULSelectControlItemElement"
+ label="select control item"/>
+ </listbox>
+
+ <!-- not nsIDOMXULSelectControlElement -->
+ <box id="box_not_nsIDOMXULSelectControlElement" role="group" label="box"/>
+
+ <!-- label element -->
+ <hbox>
+ <box>
+ <label control="btn_label_1">label1</label>
+ </box>
+ <label control="btn_label_2">label2</label>
+ <box>
+ <button id="btn_label_1"/>
+ <box>
+ <box>
+ <box>
+ <button id="btn_label_2"/>
+ </box>
+ </box>
+ </box>
+ </box>
+ <label control="btn_label_3">label3</label>
+ <button id="btn_label_3"/>
+
+ <label control="btn_label_4">label4</label>
+ <label control="btn_label_4">label5</label>
+ <button id="btn_label_4"/>
+ </hbox>
+
+ <!-- label element, anonymous content -->
+ <box id="box_label_anon1"
+ class="first"
+ role="group"/>
+
+ <box id="box_label_anon2"
+ class="second"
+ role="group"/>
+
+ <!-- tooltiptext -->
+ <box id="box_tooltiptext"
+ role="group"
+ tooltiptext="tooltiptext label"/>
+
+ <!-- the name from @title of toolbaritem -->
+ <toolbar>
+ <toolbaritem title="ooospspss">
+ <textbox id="toolbaritem_textbox"
+ flex="1"
+ type="autocomplete"
+ enablehistory="true">
+ <hbox role="button" id="toolbaritem_hboxbutton">
+ <description value="button"/>
+ </hbox>
+ </textbox>
+ </toolbaritem>
+ </toolbar>
+
+ <!-- name from children -->
+ <box id="box_children" role="button">14</box>
+
+ <!-- name from children, hidden children -->
+ <vbox role="listbox" tabindex="0">
+ <hbox id="lb_opt1_children_hidden" role="option" tabindex="0">
+ <description>i am visible</description>
+ <description style="display:none">i am hidden</description>
+ </hbox>
+
+ <!-- Name from label or sub tree -->
+ <groupbox id="groupbox">
+ <caption label="Some caption" />
+ <checkbox label="some checkbox label" />
+ </groupbox>
+ <groupbox id="groupbox2">
+ <caption><label>Some caption</label></caption>
+ <checkbox label="some checkbox label" />
+ </groupbox>
+ </vbox>
+
+ <!-- bug 441991; create name from other menuitem label listitem's own label -->
+ <hbox>
+ <listbox>
+ <listitem id="li_labelledby"
+ label="The moment the event starts"
+ aria-labelledby="menuitem-DISPLAY li_labelledby"/>
+ </listbox>
+ <menulist>
+ <menupopup>
+ <menuitem id="menuitem-DISPLAY"
+ value="DISPLAY"
+ label="Show an Alert"/>
+ <menuitem id="menuitem-EMAIL"
+ value="EMAIL"
+ label="Send an E-mail"/>
+ </menupopup>
+ </menulist>
+ </hbox>
+
+ </vbox> <!-- close tests area -->
+ </hbox> <!-- close main area -->
+</window>
+
diff --git a/accessible/tests/mochitest/name/test_link.html b/accessible/tests/mochitest/name/test_link.html
new file mode 100644
index 000000000..773e63731
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_link.html
@@ -0,0 +1,89 @@
+<html>
+
+<head>
+ <title>nsIAccessible::name calculation for HTML links (html:a)</title>
+
+ <link rel="stylesheet"
+ type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // aria-label
+ testName("aria_label", "anchor label");
+
+ // aria-labelledby
+ testName("aria_labelledby", "text");
+
+ // name from content
+ testName("namefromcontent", "1");
+
+ // name from content
+ testName("namefromimg", "img title");
+
+ // no name from content
+ testName("nonamefromcontent", null);
+
+ // title
+ testName("title", "title");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=459782"
+ title="nsIAccessible::name calculation for HTML links (html:a)">
+ Mozilla Bug 459782
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- aria-label -->
+ <a id="aria_label" href="mozilla.org"
+ aria-label="anchor label">1</a>
+ <br/>
+
+ <!-- aria-labelledby, preferred to html:label -->
+ <span id="text">text</span>
+ <label for="aria_labelledby">label</label>
+ <a id="aria_labelledby" href="mozilla.org"
+ aria-labelledby="text">1</a>
+ <br/>
+
+ <!-- name from content, preferred to @title -->
+ <a id="namefromcontent" href="mozilla.org"
+ title="title">1</a>
+ <br/>
+
+ <!-- name from content, preferred to @title -->
+ <a id="namefromimg" href="mozilla.org"
+ title="title"><img alt="img title" /></a>
+
+ <!-- no name from content, ARIA role overrides this rule -->
+ <a id="nonamefromcontent" href="mozilla.org" role="img">1</a>
+ <br/>
+
+ <!-- no content, name from @title -->
+ <a id="title" href="mozilla.org"
+ title="title"></a>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_list.html b/accessible/tests/mochitest/name/test_list.html
new file mode 100644
index 000000000..073b70dd2
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_list.html
@@ -0,0 +1,89 @@
+<html>
+
+<head>
+ <title>nsIAccessible::name calculation for HTML li</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Alter list item numbering and change list style type.
+ */
+ function bulletUpdate()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode("list"))
+ ];
+
+ this.invoke = function bulletUpdate_invoke()
+ {
+ testName("li_end", "1. list end");
+
+ var li = document.createElement("li");
+ li.setAttribute("id", "li_start");
+ li.textContent = "list start";
+ getNode("list").insertBefore(li, getNode("li_end"));
+ }
+
+ this.finalCheck = function bulletUpdate_finalCheck()
+ {
+ testName("li_start", "1. list start");
+ testName("li_end", "2. list end");
+
+ // change list style type
+ var list = getNode("list");
+ list.setAttribute("style", "list-style-type: disc;");
+ getComputedStyle(list, "").color; // make style processing sync
+
+ testName("li_start", kDiscBulletText + "list start");
+ testName("li_end", kDiscBulletText + "list end");
+ }
+
+ this.getID = function bulletUpdate_getID()
+ {
+ return "Update bullet of list items";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new bulletUpdate());
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=634200"
+ title="crash [@ nsIFrame::StyleVisibility() ]">
+ Mozilla Bug 634200
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ol id="list">
+ <li id="li_end">list end</li>
+ </ol>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_markup.html b/accessible/tests/mochitest/name/test_markup.html
new file mode 100644
index 000000000..7b478e0ba
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_markup.html
@@ -0,0 +1,60 @@
+<html>
+
+<head>
+ <title>nsIAccessible::name calculation for elements</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript"
+ src="markup.js"></script>
+
+ <script type="application/javascript">
+ // gA11yEventDumpID = "eventdump";
+ //gDumpToConsole = true;
+ //gA11yEventDumpToConsole = true;
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(testNames);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=459635"
+ title="nsIAccessible::name calculation for elements">
+ Bug 459635
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=666212"
+ title="summary attribute content mapped to accessible name in MSAA">
+ Bug 666212
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=786163"
+ title=" Sort out name calculation for HTML input buttons">
+ Bug 786163
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_svg.html b/accessible/tests/mochitest/name/test_svg.html
new file mode 100644
index 000000000..81dc0481e
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_svg.html
@@ -0,0 +1,55 @@
+<html>
+
+<head>
+ <title>Accessible name and description for SVG elements</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ testName("svg1", "A name");
+ testDescr("svg1", "A description");
+ testName("svg2", "A tooltip");
+ testDescr("svg2", "");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=459357"
+ title="Support accessible name computation for SVG">
+ Mozilla Bug 459357
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg1">
+ <title>A name</title>
+ <desc>A description</title>
+ </svg>
+
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2">
+ <desc>A tooltip</desc>
+ </svg>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/name/test_toolbaritem.xul b/accessible/tests/mochitest/name/test_toolbaritem.xul
new file mode 100644
index 000000000..8968964a9
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_toolbaritem.xul
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="general.css"
+ type="text/css"?>
+
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Name Calculating Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript">
+ <![CDATA[
+ var gQueue = null;
+ function doTest() {
+ let ids = [];
+ for (let item of ["button", "textbox"]) {
+ ids.push(item + "withtooltip");
+ ids.push(item + "withouttooltip");
+ ids.push("nested" + item + "withtooltip");
+ ids.push("nested" + item + "withouttooltip");
+ }
+
+ for (let id of ids) {
+ if (id.endsWith("withtooltip")) {
+ testName(id, id, id + " should have individual name from its tooltip - ");
+ } else {
+ testName(id, "Toolbaritem title", id + " should have toolbaritem's title for a name - ");
+ }
+ }
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1216478"
+ title="Items with tooltips inside items with a label should use their own tooltip as an accessible name, not the ancestor's label">
+ Mozilla Bug 1216478
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <toolbox>
+ <toolbar>
+ <toolbaritem title="Toolbaritem title">
+ <toolbarbutton id="buttonwithtooltip" tooltiptext="buttonwithtooltip"/>
+ <toolbarbutton id="buttonwithouttooltip"/>
+ <textbox id="textboxwithtooltip" tooltiptext="textboxwithtooltip"/>
+ <textbox id="textboxwithouttooltip"/>
+ <vbox>
+ <toolbarbutton id="nestedbuttonwithtooltip" tooltiptext="nestedbuttonwithtooltip"/>
+ <toolbarbutton id="nestedbuttonwithouttooltip"/>
+ <textbox id="nestedtextboxwithtooltip" tooltiptext="nestedtextboxwithtooltip"/>
+ <textbox id="nestedtextboxwithouttooltip"/>
+ </vbox>
+ </toolbaritem>
+ </toolbar>
+ </toolbox>
+
+
+ </vbox> <!-- close tests area -->
+ </hbox> <!-- close main area -->
+</window>
diff --git a/accessible/tests/mochitest/name/test_tree.xul b/accessible/tests/mochitest/name/test_tree.xul
new file mode 100644
index 000000000..fcb50d151
--- /dev/null
+++ b/accessible/tests/mochitest/name/test_tree.xul
@@ -0,0 +1,211 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="general.css"
+ type="text/css"?>
+
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Name Calculating Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function treeTester(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function treeTester_invoke()
+ {
+ this.DOMNode.view = new nsTreeTreeView();
+ }
+
+ this.check = function treeTester_check(aEvent)
+ {
+ var tree = {
+ role: ROLE_OUTLINE,
+ children: [
+ {
+ role: ROLE_LIST
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row1col"
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row2_col"
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row2.1_col"
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row2.2_col"
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row3_col"
+ },
+ {
+ role: ROLE_OUTLINEITEM,
+ children: [],
+ name: "row4col"
+ }
+ ]
+ };
+ testAccessibleTree(this.DOMNode, tree);
+ }
+
+ this.getID = function treeTester_getID()
+ {
+ return "Tree name testing for " + aID;
+ }
+ }
+
+ function tableTester(aID, aIsTable, aCol1ID, aCol2ID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function tableTester_invoke()
+ {
+ this.DOMNode.view = new nsTableTreeView(2);
+ }
+
+ this.check = function tableTester_check(aEvent)
+ {
+ var tree = {
+ role: aIsTable ? ROLE_TABLE : ROLE_TREE_TABLE,
+ children: [
+ {
+ role: ROLE_LIST
+ },
+ {
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_GRID_CELL,
+ children: [],
+ name: "row0_" + aCol1ID
+ },
+ {
+ role: ROLE_GRID_CELL,
+ children: [],
+ name: "row0_" + aCol2ID
+ }
+ ],
+ name: "row0_" + aCol1ID + " row0_" + aCol2ID
+ },
+ {
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_GRID_CELL,
+ children: [],
+ name: "row1_" + aCol1ID
+ },
+ {
+ role: ROLE_GRID_CELL,
+ children: [],
+ name: "row1_" + aCol2ID
+ }
+ ],
+ name: "row1_" + aCol1ID + " row1_" + aCol2ID
+ }
+ ]
+ };
+ testAccessibleTree(this.DOMNode, tree);
+ }
+
+ this.getID = function tableTester_getID()
+ {
+ return "Tree name testing for " + aID;
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ var gQueue = new eventQueue(EVENT_REORDER);
+
+ gQueue.push(new treeTester("tree"));
+ gQueue.push(new tableTester("table", true, "t_col1", "t_col2"));
+ gQueue.push(new tableTester("treetable", false, "tt_col1", "tt_col2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=546812"
+ title="Treegrid row accessible shouldn't inherit name from tree accessible">
+ Mozilla Bug 546812
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=664376"
+ title="Table rows of XUL trees no longer containing cell content as accessible name">
+ Mozilla Bug 664376
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="table" flex="1">
+ <treecols>
+ <treecol id="t_col1" flex="1" label="column"/>
+ <treecol id="t_col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treetable" flex="1">
+ <treecols>
+ <treecol id="tt_col1" flex="1" label="column" primary="true"/>
+ <treecol id="tt_col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ </vbox> <!-- close tests area -->
+ </hbox> <!-- close main area -->
+</window>
diff --git a/accessible/tests/mochitest/pivot.js b/accessible/tests/mochitest/pivot.js
new file mode 100644
index 000000000..7bb1d81ac
--- /dev/null
+++ b/accessible/tests/mochitest/pivot.js
@@ -0,0 +1,551 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+
+const PREFILTER_INVISIBLE = nsIAccessibleTraversalRule.PREFILTER_INVISIBLE;
+const PREFILTER_ARIA_HIDDEN = nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN;
+const PREFILTER_TRANSPARENT = nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT;
+const FILTER_MATCH = nsIAccessibleTraversalRule.FILTER_MATCH;
+const FILTER_IGNORE = nsIAccessibleTraversalRule.FILTER_IGNORE;
+const FILTER_IGNORE_SUBTREE = nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
+const CHAR_BOUNDARY = nsIAccessiblePivot.CHAR_BOUNDARY;
+const WORD_BOUNDARY = nsIAccessiblePivot.WORD_BOUNDARY;
+
+const NS_ERROR_NOT_IN_TREE = 0x80780026;
+const NS_ERROR_INVALID_ARG = 0x80070057;
+
+////////////////////////////////////////////////////////////////////////////////
+// Traversal rules
+
+/**
+ * Rule object to traverse all focusable nodes and text nodes.
+ */
+var HeadersTraversalRule =
+{
+ getMatchRoles: function(aRules)
+ {
+ aRules.value = [ROLE_HEADING];
+ return aRules.value.length;
+ },
+
+ preFilter: PREFILTER_INVISIBLE,
+
+ match: function(aAccessible)
+ {
+ return FILTER_MATCH;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+}
+
+/**
+ * Traversal rule for all focusable nodes or leafs.
+ */
+var ObjectTraversalRule =
+{
+ getMatchRoles: function(aRules)
+ {
+ aRules.value = [];
+ return 0;
+ },
+
+ preFilter: PREFILTER_INVISIBLE | PREFILTER_ARIA_HIDDEN | PREFILTER_TRANSPARENT,
+
+ match: function(aAccessible)
+ {
+ var rv = FILTER_IGNORE;
+ var role = aAccessible.role;
+ if (hasState(aAccessible, STATE_FOCUSABLE) &&
+ (role != ROLE_DOCUMENT && role != ROLE_INTERNAL_FRAME))
+ rv = FILTER_IGNORE_SUBTREE | FILTER_MATCH;
+ else if (aAccessible.childCount == 0 &&
+ role != ROLE_STATICTEXT && aAccessible.name.trim())
+ rv = FILTER_MATCH;
+
+ return rv;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([nsIAccessibleTraversalRule])
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Virtual state invokers and checkers
+
+/**
+ * A checker for virtual cursor changed events.
+ */
+function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod,
+ aIsFromUserInput)
+{
+ this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
+
+ this.match = function VCChangedChecker_match(aEvent)
+ {
+ var event = null;
+ try {
+ event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
+ } catch (e) {
+ return false;
+ }
+
+ var expectedReason = VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
+ nsIAccessiblePivot.REASON_NONE;
+
+ return event.reason == expectedReason;
+ };
+
+ this.check = function VCChangedChecker_check(aEvent)
+ {
+ SimpleTest.info("VCChangedChecker_check");
+
+ var event = null;
+ try {
+ event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
+ } catch (e) {
+ SimpleTest.ok(false, "Does not support correct interface: " + e);
+ }
+
+ var position = aDocAcc.virtualCursor.position;
+ var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
+ var nameMatches = position && position.name == aIdOrNameOrAcc;
+ var accMatches = position == aIdOrNameOrAcc;
+
+ SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
+ "expecting " + aIdOrNameOrAcc + ", got '" +
+ prettyName(position));
+
+ SimpleTest.is(aEvent.isFromUserInput, aIsFromUserInput,
+ "Expected user input is " + aIsFromUserInput + '\n');
+
+ if (aTextOffsets) {
+ SimpleTest.is(aDocAcc.virtualCursor.startOffset, aTextOffsets[0],
+ "wrong start offset");
+ SimpleTest.is(aDocAcc.virtualCursor.endOffset, aTextOffsets[1],
+ "wrong end offset");
+ }
+
+ var prevPosAndOffset = VCChangedChecker.
+ getPreviousPosAndOffset(aDocAcc.virtualCursor);
+
+ if (prevPosAndOffset) {
+ SimpleTest.is(event.oldAccessible, prevPosAndOffset.position,
+ "previous position does not match");
+ SimpleTest.is(event.oldStartOffset, prevPosAndOffset.startOffset,
+ "previous start offset does not match");
+ SimpleTest.is(event.oldEndOffset, prevPosAndOffset.endOffset,
+ "previous end offset does not match");
+ }
+ };
+}
+
+VCChangedChecker.prevPosAndOffset = {};
+
+VCChangedChecker.storePreviousPosAndOffset =
+ function storePreviousPosAndOffset(aPivot)
+{
+ VCChangedChecker.prevPosAndOffset[aPivot] =
+ {position: aPivot.position,
+ startOffset: aPivot.startOffset,
+ endOffset: aPivot.endOffset};
+};
+
+VCChangedChecker.getPreviousPosAndOffset =
+ function getPreviousPosAndOffset(aPivot)
+{
+ return VCChangedChecker.prevPosAndOffset[aPivot];
+};
+
+VCChangedChecker.methodReasonMap = {
+ 'moveNext': nsIAccessiblePivot.REASON_NEXT,
+ 'movePrevious': nsIAccessiblePivot.REASON_PREV,
+ 'moveFirst': nsIAccessiblePivot.REASON_FIRST,
+ 'moveLast': nsIAccessiblePivot.REASON_LAST,
+ 'setTextRange': nsIAccessiblePivot.REASON_TEXT,
+ 'moveNextByText': nsIAccessiblePivot.REASON_TEXT,
+ 'movePreviousByText': nsIAccessiblePivot.REASON_TEXT,
+ 'moveToPoint': nsIAccessiblePivot.REASON_POINT
+};
+
+/**
+ * Set a text range in the pivot and wait for virtual cursor change event.
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aTextAccessible [in] accessible to set to virtual cursor's position
+ * @param aTextOffsets [in] start and end offsets of text range to set in
+ * virtual cursor.
+ */
+function setVCRangeInvoker(aDocAcc, aTextAccessible, aTextOffsets)
+{
+ this.invoke = function virtualCursorChangedInvoker_invoke()
+ {
+ VCChangedChecker.
+ storePreviousPosAndOffset(aDocAcc.virtualCursor);
+ SimpleTest.info(prettyName(aTextAccessible) + " " + aTextOffsets);
+ aDocAcc.virtualCursor.setTextRange(aTextAccessible,
+ aTextOffsets[0],
+ aTextOffsets[1]);
+ };
+
+ this.getID = function setVCRangeInvoker_getID()
+ {
+ return "Set offset in " + prettyName(aTextAccessible) +
+ " to (" + aTextOffsets[0] + ", " + aTextOffsets[1] + ")";
+ };
+
+ this.eventSeq = [
+ new VCChangedChecker(aDocAcc, aTextAccessible, aTextOffsets, "setTextRange", true)
+ ];
+}
+
+/**
+ * Move the pivot and wait for virtual cursor change event.
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
+ * @param aRule [in] traversal rule object
+ * @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
+ * virtual cursor to land on after performing move method.
+ * false if no move is expected.
+ * @param aIsFromUserInput [in] set user input flag when invoking method, and
+ * expect it in the event.
+ */
+function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc,
+ aIsFromUserInput)
+{
+ var expectMove = (aIdOrNameOrAcc != false);
+ this.invoke = function virtualCursorChangedInvoker_invoke()
+ {
+ VCChangedChecker.
+ storePreviousPosAndOffset(aDocAcc.virtualCursor);
+ if (aPivotMoveMethod && aRule) {
+ var moved = false;
+ switch (aPivotMoveMethod) {
+ case 'moveFirst':
+ case 'moveLast':
+ moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule,
+ aIsFromUserInput === undefined ? true : aIsFromUserInput);
+ break;
+ case 'moveNext':
+ case 'movePrevious':
+ moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule,
+ aDocAcc.virtualCursor.position, false,
+ aIsFromUserInput === undefined ? true : aIsFromUserInput);
+ break;
+ }
+ SimpleTest.is(!!moved, !!expectMove,
+ "moved pivot with " + aPivotMoveMethod +
+ " to " + aIdOrNameOrAcc);
+ } else {
+ aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
+ }
+ };
+
+ this.getID = function setVCPosInvoker_getID()
+ {
+ return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
+ };
+
+ if (expectMove) {
+ this.eventSeq = [
+ new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, aPivotMoveMethod,
+ aIsFromUserInput === undefined ? !!aPivotMoveMethod : aIsFromUserInput)
+ ];
+ } else {
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
+ ];
+ }
+}
+
+/**
+ * Move the pivot by text and wait for virtual cursor change event.
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aPivotMoveMethod [in] method to test (ie. "moveNext", "moveFirst", etc.)
+ * @param aBoundary [in] boundary constant
+ * @param aTextOffsets [in] start and end offsets of text range to set in
+ * virtual cursor.
+ * @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
+ * virtual cursor to land on after performing move method.
+ * false if no move is expected.
+ * @param aIsFromUserInput [in] set user input flag when invoking method, and
+ * expect it in the event.
+ */
+function setVCTextInvoker(aDocAcc, aPivotMoveMethod, aBoundary, aTextOffsets,
+ aIdOrNameOrAcc, aIsFromUserInput)
+{
+ var expectMove = (aIdOrNameOrAcc != false);
+ this.invoke = function virtualCursorChangedInvoker_invoke()
+ {
+ VCChangedChecker.storePreviousPosAndOffset(aDocAcc.virtualCursor);
+ SimpleTest.info(aDocAcc.virtualCursor.position);
+ var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aBoundary,
+ aIsFromUserInput === undefined ? true : false);
+ SimpleTest.is(!!moved, !!expectMove,
+ "moved pivot by text with " + aPivotMoveMethod +
+ " to " + aIdOrNameOrAcc);
+ };
+
+ this.getID = function setVCPosInvoker_getID()
+ {
+ return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod + " in " +
+ prettyName(aIdOrNameOrAcc) + ", " + boundaryToString(aBoundary) +
+ ", [" + aTextOffsets + "]";
+ };
+
+ if (expectMove) {
+ this.eventSeq = [
+ new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod,
+ aIsFromUserInput === undefined ? true : aIsFromUserInput)
+ ];
+ } else {
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
+ ];
+ }
+}
+
+
+/**
+ * Move the pivot to the position under the point.
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aX [in] screen x coordinate
+ * @param aY [in] screen y coordinate
+ * @param aIgnoreNoMatch [in] don't unset position if no object was found at
+ * point.
+ * @param aRule [in] traversal rule object
+ * @param aIdOrNameOrAcc [in] id, accessible or accessible name to expect
+ * virtual cursor to land on after performing move method.
+ * false if no move is expected.
+ */
+function moveVCCoordInvoker(aDocAcc, aX, aY, aIgnoreNoMatch,
+ aRule, aIdOrNameOrAcc)
+{
+ var expectMove = (aIdOrNameOrAcc != false);
+ this.invoke = function virtualCursorChangedInvoker_invoke()
+ {
+ VCChangedChecker.
+ storePreviousPosAndOffset(aDocAcc.virtualCursor);
+ var moved = aDocAcc.virtualCursor.moveToPoint(aRule, aX, aY,
+ aIgnoreNoMatch);
+ SimpleTest.ok((expectMove && moved) || (!expectMove && !moved),
+ "moved pivot");
+ };
+
+ this.getID = function setVCPosInvoker_getID()
+ {
+ return "Do " + (expectMove ? "" : "no-op ") + "moveToPoint " + aIdOrNameOrAcc;
+ };
+
+ if (expectMove) {
+ this.eventSeq = [
+ new VCChangedChecker(aDocAcc, aIdOrNameOrAcc, null, 'moveToPoint', true)
+ ];
+ } else {
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
+ ];
+ }
+}
+
+/**
+ * Change the pivot modalRoot
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aModalRootAcc [in] accessible of the modal root, or null
+ * @param aExpectedResult [in] error result expected. 0 if expecting success
+ */
+function setModalRootInvoker(aDocAcc, aModalRootAcc, aExpectedResult)
+{
+ this.invoke = function setModalRootInvoker_invoke()
+ {
+ var errorResult = 0;
+ try {
+ aDocAcc.virtualCursor.modalRoot = aModalRootAcc;
+ } catch (x) {
+ SimpleTest.ok(
+ x.result, "Unexpected exception when changing modal root: " + x);
+ errorResult = x.result;
+ }
+
+ SimpleTest.is(errorResult, aExpectedResult,
+ "Did not get expected result when changing modalRoot");
+ };
+
+ this.getID = function setModalRootInvoker_getID()
+ {
+ return "Set modalRoot to " + prettyName(aModalRootAcc);
+ };
+
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc)
+ ];
+}
+
+/**
+ * Add invokers to a queue to test a rule and an expected sequence of element ids
+ * or accessible names for that rule in the given document.
+ *
+ * @param aQueue [in] event queue in which to push invoker sequence.
+ * @param aDocAcc [in] the managing document of the virtual cursor we are
+ * testing
+ * @param aRule [in] the traversal rule to use in the invokers
+ * @param aModalRoot [in] a modal root to use in this traversal sequence
+ * @param aSequence [in] a sequence of accessible names or element ids to expect
+ * with the given rule in the given document
+ */
+function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence)
+{
+ aDocAcc.virtualCursor.position = null;
+
+ // Add modal root (if any)
+ aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
+
+ aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
+
+ for (var i = 1; i < aSequence.length; i++) {
+ var invoker =
+ new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
+ aQueue.push(invoker);
+ }
+
+ // No further more matches for given rule, expect no virtual cursor changes.
+ aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
+
+ for (var i = aSequence.length-2; i >= 0; i--) {
+ var invoker =
+ new setVCPosInvoker(aDocAcc, "movePrevious", aRule, aSequence[i]);
+ aQueue.push(invoker);
+ }
+
+ // No previous more matches for given rule, expect no virtual cursor changes.
+ aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
+
+ aQueue.push(new setVCPosInvoker(aDocAcc, "moveLast", aRule,
+ aSequence[aSequence.length - 1]));
+
+ // No further more matches for given rule, expect no virtual cursor changes.
+ aQueue.push(new setVCPosInvoker(aDocAcc, "moveNext", aRule, false));
+
+ // set isFromUserInput to false, just to test..
+ aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0], false));
+
+ // No previous more matches for given rule, expect no virtual cursor changes.
+ aQueue.push(new setVCPosInvoker(aDocAcc, "movePrevious", aRule, false));
+
+ // Remove modal root (if any).
+ aQueue.push(new setModalRootInvoker(aDocAcc, null, 0));
+}
+
+/**
+ * A checker for removing an accessible while the virtual cursor is on it.
+ */
+function removeVCPositionChecker(aDocAcc, aHiddenParentAcc)
+{
+ this.__proto__ = new invokerChecker(EVENT_REORDER, aHiddenParentAcc);
+
+ this.check = function removeVCPositionChecker_check(aEvent) {
+ var errorResult = 0;
+ try {
+ aDocAcc.virtualCursor.moveNext(ObjectTraversalRule);
+ } catch (x) {
+ errorResult = x.result;
+ }
+ SimpleTest.is(
+ errorResult, NS_ERROR_NOT_IN_TREE,
+ "Expecting NOT_IN_TREE error when moving pivot from invalid position.");
+ };
+}
+
+/**
+ * Put the virtual cursor's position on an object, and then remove it.
+ *
+ * @param aDocAcc [in] document that manages the virtual cursor
+ * @param aPosNode [in] DOM node to hide after virtual cursor's position is
+ * set to it.
+ */
+function removeVCPositionInvoker(aDocAcc, aPosNode)
+{
+ this.accessible = getAccessible(aPosNode);
+ this.invoke = function removeVCPositionInvoker_invoke()
+ {
+ aDocAcc.virtualCursor.position = this.accessible;
+ aPosNode.parentNode.removeChild(aPosNode);
+ };
+
+ this.getID = function removeVCPositionInvoker_getID()
+ {
+ return "Bring virtual cursor to accessible, and remove its DOM node.";
+ };
+
+ this.eventSeq = [
+ new removeVCPositionChecker(aDocAcc, this.accessible.parent)
+ ];
+}
+
+/**
+ * A checker for removing the pivot root and then calling moveFirst, and
+ * checking that an exception is thrown.
+ */
+function removeVCRootChecker(aPivot)
+{
+ this.__proto__ = new invokerChecker(EVENT_REORDER, aPivot.root.parent);
+
+ this.check = function removeVCRootChecker_check(aEvent) {
+ var errorResult = 0;
+ try {
+ aPivot.moveLast(ObjectTraversalRule);
+ } catch (x) {
+ errorResult = x.result;
+ }
+ SimpleTest.is(
+ errorResult, NS_ERROR_NOT_IN_TREE,
+ "Expecting NOT_IN_TREE error when moving pivot from invalid position.");
+ };
+}
+
+/**
+ * Create a pivot, remove its root, and perform an operation where the root is
+ * needed.
+ *
+ * @param aRootNode [in] DOM node of which accessible will be the root of the
+ * pivot. Should have more than one child.
+ */
+function removeVCRootInvoker(aRootNode)
+{
+ this.pivot = gAccService.createAccessiblePivot(getAccessible(aRootNode));
+ this.invoke = function removeVCRootInvoker_invoke()
+ {
+ this.pivot.position = this.pivot.root.firstChild;
+ aRootNode.parentNode.removeChild(aRootNode);
+ };
+
+ this.getID = function removeVCRootInvoker_getID()
+ {
+ return "Remove root of pivot from tree.";
+ };
+
+ this.eventSeq = [
+ new removeVCRootChecker(this.pivot)
+ ];
+}
+
+/**
+ * A debug utility for writing proper sequences for queueTraversalSequence.
+ */
+function dumpTraversalSequence(aPivot, aRule)
+{
+ var sequence = [];
+ if (aPivot.moveFirst(aRule)) {
+ do {
+ sequence.push("'" + prettyName(aPivot.position) + "'");
+ } while (aPivot.moveNext(aRule))
+ }
+ SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
+}
diff --git a/accessible/tests/mochitest/pivot/a11y.ini b/accessible/tests/mochitest/pivot/a11y.ini
new file mode 100644
index 000000000..8add46094
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/a11y.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ doc_virtualcursor.html
+ doc_virtualcursor_text.html
+ !/accessible/tests/mochitest/*.js
+
+[test_virtualcursor.html]
+[test_virtualcursor_text.html]
diff --git a/accessible/tests/mochitest/pivot/doc_virtualcursor.html b/accessible/tests/mochitest/pivot/doc_virtualcursor.html
new file mode 100644
index 000000000..a456f2dfc
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/doc_virtualcursor.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Pivot test document</title>
+ <meta charset="utf-8" />
+</head>
+<body>
+ <h1 id="heading-1-1">Main Title</h1>
+ <h2 id="heading-2-1" aria-hidden="true">First Section Title</h2>
+ <p id="paragraph-1">
+ Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
+ leo, id <a href="#">semper</a> nulla.
+ </p>
+ <h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
+ <p id="paragraph-2" aria-hidden="">
+ Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
+ <p id="paragraph-3" aria-hidden="true">
+ <a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
+ You know, the <a href="#">singer-songwriter</a>.
+ </p>
+ <p style="opacity: 0;" id="paragraph-4">
+ This is completely transparent
+ </p>
+ <iframe
+ src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
+ </iframe>
+ <div id="hide-me">Hide me</div>
+ <p id="links" aria-hidden="false">
+ <a href="http://mozilla.org" title="Link 1 title">Link 1</a>
+ <a href="http://mozilla.org" title="Link 2 title">Link 2</a>
+ <a href="http://mozilla.org" title="Link 3 title">Link 3</a>
+ </p>
+ <ul>
+ <li>Hello<span> </span></li>
+ <li>World</li>
+ </ul>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html b/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html
new file mode 100644
index 000000000..aba87bbd8
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Pivot test document</title>
+ <meta charset="utf-8" />
+</head>
+<body>
+ <div id="start-block">This is the very beginning.</div>
+ <p id="paragraph-1">
+ This <b>is</b> <a id="p1-link-1" href="#">the</a> test of text.
+ </p>
+ <div id="section-1">A <a id="s1-link-1" href="#">multiword link</a> is here. <a id="s1-link-2" href="#">We</a> will traverse</div>
+ <div id="section-2">into, out, and between the subtrees.</div>
+ <p id="paragraph-2">Singularity.</p>
+ <table>
+ <tr>
+ <td id="cell-1">Magical</td>
+ <td id="cell-2">unicorns</td>
+ </tr>
+ <tr>
+ <td id="cell-3">and wizards</td>
+ <td id="cell-4">really exist.</td>
+ </tr>
+ </table>
+ <div id="section-3">Endless fun!</div>
+ <p id="paragraph-3">Objects<a id="p3-link-1" href="#">adjacent</a>to <a id="p3-link-2" href="#">each</a><a id="p3-link-3" href="#">other</a> should be separate.</p>
+ <div id="end-block">End!</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor.html b/accessible/tests/mochitest/pivot/test_virtualcursor.html
new file mode 100644
index 000000000..2fb339964
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests pivot functionality in virtual cursors</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ var gBrowserWnd = null;
+ var gQueue = null;
+
+ function doTest()
+ {
+ var rootAcc = getAccessible(browserDocument(), [nsIAccessibleDocument]);
+ ok(rootAcc.virtualCursor,
+ "root document does not have virtualCursor");
+
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+
+ // Test that embedded documents have their own virtual cursor.
+ is(docAcc.childDocumentCount, 1, "Expecting one child document");
+ ok(docAcc.getChildDocumentAt(0).virtualCursor,
+ "child document does not have virtualCursor");
+
+ gQueue = new eventQueue();
+
+ gQueue.onFinish = function onFinish()
+ {
+ closeBrowserWindow();
+ }
+
+ queueTraversalSequence(gQueue, docAcc, HeadersTraversalRule, null,
+ ['heading-1-1', 'heading-2-1', 'heading-2-2']);
+
+ queueTraversalSequence(
+ gQueue, docAcc, ObjectTraversalRule, null,
+ ['Main Title', 'Lorem ipsum ',
+ 'dolor', ' sit amet. Integer vitae urna leo, id ',
+ 'semper', ' nulla. ', 'Second Section Title',
+ 'Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.',
+ 'An ', 'embedded', ' document.', 'Hide me', 'Link 1', 'Link 2',
+ 'Link 3', 'Hello', 'World']);
+
+ // Just a random smoke test to see if our setTextRange works.
+ gQueue.push(
+ new setVCRangeInvoker(
+ docAcc,
+ getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText),
+ [2,6]));
+
+ gQueue.push(new removeVCPositionInvoker(
+ docAcc, doc.getElementById('hide-me')));
+
+ gQueue.push(new removeVCRootInvoker(
+ doc.getElementById('links')));
+
+ var [x, y] = getBounds(getAccessible(doc.getElementById('heading-1-1')));
+ gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
+ HeadersTraversalRule, 'heading-1-1'));
+
+ // Already on the point, so we should not get a move event.
+ gQueue.push(new moveVCCoordInvoker(docAcc, x + 1, y + 1, true,
+ HeadersTraversalRule, false));
+
+ // Attempting a coordinate outside any header, should not move.
+ gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, true,
+ HeadersTraversalRule, false));
+
+ // Attempting a coordinate outside any header, should move to null
+ gQueue.push(new moveVCCoordInvoker(docAcc, x - 1, y - 1, false,
+ HeadersTraversalRule, null));
+
+ queueTraversalSequence(
+ gQueue, docAcc, ObjectTraversalRule,
+ getAccessible(doc.getElementById('paragraph-1')),
+ ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
+ 'semper', ' nulla. ']);
+
+ gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
+ NS_ERROR_INVALID_ARG));
+
+ // Put cursor in an ignored subtree
+ // set isFromUserInput to false, just to test..
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById("hidden-link")),
+ false));
+ // Next item shoud be outside of that subtree
+ gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "An "));
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_virtualcursor.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Introduce virtual cursor/soft focus functionality to a11y API"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=698823">Mozilla Bug 698823</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor_text.html b/accessible/tests/mochitest/pivot/test_virtualcursor_text.html
new file mode 100644
index 000000000..1c11094fc
--- /dev/null
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor_text.html
@@ -0,0 +1,241 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests pivot functionality in virtual cursors</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../text.js"></script>
+ <script type="application/javascript" src="../browser.js"></script>
+ <script type="application/javascript" src="../events.js"></script>
+ <script type="application/javascript" src="../role.js"></script>
+ <script type="application/javascript" src="../states.js"></script>
+ <script type="application/javascript" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ var gBrowserWnd = null;
+ var gQueue = null;
+
+ function doTest()
+ {
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+
+ gQueue = new eventQueue();
+
+ gQueue.onFinish = function onFinish()
+ {
+ closeBrowserWindow();
+ }
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('paragraph-1'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [4,5],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [3,4],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [5,7],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3],
+ getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3],
+ getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
+ // set user input to false, and see if it works
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [5,7],
+ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)),
+ false);
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('section-1'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,1],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,9],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ // set user input to false, and see if it works
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText),
+ false));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,6],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,12],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,2],
+ getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [20,28],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5],
+ getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [6,10],
+ getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5],
+ getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [20,28],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [15,19],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2],
+ getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,12],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,6],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [10,14],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,9],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,1],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('s1-link-1'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [1,2],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [0,1],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [2,9],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [3,4],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [13,14],
+ getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('section-2'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [27,28],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1],
+ getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('paragraph-2'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,12],
+ getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('cell-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8],
+ getAccessible(doc.getElementById('cell-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3],
+ getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,11],
+ getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,6],
+ getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,13],
+ getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('section-3'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('section-3'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('section-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,13],
+ getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,6],
+ getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,11],
+ getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3],
+ getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8],
+ getAccessible(doc.getElementById('cell-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('cell-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,12],
+ getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('paragraph-3'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8],
+ getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [8,10],
+ getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
+ getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5],
+ getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [14,20],
+ getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5],
+ getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,4],
+ getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [8,10],
+ getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8],
+ getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7],
+ getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('s1-link-2'))));
+ // Start with the pivot in the middle of the paragraph
+ gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, " will traverse"));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19],
+ getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2],
+ getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
+
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('end-block'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
+ getAccessible(doc.getElementById('end-block'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, null, false));
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('start-block'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
+ getAccessible(doc.getElementById('start-block'), nsIAccessibleText)));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
+ gQueue.push(new setVCPosInvoker(docAcc, null, null,
+ getAccessible(doc.getElementById('start-block'))));
+ gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_virtualcursor_text.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Support Movement By Granularity"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=886076">Mozilla Bug 886076</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/relations.js b/accessible/tests/mochitest/relations.js
new file mode 100644
index 000000000..0d9aae2b1
--- /dev/null
+++ b/accessible/tests/mochitest/relations.js
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+
+var RELATION_CONTROLLED_BY = nsIAccessibleRelation.RELATION_CONTROLLED_BY;
+var RELATION_CONTROLLER_FOR = nsIAccessibleRelation.RELATION_CONTROLLER_FOR;
+var RELATION_DEFAULT_BUTTON = nsIAccessibleRelation.RELATION_DEFAULT_BUTTON;
+var RELATION_DESCRIBED_BY = nsIAccessibleRelation.RELATION_DESCRIBED_BY;
+var RELATION_DESCRIPTION_FOR = nsIAccessibleRelation.RELATION_DESCRIPTION_FOR;
+var RELATION_EMBEDDED_BY = nsIAccessibleRelation.RELATION_EMBEDDED_BY;
+var RELATION_EMBEDS = nsIAccessibleRelation.RELATION_EMBEDS;
+var RELATION_FLOWS_FROM = nsIAccessibleRelation.RELATION_FLOWS_FROM;
+var RELATION_FLOWS_TO = nsIAccessibleRelation.RELATION_FLOWS_TO;
+var RELATION_LABEL_FOR = nsIAccessibleRelation.RELATION_LABEL_FOR;
+var RELATION_LABELLED_BY = nsIAccessibleRelation.RELATION_LABELLED_BY;
+var RELATION_MEMBER_OF = nsIAccessibleRelation.RELATION_MEMBER_OF;
+var RELATION_NODE_CHILD_OF = nsIAccessibleRelation.RELATION_NODE_CHILD_OF;
+var RELATION_NODE_PARENT_OF = nsIAccessibleRelation.RELATION_NODE_PARENT_OF;
+var RELATION_PARENT_WINDOW_OF = nsIAccessibleRelation.RELATION_PARENT_WINDOW_OF;
+var RELATION_POPUP_FOR = nsIAccessibleRelation.RELATION_POPUP_FOR;
+var RELATION_SUBWINDOW_OF = nsIAccessibleRelation.RELATION_SUBWINDOW_OF;
+var RELATION_CONTAINING_DOCUMENT = nsIAccessibleRelation.RELATION_CONTAINING_DOCUMENT;
+var RELATION_CONTAINING_TAB_PANE = nsIAccessibleRelation.RELATION_CONTAINING_TAB_PANE;
+var RELATION_CONTAINING_APPLICATION = nsIAccessibleRelation.RELATION_CONTAINING_APPLICATION;
+const RELATION_DETAILS = nsIAccessibleRelation.RELATION_DETAILS;
+const RELATION_DETAILS_FOR = nsIAccessibleRelation.RELATION_DETAILS_FOR;
+const RELATION_ERRORMSG = nsIAccessibleRelation.RELATION_ERRORMSG;
+const RELATION_ERRORMSG_FOR = nsIAccessibleRelation.RELATION_ERRORMSG_FOR;
+
+////////////////////////////////////////////////////////////////////////////////
+// General
+
+/**
+ * Test the accessible relation.
+ *
+ * @param aIdentifier [in] identifier to get an accessible, may be ID
+ * attribute or DOM element or accessible object
+ * @param aRelType [in] relation type (see constants above)
+ * @param aRelatedIdentifiers [in] identifier or array of identifiers of
+ * expected related accessibles
+ */
+function testRelation(aIdentifier, aRelType, aRelatedIdentifiers)
+{
+ var relation = getRelationByType(aIdentifier, aRelType);
+
+ var relDescr = getRelationErrorMsg(aIdentifier, aRelType);
+ var relDescrStart = getRelationErrorMsg(aIdentifier, aRelType, true);
+
+ if (!relation || !relation.targetsCount) {
+ if (!aRelatedIdentifiers) {
+ ok(true, "No" + relDescr);
+ return;
+ }
+
+ var msg = relDescrStart + "has no expected targets: '" +
+ prettyName(aRelatedIdentifiers) + "'";
+
+ ok(false, msg);
+ return;
+
+ } else if (!aRelatedIdentifiers) {
+ ok(false, "There are unexpected targets of " + relDescr);
+ return;
+ }
+
+ var relatedIds = (aRelatedIdentifiers instanceof Array) ?
+ aRelatedIdentifiers : [aRelatedIdentifiers];
+
+ var targets = [];
+ for (var idx = 0; idx < relatedIds.length; idx++)
+ targets.push(getAccessible(relatedIds[idx]));
+
+ if (targets.length != relatedIds.length)
+ return;
+
+ var actualTargets = relation.getTargets();
+
+ // Check if all given related accessibles are targets of obtained relation.
+ for (var idx = 0; idx < targets.length; idx++) {
+ var isFound = false;
+ var enumerate = actualTargets.enumerate();
+ while (enumerate.hasMoreElements()) {
+ var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+ if (targets[idx] == relatedAcc) {
+ isFound = true;
+ break;
+ }
+ }
+
+ ok(isFound, prettyName(relatedIds[idx]) + " is not a target of" + relDescr);
+ }
+
+ // Check if all obtained targets are given related accessibles.
+ var enumerate = actualTargets.enumerate();
+ while (enumerate.hasMoreElements()) {
+ var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+ for (var idx = 0; idx < targets.length && relatedAcc != targets[idx]; idx++);
+
+ if (idx == targets.length)
+ ok(false, "There is unexpected target" + prettyName(relatedAcc) + "of" + relDescr);
+ }
+}
+
+/**
+ * Test that the given accessible relations don't exist.
+ *
+ * @param aIdentifier [in] identifier to get an accessible, may be ID
+ * attribute or DOM element or accessible object
+ * @param aRelType [in] relation type (see constants above)
+ * @param aUnrelatedIdentifiers [in] identifier or array of identifiers of
+ * accessibles that shouldn't exist for this
+ * relation.
+ */
+function testAbsentRelation(aIdentifier, aRelType, aUnrelatedIdentifiers)
+{
+ var relation = getRelationByType(aIdentifier, aRelType);
+
+ var relDescr = getRelationErrorMsg(aIdentifier, aRelType);
+ var relDescrStart = getRelationErrorMsg(aIdentifier, aRelType, true);
+
+ if (!aUnrelatedIdentifiers) {
+ ok(false, "No identifiers given for unrelated accessibles.");
+ return;
+ }
+
+ if (!relation || !relation.targetsCount) {
+ ok(true, "No relations exist.");
+ return;
+ }
+
+ var relatedIds = (aUnrelatedIdentifiers instanceof Array) ?
+ aUnrelatedIdentifiers : [aUnrelatedIdentifiers];
+
+ var targets = [];
+ for (var idx = 0; idx < relatedIds.length; idx++)
+ targets.push(getAccessible(relatedIds[idx]));
+
+ if (targets.length != relatedIds.length)
+ return;
+
+ var actualTargets = relation.getTargets();
+
+ // Any found targets that match given accessibles should be called out.
+ for (var idx = 0; idx < targets.length; idx++) {
+ var notFound = true;
+ var enumerate = actualTargets.enumerate();
+ while (enumerate.hasMoreElements()) {
+ var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
+ if (targets[idx] == relatedAcc) {
+ notFound = false;
+ break;
+ }
+ }
+
+ ok(notFound, prettyName(relatedIds[idx]) + " is a target of " + relDescr);
+ }
+}
+
+/**
+ * Return related accessible for the given relation type.
+ *
+ * @param aIdentifier [in] identifier to get an accessible, may be ID attribute
+ * or DOM element or accessible object
+ * @param aRelType [in] relation type (see constants above)
+ */
+function getRelationByType(aIdentifier, aRelType)
+{
+ var acc = getAccessible(aIdentifier);
+ if (!acc)
+ return;
+
+ var relation = null;
+ try {
+ relation = acc.getRelationByType(aRelType);
+ } catch (e) {
+ ok(false, "Can't get" + getRelationErrorMsg(aIdentifier, aRelType));
+ }
+
+ return relation;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private implementation details
+
+function getRelationErrorMsg(aIdentifier, aRelType, aIsStartSentence)
+{
+ var relStr = relationTypeToString(aRelType);
+ var msg = aIsStartSentence ? "Relation of '" : " relation of '";
+ msg += relStr + "' type for '" + prettyName(aIdentifier) + "'";
+ msg += aIsStartSentence ? " " : ".";
+
+ return msg;
+}
diff --git a/accessible/tests/mochitest/relations/a11y.ini b/accessible/tests/mochitest/relations/a11y.ini
new file mode 100644
index 000000000..a2da0cf2e
--- /dev/null
+++ b/accessible/tests/mochitest/relations/a11y.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_bindings.xhtml]
+[test_embeds.xul]
+[test_general.html]
+[test_general.xul]
+[test_tabbrowser.xul]
+[test_tree.xul]
+[test_ui_modalprompt.html]
+[test_update.html]
diff --git a/accessible/tests/mochitest/relations/test_bindings.xhtml b/accessible/tests/mochitest/relations/test_bindings.xhtml
new file mode 100644
index 000000000..65a7a0875
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_bindings.xhtml
@@ -0,0 +1,103 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>Accessible relations for bindings</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ .button {
+ -moz-binding: url('#custombutton');
+ }
+
+ .button2 {
+ -moz-binding: url('#custombutton2');
+ }
+ </style>
+
+ <bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="custombutton">
+ <content aria-labelledby="button.label label">
+ <label xmlns="http://www.w3.org/1999/xhtml" anonid="button.label">
+ anon label
+ </label>
+ <button xmlns="http://www.w3.org/1999/xhtml" anonid="button.button"
+ aria-labelledby="button.label label">
+ a button
+ </button>
+ <div xmlns="http://www.w3.org/1999/xhtml"
+ anonid="button.button2" class="button2"
+ aria-labelledby="button.label"></div>
+ <div xmlns="http://www.w3.org/1999/xhtml"
+ anonid="button.button3" class="button2"></div>
+ </content>
+ </binding>
+ <binding id="custombutton2">
+ <content aria-labelledby="button2.label">
+ <label xmlns="http://www.w3.org/1999/xhtml" anonid="button2.label">
+ nested anon label
+ </label>
+ </content>
+ </binding>
+ </bindings>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+
+ <script type="application/javascript">
+ function doTests()
+ {
+ var button = document.getElementById("button");
+ var anonLabel = document.
+ getAnonymousElementByAttribute(button, "anonid", "button.label");
+ var anonButton = document.
+ getAnonymousElementByAttribute(button, "anonid", "button.button");
+ var anonButton2 = document.
+ getAnonymousElementByAttribute(button, "anonid", "button.button2");
+ var anonButton3 = document.
+ getAnonymousElementByAttribute(button, "anonid", "button.button3");
+ var anonAnonLabel = document.
+ getAnonymousElementByAttribute(anonButton3, "anonid", "button2.label");
+
+ testRelation("label", RELATION_LABEL_FOR, button);
+ testRelation(anonLabel, RELATION_LABEL_FOR, [button, anonButton, anonButton2]);
+ testRelation(button, RELATION_LABELLED_BY, [anonLabel, "label"]);
+ testRelation(anonButton, RELATION_LABELLED_BY, anonLabel);
+ testRelation(anonButton2, RELATION_LABELLED_BY, anonLabel);
+ testRelation(anonButton3, RELATION_LABELLED_BY, anonAnonLabel);
+ testRelation(anonAnonLabel, RELATION_LABEL_FOR, anonButton3);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=421242"
+ title="Allow relations in anonymous content for binding parent">
+ Mozilla Bug 421242
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <label id="label">explicit label</label>
+ <div id="button" class="button"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/relations/test_embeds.xul b/accessible/tests/mochitest/relations/test_embeds.xul
new file mode 100644
index 000000000..0cb6d6c65
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_embeds.xul
@@ -0,0 +1,122 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Embeds relation tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadURI(aURI)
+ {
+ this.invoke = function loadURI_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
+ ];
+
+ this.finalCheck = function loadURI_finalCheck()
+ {
+ testRelation(browserDocument(), RELATION_EMBEDS,
+ getAccessible(currentTabDocument()));
+ }
+
+ this.getID = function loadURI_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ function loadOneTab(aURI)
+ {
+ this.invoke = function loadOneTab_invoke()
+ {
+ tabBrowser().loadOneTab(aURI, null, null, null, false);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
+ ];
+
+ this.finalCheck = function loadURI_finalCheck()
+ {
+ testRelation(browserDocument(), RELATION_EMBEDS,
+ getAccessible(currentTabDocument()));
+ }
+
+ this.getID = function loadOneTab_getID()
+ {
+ return "load uri '" + aURI + "' in new tab";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Testing
+
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+ function doTests()
+ {
+ testRelation(browserDocument(), RELATION_EMBEDS,
+ getAccessible(currentTabDocument()));
+
+ enableLogging("docload");
+ gQueue = new eventQueue();
+
+ gQueue.push(new loadURI("about:about"));
+ gQueue.push(new loadOneTab("about:mozilla"));
+
+ gQueue.onFinish = function()
+ {
+ disableLogging();
+ closeBrowserWindow();
+ }
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, "about:");
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
+ title="Embeds relation on root accessible can return not content document">
+ Mozilla Bug 707654
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/relations/test_general.html b/accessible/tests/mochitest/relations/test_general.html
new file mode 100644
index 000000000..bcb1d282a
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -0,0 +1,406 @@
+<html>
+
+<head>
+ <title>nsIAccessible::getAccessibleRelated() tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // html:label@for
+ testRelation("label1_1", RELATION_LABEL_FOR, "control1_1");
+ testRelation("control1_1", RELATION_LABELLED_BY, "label1_1");
+
+ // html:label@for, multiple
+ testRelation("label1_2", RELATION_LABEL_FOR, "control1_2");
+ testRelation("label1_3", RELATION_LABEL_FOR, "control1_2");
+ testRelation("control1_2", RELATION_LABELLED_BY,
+ [ "label1_2", "label1_3" ]);
+
+ // ancestor html:label (implicit association)
+ testRelation("label1_4", RELATION_LABEL_FOR, "control1_4");
+ testRelation("control1_4", RELATION_LABELLED_BY, "label1_4");
+ testRelation("control1_4_option1", RELATION_LABELLED_BY, null);
+ testRelation("label1_5", RELATION_LABEL_FOR, "control1_5");
+ testRelation("control1_5", RELATION_LABELLED_BY, "label1_5");
+ testRelation("label1_6", RELATION_LABEL_FOR, "control1_6");
+ testRelation("control1_6", RELATION_LABELLED_BY, "label1_6");
+ testRelation("label1_7", RELATION_LABEL_FOR, "control1_7");
+ testRelation("control1_7", RELATION_LABELLED_BY, "label1_7");
+ testRelation("label1_8", RELATION_LABEL_FOR, "control1_8");
+ testRelation("control1_8", RELATION_LABELLED_BY, "label1_8");
+ testRelation("label1_9", RELATION_LABEL_FOR, "control1_9");
+ testRelation("control1_9", RELATION_LABELLED_BY, "label1_9");
+ testRelation("label1_10", RELATION_LABEL_FOR, "control1_10");
+ testRelation("control1_10", RELATION_LABELLED_BY, "label1_10");
+ testRelation("label1_11", RELATION_LABEL_FOR, "control1_11");
+ testRelation("control1_11", RELATION_LABELLED_BY, "label1_11");
+ testRelation("label1_12", RELATION_LABEL_FOR, "control1_12");
+ testRelation("control1_12", RELATION_LABELLED_BY, "label1_12");
+
+ testRelation("label1_13", RELATION_LABEL_FOR, null);
+ testRelation("control1_13", RELATION_LABELLED_BY, null);
+ testRelation("control1_14", RELATION_LABELLED_BY, "label1_14");
+
+ // aria-labelledby
+ testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
+ testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
+
+ // aria-labelledby, multiple relations
+ testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
+ testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
+ testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
+
+ // aria-describedby
+ testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox4");
+ testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr1");
+
+ // aria-describedby, multiple relations
+ testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
+ testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
+ testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
+
+ // aria_owns, multiple relations
+ testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
+
+ // 'node child of' relation for outlineitem role
+ testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
+ testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
+ testRelation("tree2_ti1", RELATION_NODE_CHILD_OF, "tree2");
+ testRelation("tree2_ti1a", RELATION_NODE_CHILD_OF, "tree2_ti1");
+ testRelation("tree2_ti1b", RELATION_NODE_CHILD_OF, "tree2_ti1");
+
+ // 'node child of' relation for row role in grid.
+ // Relation for row associated using aria-level should exist.
+ testRelation("simplegrid-row3", RELATION_NODE_CHILD_OF,
+ "simplegrid-row2");
+ // Relations for hierarchical children elements shouldn't exist.
+ testAbsentRelation("simplegrid-row1", RELATION_NODE_CHILD_OF,
+ "simplegrid");
+ testAbsentRelation("simplegrid-row2", RELATION_NODE_CHILD_OF,
+ "simplegrid");
+
+ // 'node child of' relation for row role of treegrid
+ testRelation("treegridrow1", RELATION_NODE_CHILD_OF, "treegrid");
+ testRelation("treegridrow2", RELATION_NODE_CHILD_OF, "treegrid");
+ testRelation("treegridrow3", RELATION_NODE_CHILD_OF, "treegridrow2");
+
+ // 'node child of' relation for lists organized by groups
+ testRelation("listitem1", RELATION_NODE_CHILD_OF, "list");
+ testRelation("listitem1.1", RELATION_NODE_CHILD_OF, "listitem1");
+ testRelation("listitem1.2", RELATION_NODE_CHILD_OF, "listitem1");
+
+ // 'node child of' relation for the document having window, returns
+ // direct accessible parent (fixed in bug 419770).
+ var iframeElmObj = {};
+ var iframeAcc = getAccessible("iframe", null, iframeElmObj);
+ var iframeDoc = iframeElmObj.value.contentDocument;
+ var iframeDocAcc = getAccessible(iframeDoc);
+ testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
+
+ // 'node parent of' relation on ARIA tree and treegrid.
+ testRelation("tree", RELATION_NODE_PARENT_OF,
+ ["treeitem1", "treeitem2", // aria-owns
+ "treeitem3", "treeitem4", "treeitem6"]); // children
+ testRelation("treeitem4", RELATION_NODE_PARENT_OF,
+ "treeitem5"); // aria-level
+ testRelation("treeitem6", RELATION_NODE_PARENT_OF,
+ "treeitem7"); // // group role
+ testRelation("tree2", RELATION_NODE_PARENT_OF, "tree2_ti1"); // group role
+ testRelation("tree2_ti1", RELATION_NODE_PARENT_OF,
+ ["tree2_ti1a", "tree2_ti1b"]); // group role
+
+ testRelation("treegridrow2", RELATION_NODE_PARENT_OF, "treegridrow3");
+ testRelation("treegrid", RELATION_NODE_PARENT_OF,
+ ["treegridrow1", "treegridrow2"]);
+
+ // 'node parent of' relation on ARIA grid.
+ // 'node parent of' relation on ARIA grid's row.
+ // Should only have relation to child through aria-level.
+ testRelation("simplegrid-row2", RELATION_NODE_PARENT_OF,
+ "simplegrid-row3");
+
+ // 'node parent of' relation on ARIA list structured by groups
+ testRelation("list", RELATION_NODE_PARENT_OF,
+ "listitem1");
+ testRelation("listitem1", RELATION_NODE_PARENT_OF,
+ [ "listitem1.1", "listitem1.2" ]);
+
+ // aria-atomic
+ testRelation(getNode("atomic").firstChild, RELATION_MEMBER_OF, "atomic");
+
+ // aria-controls
+ getAccessible("tab");
+ todo(false,
+ "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
+ testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
+ testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
+
+ // aria-controls, multiple relations
+ testRelation("lr1", RELATION_CONTROLLED_BY, "button");
+ testRelation("lr2", RELATION_CONTROLLED_BY, "button");
+ testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
+
+ // aria-flowto
+ testRelation("flowto", RELATION_FLOWS_TO, "flowfrom");
+ testRelation("flowfrom", RELATION_FLOWS_FROM, "flowto");
+
+ // aria-flowto, multiple relations
+ testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
+ testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
+ testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
+
+ // 'default button' relation
+ testRelation("input", RELATION_DEFAULT_BUTTON, "submit");
+
+ // output 'for' relations
+ testRelation("output", RELATION_CONTROLLED_BY, ["input", "input2"]);
+ testRelation("output2", RELATION_CONTROLLED_BY, ["input", "input2"]);
+ testRelation("input", RELATION_CONTROLLER_FOR, ["output", "output2"]);
+ testRelation("input2", RELATION_CONTROLLER_FOR, ["output", "output2"]);
+
+ // 'described by'/'description for' relation for html:table and
+ // html:caption
+ testRelation("caption", RELATION_LABEL_FOR, "table");
+ testRelation("table", RELATION_LABELLED_BY, "caption");
+
+ // 'labelled by'/'label for' relation for html:fieldset and
+ // html:legend
+ testRelation("legend", RELATION_LABEL_FOR, "fieldset");
+ testRelation("fieldset", RELATION_LABELLED_BY, "legend");
+
+ // containing relations
+ testRelation("control1_1", RELATION_CONTAINING_DOCUMENT, document);
+ testRelation("control1_1", RELATION_CONTAINING_TAB_PANE, getTabDocAccessible("control1_1"));
+ testRelation("control1_1", RELATION_CONTAINING_APPLICATION, getApplicationAccessible());
+
+ // details
+ testRelation("has_details", RELATION_DETAILS, "details");
+ testRelation("details", RELATION_DETAILS_FOR, "has_details");
+
+ // error
+ testRelation("has_error", RELATION_ERRORMSG, "error");
+ testRelation("error", RELATION_ERRORMSG_FOR, "has_error");
+
+ // finish test
+ SimpleTest.finish();
+ }
+
+ disableLogging(); // from test_embeds.xul
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
+ title="mochitests for accessible relations">
+ Bug 475298
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
+ title="Implement RELATION_NODE_PARENT_OF">
+ Bug 527461
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+ title="make HTML <output> accessible">
+ Bug 558036
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=682790"
+ title="Ignore implicit label association when it's associated explicitly">
+ Bug 682790
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=687393"
+ title="HTML select options gets relation from containing label">
+ Bug 687393
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=864224"
+ title="Support nested ARIA listitems structured by role='group'">
+ Bug 864224
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <label id="label1_1" for="control1_1">label</label>
+ <input id="control1_1">
+
+ <label id="label1_2" for="control1_2">label</label>
+ <label id="label1_3" for="control1_2">label</label>
+ <input id="control1_2">
+
+ <label id="label1_4">Label
+ <select id="control1_4">
+ <option id="control1_4_option1">option</option>
+ </select>
+ </label>
+ <label id="label1_5">Label
+ <button id="control1_5">button</button>
+ </label>
+ <label id="label1_6">Label
+ <input id="control1_6">
+ </label>
+ <label id="label1_7">Label
+ <input id="control1_7" type="checkbox">
+ </label>
+ <label id="label1_8">Label
+ <input id="control1_8" type="radio">
+ </label>
+ <label id="label1_9">Label
+ <input id="control1_9" type="button" value="button">
+ </label>
+ <label id="label1_10">Label
+ <input id="control1_10" type="submit">
+ </label>
+ <label id="label1_11">Label
+ <input id="control1_11" type="image">
+ </label>
+ <label id="label1_12">Label
+ <progress id="control1_12"></progress>
+ </label>
+
+ <label id="label1_13" for="">Label
+ <input id="control1_13">
+ </label>
+ <label id="label1_14" for="control1_14">Label
+ <input id="control1_14">
+ </label>
+
+ <span id="label2">label</span>
+ <span role="checkbox" id="checkbox2" aria-labelledby="label2"></span>
+
+ <span id="label3">label1</span>
+ <span id="label4">label2</span>
+ <span role="checkbox" id="checkbox3" aria-labelledby="label3 label4"></span>
+
+ <span id="descr1">description</span>
+ <span role="checkbox" id="checkbox4" aria-describedby="descr1"></span>
+
+ <span id="descr2">description1</span>
+ <span id="descr3">description2</span>
+ <span role="checkbox" id="checkbox5" aria-describedby="descr2 descr3"></span>
+
+ <div role="treeitem" id="treeitem1">Yellow</div>
+ <div role="treeitem" id="treeitem2">Orange</div>
+ <div id="tree" role="tree" aria-owns="treeitem1 treeitem2">
+ <div role="treeitem" id="treeitem3">Blue</div>
+ <div role="treeitem" id="treeitem4" aria-level="1">Green</div>
+ <div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
+ <div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
+ <div role="group">
+ <div role="treeitem" id="treeitem7">Super light green</div>
+ </div>
+ </div>
+
+ <div role="grid" id="simplegrid">
+ <div role="row" id="simplegrid-row1" aria-level="1">
+ <div role="gridcell">cell 1,1</div>
+ <div role="gridcell">cell 1,2</div>
+ </div>
+ <div role="row" id="simplegrid-row2" aria-level="1">
+ <div role="gridcell">cell 2,1</div>
+ <div role="gridcell">cell 2,2</div>
+ </div>
+ <div role="row" id="simplegrid-row3" aria-level="2">
+ <div role="gridcell">cell 3,1</div>
+ <div role="gridcell">cell 3,2</div>
+ </div>
+ </div>
+
+ <ul role="tree" id="tree2">
+ <li role="treeitem" id="tree2_ti1">Item 1
+ <ul role="group">
+ <li role="treeitem" id="tree2_ti1a">Item 1A</li>
+ <li role="treeitem" id="tree2_ti1b">Item 1B</li>
+ </ul>
+ </li>
+ </ul>
+
+ <div role="treegrid" id="treegrid">
+ <div role="row" id="treegridrow1">
+ <span role="gridcell">cell1</span><span role="gridcell">cell2</span>
+ </div>
+ <div role="row" id="treegridrow2" aria-level="1">
+ <span role="gridcell">cell3</span><span role="gridcell">cell4</span>
+ </div>
+ <div role="row" id="treegridrow3" aria-level="2">
+ <span role="gridcell">cell5</span><span role="gridcell">cell6</span>
+ </div>
+ </div>
+
+ <div role="list" id="list">
+ <div role="listitem" id="listitem1">Item 1
+ <div role="group">
+ <div role="listitem" id="listitem1.1">Item 1A</div>
+ <div role="listitem" id="listitem1.2">Item 1B</div>
+ </div>
+ </div>
+ </div>
+
+ <iframe id="iframe"></iframe>
+
+ <div id="tablist" role="tablist">
+ <div id="tab" role="tab" aria-controls="tabpanel">tab</div>
+ </div>
+ <div id="tabpanel" role="tabpanel">tabpanel</div>
+
+ <div id="lr1" aria-live="assertive">1</div>
+ <div id="lr2" aria-live="assertive">a</div>
+ <input type="button" id="button" aria-controls="lr1 lr2"
+ onclick="getNode('lr1').textContent += '1'; getNode('lr2').textContent += 'a';"/>
+
+ <div id="atomic" aria-atomic="true">live region</div>
+
+ <span id="flowto" aria-flowto="flowfrom">flow to</span>
+ <span id="flowfrom">flow from</span>
+
+ <span id="flowto1" aria-flowto="flowfrom1 flowfrom2">flow to</span>
+ <span id="flowfrom1">flow from</span>
+ <span id="flowfrom2">flow from</span>
+
+ <form id="form">
+ <input id="input" />
+ <input id="input2" />
+ <input type="submit" id="submit" />
+ <output id="output" style="display:block" for="input input2"></output>
+ <output id="output2" for="input input2"></output>
+ </form>
+
+ <table id="table">
+ <caption id="caption">tabple caption</caption>
+ <tr>
+ <td>cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <fieldset id="fieldset">
+ <legend id="legend">legend</legend>
+ <input />
+ </fieldset>
+
+ <input id="has_details" aria-details="details"><section id="details"></section>
+ <input id="has_error" aria-errormessage="error"><section id="error"></section>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/relations/test_general.xul b/accessible/tests/mochitest/relations/test_general.xul
new file mode 100644
index 000000000..23bf7276b
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -0,0 +1,238 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessible::getAccessibleRelated() tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../relations.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // xul:label@control
+ testRelation("label1", RELATION_LABEL_FOR, "checkbox1");
+ testRelation("checkbox1", RELATION_LABELLED_BY, "label1");
+
+ // xul:label@control, multiple
+ testRelation("label1_1", RELATION_LABEL_FOR, "checkbox1_1");
+ testRelation("label1_2", RELATION_LABEL_FOR, "checkbox1_1");
+ testRelation("checkbox1_1", RELATION_LABELLED_BY,
+ [ "label1_1", "label1_2" ]);
+
+ // aria-labelledby
+ testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
+ testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
+
+ // aria-labelledby, multiple relations
+ testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
+ testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
+ testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
+
+ // aria-describedby
+ testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox4");
+ testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr1");
+
+ // aria-describedby, multiple relations
+ testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
+ testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
+ testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
+
+ // xul:description@control
+ testRelation("descr4", RELATION_DESCRIPTION_FOR, "checkbox6");
+ testRelation("checkbox6", RELATION_DESCRIBED_BY, "descr4");
+
+ // xul:description@control, multiple
+ testRelation("descr5", RELATION_DESCRIPTION_FOR, "checkbox7");
+ testRelation("descr6", RELATION_DESCRIPTION_FOR, "checkbox7");
+ testRelation("checkbox7", RELATION_DESCRIBED_BY,
+ [ "descr5", "descr6" ]);
+
+ // aria_owns, multiple relations
+ testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
+
+ // 'node child of' relation for outlineitem role
+ testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
+ testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
+
+ // no relation node_child_of for accessible contained in an unexpected
+ // parent
+ testRelation("treeitem6", RELATION_NODE_CHILD_OF, null);
+
+ // 'node child of' relation for the document having window, returns
+ // direct accessible parent (fixed in bug 419770).
+ var iframeElmObj = {};
+ var iframeAcc = getAccessible("iframe", null, iframeElmObj);
+ var iframeDoc = iframeElmObj.value.contentDocument;
+ var iframeDocAcc = getAccessible(iframeDoc);
+ testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
+
+ // aria-controls
+ getAccessible("tab");
+ todo(false,
+ "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that.");
+ testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
+ testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
+
+ // aria-controls, multiple relations
+ testRelation("lr1", RELATION_CONTROLLED_BY, "button");
+ testRelation("lr2", RELATION_CONTROLLED_BY, "button");
+ testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
+
+ // aria-flowto
+ testRelation("flowto", RELATION_FLOWS_TO, "flowfrom");
+ testRelation("flowfrom", RELATION_FLOWS_FROM, "flowto");
+
+ // aria-flowto, multiple relations
+ testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
+ testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
+ testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
+
+ // 'default button' relation
+ testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
+
+ // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
+ // xul:caption
+ var groupboxAcc = getAccessible("groupbox");
+ var labelAcc = groupboxAcc.firstChild;
+ testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc);
+ testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc);
+
+ // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
+ // (fixed in bug 366527)
+ testRelation("tabpanel1", RELATION_LABELLED_BY, "tab1");
+ testRelation("tab1", RELATION_LABEL_FOR, "tabpanel1");
+ testRelation("tabpanel2", RELATION_LABELLED_BY, "tab2");
+ testRelation("tab2", RELATION_LABEL_FOR, "tabpanel2");
+ testRelation("tabpanel3", RELATION_LABELLED_BY, "tab3");
+ testRelation("tab3", RELATION_LABEL_FOR, "tabpanel3");
+
+ // finish test
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <vbox style="overflow: auto;" flex="1">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
+ title="mochitests for accessible relations">
+ Mozilla Bug 475298
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673389"
+ title="node_child_of on an item not in a proper container">
+ Mozilla Bug 67389
+ </a><br/>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <label id="label1" control="checkbox1">label</label>
+ <checkbox id="checkbox1"/>
+
+ <label id="label1_1" control="checkbox1_1">label</label>
+ <label id="label1_2" control="checkbox1_1">label</label>
+ <checkbox id="checkbox1_1"/>
+
+ <description id="label2">label</description>
+ <description role="checkbox" id="checkbox2" aria-labelledby="label2"/>
+
+ <description id="label3">label</description>
+ <description id="label4">label</description>
+ <description role="checkbox" id="checkbox3"
+ aria-labelledby="label3 label4"/>
+
+ <description id="descr1">description</description>
+ <description role="checkbox" id="checkbox4" aria-describedby="descr1"/>
+
+ <description id="descr2">label</description>
+ <description id="descr3">label</description>
+ <description role="checkbox" id="checkbox5"
+ aria-describedby="descr2 descr3"/>
+
+ <description id="descr4" control="checkbox6">description</description>
+ <checkbox id="checkbox6"/>
+
+ <description id="descr5" control="checkbox7">description</description>
+ <description id="descr6" control="checkbox7">description</description>
+ <checkbox id="checkbox7"/>
+
+ <description role="treeitem" id="treeitem1">Yellow</description>
+ <description role="treeitem" id="treeitem2">Orange</description>
+ <vbox id="tree" role="tree" aria-owns="treeitem1 treeitem2">
+ <description role="treeitem" id="treeitem3">Blue</description>
+ <description role="treeitem" id="treeitem4" aria-level="1">Green</description>
+ <description role="treeitem" id="treeitem5" aria-level="2">Light green</description>
+ </vbox>
+
+ <description role="treeitem" id="treeitem6">Dark green</description>
+
+ <iframe id="iframe"/>
+
+ <hbox id="tablist" role="tablist">
+ <description id="tab" role="tab" aria-controls="tabpanel">tab</description>
+ </hbox>
+ <description id="tabpanel" role="tabpanel">tabpanel</description>
+
+ <description id="lr1" aria-live="assertive">1</description>
+ <description id="lr2" aria-live="assertive">a</description>
+ <button id="button" aria-controls="lr1 lr2" label="button"
+ oncommand="getNode('lr1').textContent += '1'; getNode('lr2').textContent += 'a';"/>
+
+ <description id="flowto1" aria-flowto="flowfrom1 flowfrom2">flow to</description>
+ <description id="flowfrom1">flow from</description>
+ <description id="flowfrom2">flow from</description>
+
+ <description id="flowto" aria-flowto="flowfrom">flow to</description>
+ <description id="flowfrom">flow from</description>
+
+ <textbox id="textbox"/>
+ <button id="submit" default="true" label="Default"/>
+
+ <groupbox id="groupbox">
+ <caption label="caption"/>
+ </groupbox>
+
+ <tabbox>
+ <tabs>
+ <tab label="tab1" id="tab1"/>
+ <tab label="tab2" id="tab2" linkedpanel="tabpanel2"/>
+ <tab label="tab3" id="tab3" linkedpanel="tabpanel3"/>
+ </tabs>
+ <tabpanels>
+ <tabpanel id="tabpanel1">
+ <description>tabpanel1</description>
+ </tabpanel>
+ <tabpanel id="tabpanel3">
+ <description>tabpanel3</description>
+ </tabpanel>
+ <tabpanel id="tabpanel2">
+ <description>tabpanel2</description>
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ </vbox>
+</window>
+
diff --git a/accessible/tests/mochitest/relations/test_tabbrowser.xul b/accessible/tests/mochitest/relations/test_tabbrowser.xul
new file mode 100644
index 000000000..8fd340fec
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_tabbrowser.xul
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tabbrowser relation tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../relations.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker
+ function testTabRelations()
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.invoke = function testTabRelations_invoke()
+ {
+ var docURIs = ["about:", "about:mozilla"];
+ tabBrowser().loadTabs(docURIs, false, true);
+ }
+
+ this.finalCheck = function testTabRelations_finalCheck(aEvent)
+ {
+ ////////////////////////////////////////////////////////////////////////
+ // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
+
+ var tabs = tabBrowser().tabContainer.childNodes;
+ var panels = tabBrowser().mTabBox.tabpanels.childNodes;
+
+ testRelation(panels[0], RELATION_LABELLED_BY, tabs[0]);
+ testRelation(tabs[0], RELATION_LABEL_FOR, panels[0]);
+ testRelation(panels[1], RELATION_LABELLED_BY, tabs[1]);
+ testRelation(tabs[1], RELATION_LABEL_FOR, panels[1]);
+ }
+
+ this.getID = function testTabRelations_getID()
+ {
+ return "relations of tabs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ // Load documents into tabs and wait for DocLoadComplete events caused by
+ // these documents load before we start the test.
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new testTabRelations());
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+ title="No relationship between tabs and associated property page in new tabbrowser construct">
+ Mozilla Bug 552944
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/relations/test_tree.xul b/accessible/tests/mochitest/relations/test_tree.xul
new file mode 100644
index 000000000..300fa5bc3
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_tree.xul
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree relations tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../relations.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ var treeNode = getNode("tree");
+
+ var tree = getAccessible(treeNode);
+ var treeitem1 = tree.firstChild.nextSibling;
+ testRelation(treeitem1, RELATION_NODE_CHILD_OF, [tree]);
+
+ var treeitem2 = treeitem1.nextSibling;
+ testRelation(treeitem2, RELATION_NODE_CHILD_OF, [tree]);
+
+ var treeitem3 = treeitem2.nextSibling;
+ testRelation(treeitem3, RELATION_NODE_CHILD_OF, [treeitem2]);
+
+ var treeitem4 = treeitem3.nextSibling;
+ testRelation(treeitem4, RELATION_NODE_CHILD_OF, [treeitem2]);
+
+ var treeitem5 = treeitem4.nextSibling;
+ testRelation(treeitem5, RELATION_NODE_CHILD_OF, [tree]);
+
+ var treeitem6 = treeitem5.nextSibling;
+ testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
+
+ testRelation(tree, RELATION_NODE_PARENT_OF,
+ [treeitem1, treeitem2, treeitem5, treeitem6]);
+ testRelation(treeitem2, RELATION_NODE_PARENT_OF,
+ [treeitem3, treeitem4]);
+
+ // treeitems and treecells shouldn't pick up relations from tree
+ testRelation(treeitem1, RELATION_LABELLED_BY, null);
+ testRelation(treeitem1.firstChild, RELATION_LABELLED_BY, null);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTreeTreeView());
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Bug 503727
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
+ title="Implement RELATION_NODE_PARENT_OF">
+ Bug 527461
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=691248"
+ title="XUL tree items shouldn't pick up relations from XUL tree">
+ Bug 691248
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <label control="tree" value="It's a tree"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/relations/test_ui_modalprompt.html b/accessible/tests/mochitest/relations/test_ui_modalprompt.html
new file mode 100644
index 000000000..21918b33c
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_ui_modalprompt.html
@@ -0,0 +1,107 @@
+<html>
+
+<head>
+ <title>Modal prompts</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ function hasTabModalPrompts() {
+ try {
+ return SpecialPowers.getBoolPref("prompts.tab_modal.enabled");
+ } catch (ex) {
+ return false;
+ }
+ }
+
+ function showAlert()
+ {
+ this.eventSeq = [
+ {
+ type: EVENT_SHOW,
+ match: function(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_DIALOG;
+ }
+ }
+ ];
+
+ this.invoke = function showAlert_invoke()
+ {
+ window.setTimeout(
+ function()
+ {
+ currentTabDocument().defaultView.alert("hello");
+ }, 0);
+ }
+
+ this.check = function showAlert_finalCheck(aEvent)
+ {
+ var dialog = aEvent.accessible.DOMNode;
+ var info = dialog.ui.infoBody;
+ testRelation(info, RELATION_DESCRIPTION_FOR, dialog);
+ testRelation(dialog, RELATION_DESCRIBED_BY, info);
+ }
+
+ this.getID = function showAlert_getID()
+ {
+ return "show alert";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new showAlert());
+ gQueue.onFinish = function()
+ {
+ synthesizeKey("VK_RETURN", {}, browserWindow());
+ closeBrowserWindow();
+ }
+ gQueue.invoke(); // will call SimpleTest.finish()
+ }
+
+ if (!hasTabModalPrompts()) {
+ todo(false, "Test disabled when tab modal prompts are not enabled.");
+ } else {
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests);
+ }
+ </script>
+
+</head>
+
+<body id="body">
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=661293"
+ title="The tabmodalprompt dialog's prompt label doesn't get the text properly associated for accessibility">
+ Mozilla Bug 661293
+ </a>
+ <br>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/relations/test_update.html b/accessible/tests/mochitest/relations/test_update.html
new file mode 100644
index 000000000..a04831427
--- /dev/null
+++ b/accessible/tests/mochitest/relations/test_update.html
@@ -0,0 +1,225 @@
+<html>
+
+<head>
+ <title>Test updating of accessible relations</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../relations.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function testRelated(aRelAttr, aHostRelation, aDependentRelation,
+ aHostID, aHostNodeID, aDependent1ID, aDependent2ID)
+ {
+ // no attribute
+ testRelation(aDependent1ID, aDependentRelation, null);
+ testRelation(aDependent2ID, aDependentRelation, null);
+ if (aHostRelation)
+ testRelation(aHostID, aHostRelation, null);
+
+ // set attribute
+ getNode(aHostNodeID).setAttribute(aRelAttr, aDependent1ID);
+ testRelation(aDependent1ID, aDependentRelation, aHostID);
+ testRelation(aDependent2ID, aDependentRelation, null);
+ if (aHostRelation)
+ testRelation(aHostID, aHostRelation, aDependent1ID);
+
+ // change attribute
+ getNode(aHostNodeID).setAttribute(aRelAttr, aDependent2ID);
+ testRelation(aDependent1ID, aDependentRelation, null);
+ testRelation(aDependent2ID, aDependentRelation, aHostID);
+ if (aHostRelation)
+ testRelation(aHostID, aHostRelation, aDependent2ID);
+
+ // remove attribute
+ getNode(aHostNodeID).removeAttribute(aRelAttr);
+ testRelation(aDependent1ID, aDependentRelation, null);
+ testRelation(aDependent2ID, aDependentRelation, null);
+ if (aHostRelation)
+ testRelation(aHostID, aHostRelation, null);
+ }
+
+ function insertRelated(aHostRelAttr, aDependentID, aInsertHostFirst,
+ aHostRelation, aDependentRelation)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, document)
+ ];
+
+ this.invoke = function insertRelated_invoke()
+ {
+ this.hostNode = document.createElement("div");
+ this.hostNode.setAttribute(aHostRelAttr, aDependentID);
+
+ this.dependentNode = document.createElement("div");
+ this.dependentNode.setAttribute("id", aDependentID);
+
+ if (aInsertHostFirst) {
+ document.body.appendChild(this.hostNode);
+ document.body.appendChild(this.dependentNode);
+ } else {
+ document.body.appendChild(this.dependentNode);
+ document.body.appendChild(this.hostNode);
+ }
+ }
+
+ this.finalCheck = function insertRelated_finalCheck()
+ {
+ testRelation(this.dependentNode, aDependentRelation, this.hostNode);
+ if (aHostRelation)
+ testRelation(this.hostNode, aHostRelation, this.dependentNode);
+ }
+
+ this.getID = function insertRelated_getID()
+ {
+ return "Insert " + aHostRelAttr + "='" + aDependentID + "' node" +
+ (aInsertHostFirst ? " before" : "after") + " dependent node";
+ }
+ }
+
+ /**
+ * Relative accessible recreation shouldn't break accessible relations.
+ * Note: modify this case if the invoke function doesn't change accessible
+ * tree due to changes in layout module. It can be changed on any case
+ * when accessibles are recreated.
+ */
+ function recreateRelatives(aContainerID, aLabelID, aElmID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.container = getNode(this.containerNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.container),
+ new invokerChecker(EVENT_SHOW, this.containerNode)
+ ];
+
+ this.invoke = function recreateRelatives_invoke()
+ {
+ testRelation(aLabelID, RELATION_LABEL_FOR, aElmID);
+ testRelation(aElmID, RELATION_LABELLED_BY, aLabelID);
+
+ this.containerNode.style.overflow = "visible";
+ }
+
+ this.finalCheck = function recreateRelatives_finalCheck()
+ {
+ testRelation(aLabelID, RELATION_LABEL_FOR, aElmID);
+ testRelation(aElmID, RELATION_LABELLED_BY, aLabelID);
+ }
+
+ this.getID = function recreateRelatives_getID()
+ {
+ return "recreate relatives ";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ // Relation updates on ARIA attribute changes.
+ testRelated("aria-labelledby",
+ RELATION_LABELLED_BY, RELATION_LABEL_FOR,
+ "host", "host", "dependent1", "dependent2");
+
+ testRelated("aria-describedby",
+ RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR,
+ "host", "host", "dependent1", "dependent2");
+
+ testRelated("aria-controls",
+ RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY,
+ "host", "host", "dependent1", "dependent2");
+
+ testRelated("aria-flowto",
+ RELATION_FLOWS_TO, RELATION_FLOWS_FROM,
+ "host", "host", "dependent1", "dependent2");
+
+ // Document relation updates on ARIA attribute change.
+ testRelated("aria-labelledby",
+ RELATION_LABELLED_BY, RELATION_LABEL_FOR,
+ document, "body", "dependent1", "dependent2");
+
+ // Insert related accessibles into tree.
+ gQueue = new eventQueue();
+ gQueue.push(new insertRelated("aria-labelledby", "dependent3", true,
+ RELATION_LABELLED_BY, RELATION_LABEL_FOR));
+ gQueue.push(new insertRelated("aria-labelledby", "dependent4", false,
+ RELATION_LABELLED_BY, RELATION_LABEL_FOR));
+
+ gQueue.push(new insertRelated("aria-describedby", "dependent5", true,
+ RELATION_DESCRIBED_BY,
+ RELATION_DESCRIPTION_FOR));
+ gQueue.push(new insertRelated("aria-describedby", "dependent6", false,
+ RELATION_DESCRIBED_BY,
+ RELATION_DESCRIPTION_FOR));
+
+ gQueue.push(new insertRelated("aria-controls", "dependent9", true,
+ RELATION_CONTROLLER_FOR,
+ RELATION_CONTROLLED_BY));
+ gQueue.push(new insertRelated("aria-controls", "dependent10", false,
+ RELATION_CONTROLLER_FOR,
+ RELATION_CONTROLLED_BY));
+
+ gQueue.push(new insertRelated("aria-flowto", "dependent11", true,
+ RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
+ gQueue.push(new insertRelated("aria-flowto", "dependent12", false,
+ RELATION_FLOWS_TO, RELATION_FLOWS_FROM));
+
+ // Update relations when accessibles are recreated
+ gQueue.push(new recreateRelatives("container", "label", "input"));
+
+ gQueue.invoke(); // will call SimpleTest.finish()
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body id="body">
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=573469"
+ title="Cache relations defined by ARIA attributes">
+ Mozilla Bug 573469
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=631068"
+ title="Accessible recreation breaks relations">
+ Mozilla Bug 631068
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=635346"
+ title="Allow relations for document defined on document content">
+ Mozilla Bug 635346
+ </a>
+ <br>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="dependent1">label</div>
+ <div id="dependent2">label2</div>
+ <div role="checkbox" id="host"></div>
+
+ <form id="container" style="overflow: hidden;">
+ <label for="input" id="label">label</label>
+ <input id="input">
+ </form>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/role.js b/accessible/tests/mochitest/role.js
new file mode 100644
index 000000000..b76907f78
--- /dev/null
+++ b/accessible/tests/mochitest/role.js
@@ -0,0 +1,178 @@
+////////////////////////////////////////////////////////////////////////////////
+// Role constants
+
+const ROLE_ALERT = nsIAccessibleRole.ROLE_ALERT;
+const ROLE_ANIMATION = nsIAccessibleRole.ROLE_ANIMATION;
+const ROLE_APPLICATION = nsIAccessibleRole.ROLE_APPLICATION;
+const ROLE_APP_ROOT = nsIAccessibleRole.ROLE_APP_ROOT;
+const ROLE_AUTOCOMPLETE = nsIAccessibleRole.ROLE_AUTOCOMPLETE;
+const ROLE_BUTTONDROPDOWNGRID = nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID;
+const ROLE_CANVAS = nsIAccessibleRole.ROLE_CANVAS;
+const ROLE_CAPTION = nsIAccessibleRole.ROLE_CAPTION;
+const ROLE_CELL = nsIAccessibleRole.ROLE_CELL;
+const ROLE_CHECKBUTTON = nsIAccessibleRole.ROLE_CHECKBUTTON;
+const ROLE_CHECK_MENU_ITEM = nsIAccessibleRole.ROLE_CHECK_MENU_ITEM;
+const ROLE_CHROME_WINDOW = nsIAccessibleRole.ROLE_CHROME_WINDOW;
+const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
+const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
+const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
+const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
+const ROLE_DEFINITION = nsIAccessibleRole.ROLE_DEFINITION;
+const ROLE_DEFINITION_LIST = nsIAccessibleRole.ROLE_DEFINITION_LIST;
+const ROLE_DETAILS = nsIAccessibleRole.ROLE_DETAILS;
+const ROLE_DIAGRAM = nsIAccessibleRole.ROLE_DIAGRAM;
+const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
+const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
+const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
+const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
+const ROLE_EQUATION = nsIAccessibleRole.ROLE_EQUATION;
+const ROLE_FIGURE = nsIAccessibleRole.ROLE_FIGURE;
+const ROLE_FOOTER = nsIAccessibleRole.ROLE_FOOTER;
+const ROLE_FLAT_EQUATION = nsIAccessibleRole.ROLE_FLAT_EQUATION;
+const ROLE_FORM = nsIAccessibleRole.ROLE_FORM;
+const ROLE_GRAPHIC = nsIAccessibleRole.ROLE_GRAPHIC;
+const ROLE_GRID_CELL = nsIAccessibleRole.ROLE_GRID_CELL;
+const ROLE_GROUPING = nsIAccessibleRole.ROLE_GROUPING;
+const ROLE_HEADER = nsIAccessibleRole.ROLE_HEADER;
+const ROLE_HEADING = nsIAccessibleRole.ROLE_HEADING;
+const ROLE_IMAGE_MAP = nsIAccessibleRole.ROLE_IMAGE_MAP;
+const ROLE_INTERNAL_FRAME = nsIAccessibleRole.ROLE_INTERNAL_FRAME;
+const ROLE_LABEL = nsIAccessibleRole.ROLE_LABEL;
+const ROLE_LINK = nsIAccessibleRole.ROLE_LINK;
+const ROLE_LIST = nsIAccessibleRole.ROLE_LIST;
+const ROLE_LISTBOX = nsIAccessibleRole.ROLE_LISTBOX;
+const ROLE_LISTITEM = nsIAccessibleRole.ROLE_LISTITEM;
+const ROLE_MATHML_MATH = nsIAccessibleRole.ROLE_MATHML_MATH;
+const ROLE_MATHML_IDENTIFIER = nsIAccessibleRole.ROLE_MATHML_IDENTIFIER;
+const ROLE_MATHML_NUMBER = nsIAccessibleRole.ROLE_MATHML_NUMBER;
+const ROLE_MATHML_OPERATOR = nsIAccessibleRole.ROLE_MATHML_OPERATOR;
+const ROLE_MATHML_TEXT = nsIAccessibleRole.ROLE_MATHML_TEXT;
+const ROLE_MATHML_STRING_LITERAL = nsIAccessibleRole.ROLE_MATHML_STRING_LITERAL;
+const ROLE_MATHML_GLYPH = nsIAccessibleRole.ROLE_MATHML_GLYPH;
+const ROLE_MATHML_ROW = nsIAccessibleRole.ROLE_MATHML_ROW;
+const ROLE_MATHML_FRACTION = nsIAccessibleRole.ROLE_MATHML_FRACTION;
+const ROLE_MATHML_SQUARE_ROOT = nsIAccessibleRole.ROLE_MATHML_SQUARE_ROOT;
+const ROLE_MATHML_ROOT = nsIAccessibleRole.ROLE_MATHML_ROOT;
+const ROLE_MATHML_FENCED = nsIAccessibleRole.ROLE_MATHML_FENCED;
+const ROLE_MATHML_ENCLOSED = nsIAccessibleRole.ROLE_MATHML_ENCLOSED;
+const ROLE_MATHML_STYLE = nsIAccessibleRole.ROLE_MATHML_STYLE;
+const ROLE_MATHML_SUB = nsIAccessibleRole.ROLE_MATHML_SUB;
+const ROLE_MATHML_SUP = nsIAccessibleRole.ROLE_MATHML_SUP;
+const ROLE_MATHML_SUB_SUP = nsIAccessibleRole.ROLE_MATHML_SUB_SUP;
+const ROLE_MATHML_UNDER = nsIAccessibleRole.ROLE_MATHML_UNDER;
+const ROLE_MATHML_OVER = nsIAccessibleRole.ROLE_MATHML_OVER;
+const ROLE_MATHML_UNDER_OVER = nsIAccessibleRole.ROLE_MATHML_UNDER_OVER;
+const ROLE_MATHML_MULTISCRIPTS = nsIAccessibleRole.ROLE_MATHML_MULTISCRIPTS;
+const ROLE_MATHML_TABLE = nsIAccessibleRole.ROLE_MATHML_TABLE;
+const ROLE_MATHML_LABELED_ROW = nsIAccessibleRole.ROLE_MATHML_LABELED_ROW;
+const ROLE_MATHML_TABLE_ROW = nsIAccessibleRole.ROLE_MATHML_TABLE_ROW;
+const ROLE_MATHML_CELL = nsIAccessibleRole.ROLE_MATHML_CELL;
+const ROLE_MATHML_ACTION = nsIAccessibleRole.ROLE_MATHML_ACTION;
+const ROLE_MATHML_ERROR = nsIAccessibleRole.ROLE_MATHML_ERROR;
+const ROLE_MATHML_STACK = nsIAccessibleRole.ROLE_MATHML_STACK;
+const ROLE_MATHML_LONG_DIVISION = nsIAccessibleRole.ROLE_MATHML_LONG_DIVISION;
+const ROLE_MATHML_STACK_GROUP = nsIAccessibleRole.ROLE_MATHML_STACK_GROUP;
+const ROLE_MATHML_STACK_ROW = nsIAccessibleRole.ROLE_MATHML_STACK_ROW;
+const ROLE_MATHML_STACK_CARRIES = nsIAccessibleRole.ROLE_MATHML_STACK_CARRIES;
+const ROLE_MATHML_STACK_CARRY = nsIAccessibleRole.ROLE_MATHML_STACK_CARRY;
+const ROLE_MATHML_STACK_LINE = nsIAccessibleRole.ROLE_MATHML_STACK_LINE;
+const ROLE_MENUBAR = nsIAccessibleRole.ROLE_MENUBAR;
+const ROLE_MENUITEM = nsIAccessibleRole.ROLE_MENUITEM;
+const ROLE_MENUPOPUP = nsIAccessibleRole.ROLE_MENUPOPUP;
+const ROLE_NOTHING = nsIAccessibleRole.ROLE_NOTHING;
+const ROLE_NOTE = nsIAccessibleRole.ROLE_NOTE;
+const ROLE_OPTION = nsIAccessibleRole.ROLE_OPTION;
+const ROLE_OUTLINE = nsIAccessibleRole.ROLE_OUTLINE;
+const ROLE_OUTLINEITEM = nsIAccessibleRole.ROLE_OUTLINEITEM;
+const ROLE_PAGETAB = nsIAccessibleRole.ROLE_PAGETAB;
+const ROLE_PAGETABLIST = nsIAccessibleRole.ROLE_PAGETABLIST;
+const ROLE_PANE = nsIAccessibleRole.ROLE_PANE;
+const ROLE_PARAGRAPH = nsIAccessibleRole.ROLE_PARAGRAPH;
+const ROLE_PARENT_MENUITEM = nsIAccessibleRole.ROLE_PARENT_MENUITEM;
+const ROLE_PASSWORD_TEXT = nsIAccessibleRole.ROLE_PASSWORD_TEXT;
+const ROLE_PROGRESSBAR = nsIAccessibleRole.ROLE_PROGRESSBAR;
+const ROLE_PROPERTYPAGE = nsIAccessibleRole.ROLE_PROPERTYPAGE;
+const ROLE_PUSHBUTTON = nsIAccessibleRole.ROLE_PUSHBUTTON;
+const ROLE_RADIOBUTTON = nsIAccessibleRole.ROLE_RADIOBUTTON;
+const ROLE_RADIO_GROUP = nsIAccessibleRole.ROLE_RADIO_GROUP;
+const ROLE_RADIO_MENU_ITEM = nsIAccessibleRole.ROLE_RADIO_MENU_ITEM;
+const ROLE_RICH_OPTION = nsIAccessibleRole.ROLE_RICH_OPTION;
+const ROLE_ROW = nsIAccessibleRole.ROLE_ROW;
+const ROLE_ROWHEADER = nsIAccessibleRole.ROLE_ROWHEADER;
+const ROLE_SCROLLBAR = nsIAccessibleRole.ROLE_SCROLLBAR;
+const ROLE_SECTION = nsIAccessibleRole.ROLE_SECTION;
+const ROLE_SEPARATOR = nsIAccessibleRole.ROLE_SEPARATOR;
+const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
+const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
+const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT;
+const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR;
+const ROLE_SUMMARY = nsIAccessibleRole.ROLE_SUMMARY;
+const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH;
+const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE;
+const ROLE_TERM = nsIAccessibleRole.ROLE_TERM;
+const ROLE_TEXT = nsIAccessibleRole.ROLE_TEXT;
+const ROLE_TEXT_CONTAINER = nsIAccessibleRole.ROLE_TEXT_CONTAINER;
+const ROLE_TEXT_LEAF = nsIAccessibleRole.ROLE_TEXT_LEAF;
+const ROLE_TOGGLE_BUTTON = nsIAccessibleRole.ROLE_TOGGLE_BUTTON;
+const ROLE_TOOLBAR = nsIAccessibleRole.ROLE_TOOLBAR;
+const ROLE_TOOLTIP = nsIAccessibleRole.ROLE_TOOLTIP;
+const ROLE_TREE_TABLE = nsIAccessibleRole.ROLE_TREE_TABLE;
+const ROLE_WHITESPACE = nsIAccessibleRole.ROLE_WHITESPACE;
+
+////////////////////////////////////////////////////////////////////////////////
+// Public methods
+
+/**
+ * Test that the role of the given accessible is the role passed in.
+ *
+ * @param aAccOrElmOrID the accessible, DOM element or ID to be tested.
+ * @param aRole The role that is to be expected.
+ */
+function testRole(aAccOrElmOrID, aRole)
+{
+ var role = getRole(aAccOrElmOrID);
+ is(role, aRole, "Wrong role for " + prettyName(aAccOrElmOrID) + "!");
+}
+
+/**
+ * Return the role of the given accessible. Return -1 if accessible could not
+ * be retrieved.
+ *
+ * @param aAccOrElmOrID [in] The accessible, DOM element or element ID the
+ * accessible role is being requested for.
+ */
+function getRole(aAccOrElmOrID)
+{
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return -1;
+
+ var role = -1;
+ try {
+ role = acc.role;
+ } catch(e) {
+ ok(false, "Role for " + aAccOrElmOrID + " could not be retrieved!");
+ }
+
+ return role;
+}
+
+/**
+ * Analogy of SimpleTest.is function used to check the role.
+ */
+function isRole(aIdentifier, aRole, aMsg)
+{
+ var role = getRole(aIdentifier);
+ if (role == - 1)
+ return;
+
+ if (role == aRole) {
+ ok(true, aMsg);
+ return;
+ }
+
+ var got = roleToString(role);
+ var expected = roleToString(aRole);
+
+ ok(false, aMsg + "got '" + got + "', expected '" + expected + "'");
+}
diff --git a/accessible/tests/mochitest/role/a11y.ini b/accessible/tests/mochitest/role/a11y.ini
new file mode 100644
index 000000000..295fd34e4
--- /dev/null
+++ b/accessible/tests/mochitest/role/a11y.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+
+[test_aria.html]
+[test_aria.xul]
+[test_general.html]
+[test_general.xul]
+[test_svg.html]
diff --git a/accessible/tests/mochitest/role/test_aria.html b/accessible/tests/mochitest/role/test_aria.html
new file mode 100644
index 000000000..22021fa57
--- /dev/null
+++ b/accessible/tests/mochitest/role/test_aria.html
@@ -0,0 +1,345 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test weak ARIA roles</title>
+
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // ARIA role map.
+ testRole("aria_alert", ROLE_ALERT);
+ testRole("aria_alertdialog", ROLE_DIALOG);
+ testRole("aria_application", ROLE_APPLICATION);
+ testRole("aria_article", ROLE_DOCUMENT);
+ testRole("aria_button", ROLE_PUSHBUTTON);
+ testRole("aria_checkbox", ROLE_CHECKBUTTON);
+ testRole("aria_columnheader", ROLE_COLUMNHEADER);
+ testRole("aria_combobox", ROLE_COMBOBOX);
+ testRole("aria_dialog", ROLE_DIALOG);
+ testRole("aria_directory", ROLE_LIST);
+ testRole("aria_document", ROLE_DOCUMENT);
+ testRole("aria_form", ROLE_FORM);
+ testRole("aria_feed", ROLE_GROUPING);
+ testRole("aria_grid", ROLE_TABLE);
+ testRole("aria_gridcell", ROLE_GRID_CELL);
+ testRole("aria_group", ROLE_GROUPING);
+ testRole("aria_heading", ROLE_HEADING);
+ testRole("aria_img", ROLE_GRAPHIC);
+ testRole("aria_link", ROLE_LINK);
+ testRole("aria_list", ROLE_LIST);
+ testRole("aria_listbox", ROLE_LISTBOX);
+ testRole("aria_listitem", ROLE_LISTITEM);
+ testRole("aria_log", ROLE_TEXT); // weak role
+ testRole("aria_marquee", ROLE_ANIMATION);
+ testRole("aria_math", ROLE_FLAT_EQUATION);
+ testRole("aria_menu", ROLE_MENUPOPUP);
+ testRole("aria_menubar", ROLE_MENUBAR);
+ testRole("aria_menuitem", ROLE_MENUITEM);
+ testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM);
+ testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM);
+ testRole("aria_note", ROLE_NOTE);
+ testRole("aria_presentation", ROLE_TEXT); // weak role
+ testRole("aria_progressbar", ROLE_PROGRESSBAR);
+ testRole("aria_radio", ROLE_RADIOBUTTON);
+ testRole("aria_radiogroup", ROLE_RADIO_GROUP);
+ testRole("aria_region", ROLE_PANE);
+ testRole("aria_row", ROLE_ROW);
+ testRole("aria_rowheader", ROLE_ROWHEADER);
+ testRole("aria_scrollbar", ROLE_SCROLLBAR);
+ testRole("aria_searchbox", ROLE_ENTRY);
+ testRole("aria_separator", ROLE_SEPARATOR);
+ testRole("aria_slider", ROLE_SLIDER);
+ testRole("aria_spinbutton", ROLE_SPINBUTTON);
+ testRole("aria_status", ROLE_STATUSBAR);
+ testRole("aria_switch", ROLE_SWITCH);
+ testRole("aria_tab", ROLE_PAGETAB);
+ testRole("aria_tablist", ROLE_PAGETABLIST);
+ testRole("aria_tabpanel", ROLE_PROPERTYPAGE);
+ testRole("aria_textbox", ROLE_ENTRY);
+ testRole("aria_timer", ROLE_TEXT); // weak role
+ testRole("aria_toolbar", ROLE_TOOLBAR);
+ testRole("aria_tooltip", ROLE_TOOLTIP);
+ testRole("aria_tree", ROLE_OUTLINE);
+ testRole("aria_treegrid", ROLE_TREE_TABLE);
+ testRole("aria_treeitem", ROLE_OUTLINEITEM);
+
+ // Note:
+ // The phrase "weak foo" here means that there is no good foo-to-platform
+ // role mapping. Similarly "strong foo" means there is a good foo-to-
+ // platform role mapping.
+
+ testRole("articlemain", ROLE_DOCUMENT);
+ testRole("articleform", ROLE_FORM);
+
+ // Test article exposed as document
+ testRole("testArticle", ROLE_DOCUMENT);
+
+ // weak roles that are forms of "live regions"
+ testRole("log_table", ROLE_TABLE);
+ testRole("timer_div", ROLE_SECTION);
+
+ // other roles that are forms of "live regions"
+ testRole("marquee_h1", ROLE_ANIMATION);
+
+ // strong landmark
+ testRole("application", ROLE_APPLICATION);
+ testRole("form", ROLE_FORM);
+ testRole("application_table", ROLE_APPLICATION);
+
+ // weak landmarks
+ var weak_landmarks = ["banner", "complementary", "contentinfo",
+ "main", "navigation", "search"];
+ for (l in weak_landmarks)
+ testRole(weak_landmarks[l], ROLE_SECTION);
+
+ for (l in weak_landmarks) {
+ var id = weak_landmarks[l] + "_table";
+ testRole(id, ROLE_TABLE);
+
+ var accessibleTable = getAccessible(id, [nsIAccessibleTable], null,
+ DONOTFAIL_IF_NO_INTERFACE);
+ ok(accessibleTable ? true : false,
+ "landmarked table should have nsIAccessibleTable");
+
+ if (accessibleTable)
+ is(accessibleTable.getCellAt(0,0).firstChild.name, "hi", "no cell");
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // test gEmptyRoleMap
+ testRole("buttontable_row", ROLE_NOTHING);
+ testRole("buttontable_cell", ROLE_NOTHING);
+
+ // abstract roles
+ var abstract_roles = ["composite", "landmark", "structure", "widget",
+ "window", "input", "range", "select", "section",
+ "sectionhead"];
+ for (a in abstract_roles)
+ testRole(abstract_roles[a], ROLE_SECTION);
+
+ //////////////////////////////////////////////////////////////////////////
+ // roles transformed by ARIA state attributes
+ testRole("togglebutton", ROLE_TOGGLE_BUTTON);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ignore unknown roles, take first known
+ testRole("unknown_roles", ROLE_PUSHBUTTON);
+
+ //////////////////////////////////////////////////////////////////////////
+ // misc roles
+ testRole("note", ROLE_NOTE);
+ testRole("scrollbar", ROLE_SCROLLBAR);
+ testRole("dir", ROLE_LIST);
+
+ //////////////////////////////////////////////////////////////////////////
+ // test document role map update
+ var testDoc = getAccessible(document, [nsIAccessibleDocument]);
+ testRole(testDoc, ROLE_DOCUMENT);
+ document.body.setAttribute("role", "application");
+ testRole(testDoc, ROLE_APPLICATION);
+
+ // Test equation image
+ testRole("img_eq", ROLE_FLAT_EQUATION);
+
+ // Test textual equation
+ testRole("txt_eq", ROLE_FLAT_EQUATION);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428479">Mozilla Bug 428479</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=429666">Mozilla Bug 429666</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=481114">Mozilla Bug 481114</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469688">Mozilla Bug 469688</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469688">Mozilla Bug 520188</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289">Mozilla Bug 529289</a>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289">Mozilla Bug 607219</a>
+ <a target="_blank"
+ title="HTML buttons with aria-pressed not exposing IA2 TOGGLE_BUTTON role"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=725432">
+ Bug 725432
+ </a>
+ <a target="_blank"
+ title="Map ARIA role FORM"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=735645">
+ Bug 735645
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
+ title="Support ARIA 1.1 switch role">
+ Bug 1136563
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121518"
+ title="Support ARIA 1.1 searchbox role">
+ Bug 1121518
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <span id="aria_alert" role="alert"/>
+ <span id="aria_alertdialog" role="alertdialog"/>
+ <span id="aria_application" role="application"/>
+ <span id="aria_article" role="article"/>
+ <span id="aria_button" role="button"/>
+ <span id="aria_checkbox" role="checkbox"/>
+ <span id="aria_columnheader" role="columnheader"/>
+ <span id="aria_combobox" role="combobox"/>
+ <span id="aria_dialog" role="dialog"/>
+ <span id="aria_directory" role="directory"/>
+ <span id="aria_document" role="document"/>
+ <span id="aria_form" role="form"/>
+ <span id="aria_feed" role="feed"/>
+ <span id="aria_grid" role="grid"/>
+ <span id="aria_gridcell" role="gridcell"/>
+ <span id="aria_group" role="group"/>
+ <span id="aria_heading" role="heading"/>
+ <span id="aria_img" role="img"/>
+ <span id="aria_link" role="link"/>
+ <span id="aria_list" role="list"/>
+ <span id="aria_listbox" role="listbox"/>
+ <span id="aria_listitem" role="listitem"/>
+ <span id="aria_log" role="log"/>
+ <span id="aria_marquee" role="marquee"/>
+ <span id="aria_math" role="math"/>
+ <span id="aria_menu" role="menu"/>
+ <span id="aria_menubar" role="menubar"/>
+ <span id="aria_menuitem" role="menuitem"/>
+ <span id="aria_menuitemcheckbox" role="menuitemcheckbox"/>
+ <span id="aria_menuitemradio" role="menuitemradio"/>
+ <span id="aria_note" role="note"/>
+ <span id="aria_presentation" role="presentation" tabindex="0"/>
+ <span id="aria_progressbar" role="progressbar"/>
+ <span id="aria_radio" role="radio"/>
+ <span id="aria_radiogroup" role="radiogroup"/>
+ <span id="aria_region" role="region"/>
+ <span id="aria_row" role="row"/>
+ <span id="aria_rowheader" role="rowheader"/>
+ <span id="aria_scrollbar" role="scrollbar"/>
+ <span id="aria_searchbox" role="textbox"/>
+ <span id="aria_separator" role="separator"/>
+ <span id="aria_slider" role="slider"/>
+ <span id="aria_spinbutton" role="spinbutton"/>
+ <span id="aria_status" role="status"/>
+ <span id="aria_switch" role="switch"/>
+ <span id="aria_tab" role="tab"/>
+ <span id="aria_tablist" role="tablist"/>
+ <span id="aria_tabpanel" role="tabpanel"/>
+ <span id="aria_textbox" role="textbox"/>
+ <span id="aria_timer" role="timer"/>
+ <span id="aria_toolbar" role="toolbar"/>
+ <span id="aria_tooltip" role="tooltip"/>
+ <span id="aria_tree" role="tree"/>
+ <span id="aria_treegrid" role="treegrid"/>
+ <span id="aria_treeitem" role="treeitem"/>
+
+ <article id="articlemain" role="main">a main area</article>
+ <article id="articleform" role="form">a form area</article>
+
+ <div id="testArticle" role="article" title="Test article">
+ <p>This is a paragraph inside the article.</p>
+ </div>
+
+ <!-- "live" roles -->
+ <table role="log" id="log_table">
+ <tr><td>Table based log</td></tr>
+ </table>
+ <h1 role="marquee" id="marquee_h1">marquee</h1>
+ <div role="timer" id="timer_div">timer</div>
+
+ <!-- landmarks -->
+ <div role="application" id="application">application</div>
+ <div role="form" id="form">form</div>
+
+ <!-- weak landmarks -->
+ <div role="banner" id="banner">banner</div>
+ <div role="complementary" id="complementary">complementary</div>
+ <div role="contentinfo" id="contentinfo">contentinfo</div>
+ <div role="main" id="main">main</div>
+ <div role="navigation" id="navigation">navigation</div>
+ <div role="search" id="search">search</div>
+
+ <!-- landmarks are tables -->
+ <table role="application" id="application_table">application table
+ <tr><td>hi<td></tr></table>
+ <table role="banner" id="banner_table">banner table
+ <tr><td>hi<td></tr></table>
+ <table role="complementary" id="complementary_table">complementary table
+ <tr><td>hi<td></tr></table>
+ <table role="contentinfo" id="contentinfo_table">contentinfo table
+ <tr><td>hi<td></tr></table>
+ <table role="main" id="main_table">main table
+ <tr><td>hi<td></tr></table>
+ <table role="navigation" id="navigation_table">navigation table
+ <tr><td>hi<td></tr></table>
+ <table role="search" id="search_table">search table
+ <tr><td>hi<td></tr></table>
+
+ <!-- test gEmptyRoleMap -->
+ <table role="button">
+ <tr id="buttontable_row">
+ <td id="buttontable_cell">cell</td>
+ </tr>
+ </table>
+
+ <!-- user agents must not map abstract roles to platform API -->
+ <!-- test abstract base type roles -->
+ <div role="composite" id="composite">composite</div>
+ <div role="landmark" id="landmark">landmark</div>
+ <div role="roletype" id="roletype">roletype</div>
+ <div role="structure" id="structure">structure</div>
+ <div role="widget" id="widget">widget</div>
+ <div role="window" id="window">window</div>
+ <!-- test abstract input roles -->
+ <div role="input" id="input">input</div>
+ <div role="range" id="range">range</div>
+ <div role="select" id="select">select</div>
+ <!-- test abstract structure roles -->
+ <div role="section" id="section">section</div>
+ <div role="sectionhead" id="sectionhead">sectionhead</div>
+
+ <!-- roles transformed by ARIA state attributes -->
+ <button aria-pressed="true" id="togglebutton">
+
+ <!-- take the first known mappable role -->
+ <div role="wiggly:worm abc123 button" id="unknown_roles">worm button</div>
+
+ <!-- misc roles -->
+ <div role="note" id="note">note</div>
+ <div role="scrollbar" id="scrollbar">scrollbar</div>
+
+ <div id="dir" role="directory">
+ <div role="listitem">A</div>
+ <div role="listitem">B</div>
+ <div role="listitem">C</div>
+ </div>
+
+ <p>Image:
+ <img id="img_eq" role="math" src="foo" alt="x^2 + y^2 + z^2">
+ </p>
+
+ <p>Text:
+ <span id="txt_eq" role="math" title="x^2 + y^2 + z^2">x<sup>2</sup> +
+ y<sup>2</sup> + z<sup>2</sup></span>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/role/test_aria.xul b/accessible/tests/mochitest/role/test_aria.xul
new file mode 100644
index 000000000..544034d1f
--- /dev/null
+++ b/accessible/tests/mochitest/role/test_aria.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Name Calculating Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ ok(!isAccessible("presentation_label"),
+ "Presentation label shouldn't be accessible.");
+ ok(!isAccessible("presentation_descr"),
+ "Presentation description shouldn't be accessible.");
+
+ // aria-pressed
+ testRole("pressed_button", ROLE_TOGGLE_BUTTON);
+ testRole("pressed_menu_button", ROLE_TOGGLE_BUTTON);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=494345"
+ title="Do not create accessibles for XUL label or description having a role of 'presentation'">
+ Mozilla Bug 494345
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1033283"
+ title="Expose pressed state on XUL menu toggle buttons">
+ Mozilla Bug 1033283
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <label id="presentation_label" role="presentation" value="label"/>
+ <description id="presentation_descr" role="presentation" value="description"/>
+ <button id="pressed_button" aria-pressed="true" label="I am pressed" />
+ <button id="pressed_menu_button" aria-pressed="true" label="I am pressed" type="menu-button">
+ <menupopup>
+ <menuitem label="I am a menu item" />
+ </menupopup>
+ </button>
+ </vbox>
+
+
+ </hbox>
+</window>
+
diff --git a/accessible/tests/mochitest/role/test_general.html b/accessible/tests/mochitest/role/test_general.html
new file mode 100644
index 000000000..40f522482
--- /dev/null
+++ b/accessible/tests/mochitest/role/test_general.html
@@ -0,0 +1,186 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>test nsHyperTextAccessible accesible objects creation and their roles</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+ function doTests()
+ {
+ // landmark tests section
+ testRole("frm", ROLE_FORM);
+
+ // nsHyperTextAcc tests section
+ // Test html:form.
+ testRole("nav", ROLE_SECTION);
+ testRole("header", ROLE_HEADER);
+ testRole("footer", ROLE_FOOTER);
+ testRole("article", ROLE_DOCUMENT);
+ testRole("aside", ROLE_NOTE);
+ testRole("section", ROLE_SECTION);
+
+ // Bug 996821
+ // Check that landmark elements get accessibles with styled overflow.
+ testRole("section_overflow", ROLE_SECTION);
+ testRole("nav_overflow", ROLE_SECTION);
+ testRole("header_overflow", ROLE_HEADER);
+ testRole("aside_overflow", ROLE_NOTE);
+ testRole("footer_overflow", ROLE_FOOTER);
+ testRole("article_overflow", ROLE_DOCUMENT);
+
+ // test html:div
+ testRole("sec", ROLE_SECTION);
+
+ // Test html:blockquote
+ testRole("quote", ROLE_SECTION);
+
+ // Test html:h, all levels
+ testRole("head1", ROLE_HEADING);
+ testRole("head2", ROLE_HEADING);
+ testRole("head3", ROLE_HEADING);
+ testRole("head4", ROLE_HEADING);
+ testRole("head5", ROLE_HEADING);
+ testRole("head6", ROLE_HEADING);
+
+ // Test that an html:input @type="file" is exposed as ROLE_TEXT_CONTAINER.
+ // After fix for bug 471356, it was temporarily exposed as a paragraph,
+ // breaking JAWS compatibility.
+ testRole("data", ROLE_TEXT_CONTAINER);
+
+ // Test regular paragraph by comparison to make sure exposure does not
+ // get broken.
+ testRole("p", ROLE_PARAGRAPH);
+
+ // Test dl, dt, dd
+ testRole("definitionlist", ROLE_DEFINITION_LIST);
+ testRole("definitionterm", ROLE_TERM);
+ testRole("definitiondescription", ROLE_DEFINITION);
+
+ // Has click, mousedown or mouseup listeners.
+ testRole("span1", ROLE_TEXT);
+ testRole("span2", ROLE_TEXT);
+ testRole("span3", ROLE_TEXT);
+
+ // Test role of listbox inside combobox
+ testRole("listbox1", ROLE_COMBOBOX_LIST);
+ testRole("listbox2", ROLE_COMBOBOX_LIST);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+<body>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=472326"
+ title="html:input of type "file" no longer rendered to screen readers">
+ Mozilla Bug 472326
+ </a><br>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=474261"
+ title="Test remaining implementations in nsHyperTextAccessible::GetRole">
+ bug 474261
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=423409"
+ title="Expose click action if mouseup and mousedown are registered">
+ Mozilla Bug 423409
+ </a>
+ <a target="_blank"
+ title="Provide mappings for html5 <nav> <header> <footer> <article>"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=593368">
+ Bug 593368
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+ title="Map <article> like we do aria role article">
+ Bug 613502
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=610650"
+ title="Change implementation of HTML5 landmark elements to conform">
+ Bug 610650
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=614310"
+ title="Map section to pane (like role=region)">
+ Mozilla Bug 614310
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=734982"
+ title="Map ARIA role FORM">
+ Bug 734982
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1044431"
+ title="Listbox owned by combobox has the wrong role">
+ Mozilla Bug 1044431
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <form id="frm" action="submit.php" method="post">
+ <label for="data">File</label>:
+ <input type="file" id="data" name="data" size="50"/>
+ </form>
+
+ <nav id="nav">a nav</nav>
+ <header id="header">a header</header>
+ <footer id="footer">a footer</footer>
+ <article id="article">an article</article>
+ <aside id="aside">by the way I am an aside</aside>
+ <section id="section">a section</section>
+
+ <section style="overflow: hidden;" id="section_overflow">
+ <nav style="overflow: hidden;"
+ id="nav_overflow">overflow nav</nav>
+ <header style="overflow: hidden;"
+ id="header_overflow">overflow header</header>
+ <aside style="overflow: hidden;"
+ id="aside_overflow">overflow aside</aside>
+ <footer style="overflow: hidden;"
+ id="footer_overflow">overflow footer</footer>
+ </section>
+ <article style="overflow: hidden;"
+ id="article_overflow">overflow article</article>
+
+ <p id="p">A paragraph for comparison.</p>
+ <div id="sec">A normal div</div>
+ <blockquote id="quote">A citation</blockquote>
+ <h1 id="head1">A heading level 1</h1>
+ <h2 id="head2">A heading level 2</h2>
+ <h3 id="head3">A heading level 3</h3>
+ <h4 id="head4">A heading level 4</h4>
+ <h5 id="head5">A heading level 5</h5>
+ <h6 id="head6">A heading level 6</h6>
+
+ <dl id="definitionlist">
+ <dt id="definitionterm">gecko</dt>
+ <dd id="definitiondescription">geckos have sticky toes</dd>
+ </dl>
+
+ <span id="span1" onclick="">clickable span</span>
+ <span id="span2" onmousedown="">clickable span</span>
+ <span id="span3" onmouseup="">clickable span</span>
+
+ <div id="combobox1" role="combobox">
+ <div id="listbox1" role="listbox"></div>
+ </div>
+ <div id="combobox2" role="combobox" aria-owns="listbox2"></div>
+ <div id="listbox2" role="listbox"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/role/test_general.xul b/accessible/tests/mochitest/role/test_general.xul
new file mode 100644
index 000000000..d5982b63f
--- /dev/null
+++ b/accessible/tests/mochitest/role/test_general.xul
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Role XUL Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ ok(!isAccessible("statusbarpanel"),
+ "statusbarpanel shouldn't be accessible.");
+ testRole("statusbarpanel-iconic", ROLE_PUSHBUTTON);
+ testRole("statusbarpanel-iconic-text", ROLE_PUSHBUTTON);
+ testRole("statusbar", ROLE_STATUSBAR);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=900097"
+ title="statusbarpanel shouldn't be a button accessible">
+ Mozilla Bug 900097
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <statusbarpanel id="statusbarpanel"></statusbarpanel>
+ <statusbarpanel id="statusbarpanel-iconic" class="statusbarpanel-iconic"></statusbarpanel>
+ <statusbarpanel id="statusbarpanel-iconic-text" class="statusbarpanel-iconic-text"></statusbarpanel>
+ <statusbar id="statusbar"></statusbar>
+
+ </hbox>
+</window>
+
diff --git a/accessible/tests/mochitest/role/test_svg.html b/accessible/tests/mochitest/role/test_svg.html
new file mode 100644
index 000000000..164861dc8
--- /dev/null
+++ b/accessible/tests/mochitest/role/test_svg.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>SVG elements accessible roles</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+ function doTests()
+ {
+ testRole("svg", ROLE_DIAGRAM);
+ testRole("rect", ROLE_GRAPHIC);
+ testRole("circle", ROLE_GRAPHIC);
+ testRole("ellipse", ROLE_GRAPHIC);
+ testRole("line", ROLE_GRAPHIC);
+ testRole("polygon", ROLE_GRAPHIC);
+ testRole("polyline", ROLE_GRAPHIC);
+ testRole("path", ROLE_GRAPHIC);
+ testRole("image", ROLE_GRAPHIC);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=822983"
+ title="Map SVG graphic elements to accessibility API">
+ Bug 822983
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <rect width="300" height="100" id="rect"
+ style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/>
+ <circle cx="100" cy="50" r="40" stroke="black" id="circle"
+ stroke-width="2" fill="red"/>
+ <ellipse cx="300" cy="80" rx="100" ry="50" id="ellipse"
+ style="fill:yellow;stroke:purple;stroke-width:2"/>
+ <line x1="0" y1="0" x2="200" y2="200" id="line"
+ style="stroke:rgb(255,0,0);stroke-width:2"/>
+ <polygon points="200,10 250,190 160,210" id="polygon"
+ style="fill:lime;stroke:purple;stroke-width:1"/>
+ <polyline points="20,20 40,25 60,40 80,120 120,140 200,180" id="polyline"
+ style="fill:none;stroke:black;stroke-width:3" />
+ <path d="M150 0 L75 200 L225 200 Z" id="path"/>
+ <image x1="25" y1="80" width="50" height="20" id="image"
+ xlink:href="../moz.png"/>
+ </svg>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/scroll/a11y.ini b/accessible/tests/mochitest/scroll/a11y.ini
new file mode 100644
index 000000000..e2e9dfd48
--- /dev/null
+++ b/accessible/tests/mochitest/scroll/a11y.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_zoom.html]
+[test_zoom_text.html]
diff --git a/accessible/tests/mochitest/scroll/test_zoom.html b/accessible/tests/mochitest/scroll/test_zoom.html
new file mode 100644
index 000000000..05dd3c444
--- /dev/null
+++ b/accessible/tests/mochitest/scroll/test_zoom.html
@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test scrollToPoint when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+
+ <script type="application/javascript">
+ function testScrollToPoint()
+ {
+ // scrollToPoint relative screen
+ var anchor = getAccessible("bottom1");
+ var [x, y] = getPos(anchor);
+ var [docX, docY] = getPos(document);
+
+ anchor.scrollToPoint(COORDTYPE_SCREEN_RELATIVE, docX, docY);
+ testPos(anchor, [x, docY]);
+
+ // scrollToPoint relative window
+ anchor = getAccessible("bottom2");
+ var [x, y] = getPos(anchor);
+ var wnd = getRootAccessible().DOMDocument.defaultView;
+ var [screenX, screenY] = CSSToDevicePixels(wnd, wnd.screenX, wnd.screenY);
+ var scrollToX = docX - screenX, scrollToY = docY - screenY;
+
+ anchor.scrollToPoint(COORDTYPE_WINDOW_RELATIVE, scrollToX, scrollToY);
+ testPos(anchor, [x, docY]);
+
+ // scrollToPoint relative parent
+ anchor = getAccessible("bottom3");
+ var [x, y] = getPos(anchor);
+ var [parentX, parentY] = getPos(anchor.parent);
+ var scrollToX = parentX - docX, scrollToY = parentY - docY;
+
+ anchor.scrollToPoint(COORDTYPE_PARENT_RELATIVE, scrollToX, scrollToY);
+ testPos(anchor, [x, docY]);
+ }
+
+ function doTest()
+ {
+ testScrollToPoint();
+ zoomDocument(document, 2.0);
+ testScrollToPoint(); // zoom and test again
+
+ zoomDocument(document, 1.0);
+ SimpleTest.finish();
+ }
+
+ addA11yLoadEvent(doTest);
+ SimpleTest.waitForExplicitFinish();
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727942"
+ title="scrollToPoint is broken when page is zoomed">
+ Mozilla Bug 727942
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <h1>Below there is a bunch of named anchors</h1>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ This is in the middle anchor #1<a id="bottom1"></a>
+ <br><br><br><br><br><br><br><br><br><br>
+ This is in the middle anchor #2<a id="bottom2"></a>
+ <br><br><br><br><br><br><br><br><br><br>
+ This is in the middle anchor #3<a id="bottom3"></a>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+ <br><br><br><br><br><br><br><br><br><br>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/scroll/test_zoom_text.html b/accessible/tests/mochitest/scroll/test_zoom_text.html
new file mode 100644
index 000000000..3ef8fcded
--- /dev/null
+++ b/accessible/tests/mochitest/scroll/test_zoom_text.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test scrollSubstringToPoint when page is zoomed</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var tabDocument = currentTabDocument();
+ var paragraphNode = tabDocument.getElementById("paragraph");
+ var paragraph = getAccessible(paragraphNode, [nsIAccessibleText]);
+ var offset = 64; // beginning of 4th stanza
+
+ var [x, y] = getPos(paragraph);
+ var [docX, docY] = getPos(tabDocument);
+
+ paragraph.scrollSubstringToPoint(offset, offset,
+ COORDTYPE_SCREEN_RELATIVE, docX, docY);
+ testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE);
+
+ zoomDocument(tabDocument, 2.0);
+
+ paragraphNode = tabDocument.getElementById("paragraph2");
+ paragraph = getAccessible(paragraphNode, [nsIAccessibleText]);
+ offset = 52; // // beginning of 4th stanza
+ var [x, y] = getPos(paragraph);
+ paragraph.scrollSubstringToPoint(offset, offset,
+ COORDTYPE_SCREEN_RELATIVE, docX, docY);
+ testTextPos(paragraph, offset, [x, docY], COORDTYPE_SCREEN_RELATIVE);
+
+ closeBrowserWindow();
+ SimpleTest.finish();
+ }
+
+ var url = "data:text/html,<html>" +
+ "<meta http-equiv='Content-Type' content='text/html;charset=utf-8' />" +
+ "<body>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br><hr>" +
+ "<p id='paragraph'>" +
+ " Пошел котик на торжок<br>" +
+ " Купил котик пирожок<br>" +
+ " Пошел котик на улочку<br>" +
+ " Купил котик булочку<br>" +
+ "</p>" +
+ "<hr><br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br><hr>" +
+ "<p id='paragraph2'>" +
+ " Самому ли съесть<br>" +
+ " Либо Сашеньке снесть<br>" +
+ " Я и сам укушу<br>" +
+ " Я и Сашеньке снесу<br>" +
+ "</p>" +
+ "<hr><br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "<br><br><br><br><br><br><br><br><br><br>" +
+ "</body></html>";
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest,
+ url,
+ { left: 0, top: 0, width: 600, height: 600 });
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727942"
+ title="scrollSubstringToPoint is broken when page is zoomed">
+ Mozilla Bug 727942
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/selectable.js b/accessible/tests/mochitest/selectable.js
new file mode 100644
index 000000000..87bf296f0
--- /dev/null
+++ b/accessible/tests/mochitest/selectable.js
@@ -0,0 +1,80 @@
+/**
+ * Test selection getter methods of nsIAccessibleSelectable.
+ *
+ * @param aIdentifier [in] selectable container accessible
+ * @param aSelectedChildren [in] array of selected children
+ */
+function testSelectableSelection(aIdentifier, aSelectedChildren, aMsg)
+{
+ var acc = getAccessible(aIdentifier, [nsIAccessibleSelectable]);
+ if (!acc)
+ return;
+
+ var msg = aMsg ? aMsg : "";
+ var len = aSelectedChildren.length;
+
+ // getSelectedChildren
+ var selectedChildren = acc.selectedItems;
+ is(selectedChildren ? selectedChildren.length : 0, len,
+ msg + "getSelectedChildren: wrong selected children count for " +
+ prettyName(aIdentifier));
+
+ for (var idx = 0; idx < len; idx++) {
+ var expectedAcc = getAccessible(aSelectedChildren[idx]);
+ var actualAcc = selectedChildren.queryElementAt(idx, nsIAccessible);
+ is(actualAcc, expectedAcc,
+ msg + "getSelectedChildren: wrong selected child at index " + idx +
+ " for " + prettyName(aIdentifier) + " { actual : " +
+ prettyName(actualAcc) + ", expected: " + prettyName(expectedAcc) + "}");
+ }
+
+ // selectedItemCount
+ is(acc.selectedItemCount, aSelectedChildren.length,
+ "selectedItemCount: wrong selected children count for " + prettyName(aIdentifier));
+
+ // getSelectedItemAt
+ for (var idx = 0; idx < len; idx++) {
+ var expectedAcc = getAccessible(aSelectedChildren[idx]);
+ is(acc.getSelectedItemAt(idx), expectedAcc,
+ msg + "getSelectedItemAt: wrong selected child at index " + idx + " for " +
+ prettyName(aIdentifier));
+ }
+
+ // isItemSelected
+ testIsItemSelected(acc, acc, { value: 0 }, aSelectedChildren, msg);
+}
+
+/**
+ * Test isItemSelected method, helper for testSelectableSelection
+ */
+function testIsItemSelected(aSelectAcc, aTraversedAcc, aIndexObj, aSelectedChildren, aMsg)
+{
+ var childCount = aTraversedAcc.childCount;
+ for (var idx = 0; idx < childCount; idx++) {
+ var child = aTraversedAcc.getChildAt(idx);
+ var [state, extraState] = getStates(child);
+ if (state & STATE_SELECTABLE) {
+ var isSelected = false;
+ var len = aSelectedChildren.length;
+ for (var jdx = 0; jdx < len; jdx++) {
+ if (child == getAccessible(aSelectedChildren[jdx])) {
+ isSelected = true;
+ break;
+ }
+ }
+
+ // isItemSelected
+ is(aSelectAcc.isItemSelected(aIndexObj.value++), isSelected,
+ aMsg + "isItemSelected: wrong selected child " + prettyName(child) +
+ " for " + prettyName(aSelectAcc));
+
+ // selected state
+ testStates(child, isSelected ? STATE_SELECTED : 0, 0,
+ !isSelected ? STATE_SELECTED : 0 , 0);
+
+ continue;
+ }
+
+ testIsItemSelected(aSelectAcc, child, aIndexObj, aSelectedChildren);
+ }
+}
diff --git a/accessible/tests/mochitest/selectable/a11y.ini b/accessible/tests/mochitest/selectable/a11y.ini
new file mode 100644
index 000000000..4fc11fee8
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/a11y.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/treeview.css
+
+[test_aria.html]
+[test_listbox.xul]
+[test_menu.xul]
+[test_menulist.xul]
+[test_select.html]
+[test_tree.xul]
diff --git a/accessible/tests/mochitest/selectable/test_aria.html b/accessible/tests/mochitest/selectable/test_aria.html
new file mode 100644
index 000000000..075871b66
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_aria.html
@@ -0,0 +1,225 @@
+<html>
+
+<head>
+ <title>nsIAccessibleSelectable ARIA widgets testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../selectable.js"></script>
+
+ <script type="application/javascript">
+ function testSelectable(aID, aSelectableChildren)
+ {
+ var acc = getAccessible(aID, [nsIAccessibleSelectable]);
+
+ testSelectableSelection(acc, []);
+
+ acc.selectAll();
+ testSelectableSelection(acc, aSelectableChildren);
+
+ acc.unselectAll();
+ testSelectableSelection(acc, []);
+ }
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // role="tablist"
+
+ id = "tablist";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="listbox"
+
+ id = "listbox1";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="listbox" aria-multiselectable
+
+ id = "listbox2";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ select = getAccessible(id, [nsIAccessibleSelectable]);
+ select.addItemToSelection(0);
+ testSelectableSelection(id, [ "listbox2_item1" ]);
+ select.removeItemFromSelection(0);
+ testSelectableSelection(id, [ ]);
+ select.selectAll();
+ testSelectableSelection(id, [ "listbox2_item1", "listbox2_item2" ]);
+ select.unselectAll();
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="grid"
+
+ id = "grid1";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="tree"
+
+ id = "tree1";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="treegrid"
+
+ id = "treegrid1";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectableSelection(id, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // role="grid" aria-multiselectable, selectable children in subtree
+
+ id = "grid2";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ testSelectable(id,
+ ["grid2_colhead1", "grid2_colhead2", "grid2_colhead3",
+ "grid2_rowhead", "grid2_cell1", "grid2_cell2"]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=530014"
+ title="ARIA single selectable widget should implement nsIAccessibleSelectable">
+ Mozilla Bug 530014
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566551"
+ title="ARIA grid and accessible selectable methods shouldn't use GetNextSibling">
+ Mozilla Bug 566551
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=590176"
+ title="add pseudo SelectAccessible interface">
+ Mozilla Bug 590176
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=804040"
+ title="Selection event not fired when selection of ARIA tab changes">
+ Mozilla Bug 804040
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="tablist" id="tablist">
+ <div role="tab">tab1</div>
+ <div role="tab">tab2</div>
+ </div>
+
+ <div role="listbox" id="listbox1">
+ <div role="option">item1</div>
+ <div role="option">item2</div>
+ </div>
+
+ <div role="listbox" id="listbox2" aria-multiselectable="true">
+ <div role="option" id="listbox2_item1">item1</div>
+ <div role="option" id="listbox2_item2">item2</div>
+ </div>
+
+ <div role="grid" id="grid1">
+ <div role="row">
+ <span role="gridcell">cell</span>
+ <span role="gridcell">cell</span>
+ </div>
+ <div role="row">
+ <span role="gridcell">cell</span>
+ <span role="gridcell">cell</span>
+ </div>
+ </div>
+
+ <div role="tree" id="tree1">
+ <div role="treeitem">
+ item1
+ <div role="group">
+ <div role="treeitem">item1.1</div>
+ </div>
+ </div>
+ <div>item2</div>
+ </div>
+
+ <div role="treegrid" id="treegrid1">
+ <div role="row" aria-level="1">
+ <span role="gridcell">cell</span>
+ <span role="gridcell">cell</span>
+ </div>
+ <div role="row" aria-level="2">
+ <span role="gridcell">cell</span>
+ <span role="gridcell">cell</span>
+ </div>
+ <div role="row" aria-level="1">
+ <span role="gridcell">cell</span>
+ <span role="gridcell">cell</span>
+ </div>
+ </div>
+
+ <table tabindex="0" border="2" cellspacing="0" id="grid2" role="grid"
+ aria-multiselectable="true">
+ <thead>
+ <tr>
+ <th tabindex="-1" role="columnheader" id="grid2_colhead1"
+ style="width:6em">Entry #</th>
+ <th tabindex="-1" role="columnheader" id="grid2_colhead2"
+ style="width:10em">Date</th>
+ <th tabindex="-1" role="columnheader" id="grid2_colhead3"
+ style="width:20em">Expense</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td tabindex="-1" role="rowheader" id="grid2_rowhead"
+ aria-readonly="true">1</td>
+ <td tabindex="-1" role="gridcell" id="grid2_cell1"
+ aria-selected="false">03/14/05</td>
+ <td tabindex="-1" role="gridcell" id="grid2_cell2"
+ aria-selected="false">Conference Fee</td>
+ </tr>
+ </tobdy>
+ </table>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/selectable/test_listbox.xul b/accessible/tests/mochitest/selectable/test_listbox.xul
new file mode 100644
index 000000000..990589257
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_listbox.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL tree selectable tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../selectable.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "debug";
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // single selectable listbox
+
+ var id = "listbox";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of " + id);
+
+ var select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "lb1_item2" ], "addItemToSelect(1): ");
+
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ ],
+ "removeItemFromSelection(1): ");
+
+ todo(select.selectAll() == false,
+ "No way to select all items in listbox '" + id + "'");
+ testSelectableSelection(select, [ "lb1_item1" ], "selectAll: ");
+
+ select.addItemToSelection(1);
+ select.unselectAll();
+ testSelectableSelection(select, [ ], "unselectAll: ");
+
+ //////////////////////////////////////////////////////////////////////////
+ // multiple selectable listbox
+
+ var id = "listbox2";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of " + id);
+
+ var select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "lb2_item2" ], "addItemToSelect(1): ");
+
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ ],
+ "removeItemFromSelection(1): ");
+
+ is(select.selectAll(), true,
+ "All items should be selected in listbox '" + id + "'");
+ testSelectableSelection(select, [ "lb2_item1", "lb2_item2" ],
+ "selectAll: ");
+
+ select.unselectAll();
+ testSelectableSelection(select, [ ], "unselectAll: ");
+
+ //////////////////////////////////////////////////////////////////////////
+ // listbox with headers
+
+ // XXX: addItemToSelection/removeItemFromSelection don't work correctly
+ // on listboxes with headers because header is inserted into hierarchy
+ // and child indexes that are used in these methods are shifted (see bug
+ // 591939).
+ todo(false,
+ "Fix addItemToSelection/removeItemFromSelection on listboxes with headers.");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=590176"
+ title="add pseudo SelectAccessible interface">
+ Mozilla Bug 590176
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <listbox id="listbox">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem id="lb1_item1">
+ <listcell label="cell0"/>
+ <listcell label="cell1"/>
+ </listitem>
+ <listitem id="lb1_item2">
+ <listcell label="cell3"/>
+ <listcell label="cell4"/>
+ </listitem>
+ </listbox>
+
+ <listbox id="listbox2" seltype="multiple">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem id="lb2_item1">
+ <listcell label="cell0"/>
+ <listcell label="cell1"/>
+ </listitem>
+ <listitem id="lb2_item2">
+ <listcell label="cell3"/>
+ <listcell label="cell4"/>
+ </listitem>
+ </listbox>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/selectable/test_menu.xul b/accessible/tests/mochitest/selectable/test_menu.xul
new file mode 100644
index 000000000..4accedbbd
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_menu.xul
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL tree selectable tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../selectable.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "debug";
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // menu
+
+ var id = "menu";
+ var menu = getAccessible("menu");
+ var menuList = menu.firstChild;
+ todo(isAccessible(menuList, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of menu '" + id + "'");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=590176"
+ title="add pseudo SelectAccessible interface">
+ Mozilla Bug 590176
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menu label="menu" id="menu">
+ <menupopup>
+ <menuitem label="item1" id="m_item1"/>
+ <menuitem label="item2" id="m_item2"/>
+ </menupopup>
+ </menu>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/selectable/test_menulist.xul b/accessible/tests/mochitest/selectable/test_menulist.xul
new file mode 100644
index 000000000..5bd7a26b2
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_menulist.xul
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL tree selectable tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../selectable.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "debug";
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // menulist aka combobox
+
+ var id = "combobox";
+ var combobox = getAccessible(id);
+ var comboboxList = combobox.firstChild;
+ ok(isAccessible(comboboxList, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of " + id);
+
+ var select = getAccessible(comboboxList, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ "cb1_item1" ]);
+
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "cb1_item2" ], "addItemToSelection(1): ");
+
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ ],
+ "removeItemFromSelection(1): ");
+
+ is(select.selectAll(), false,
+ "No way to select all items in combobox '" + id + "'");
+ testSelectableSelection(select, [ ], "selectAll: ");
+
+ select.addItemToSelection(1);
+ select.unselectAll();
+ testSelectableSelection(select, [ ], "unselectAll: ");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=590176"
+ title="add pseudo SelectAccessible interface">
+ Mozilla Bug 590176
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menulist id="combobox">
+ <menupopup>
+ <menuitem id="cb1_item1" label="item1"/>
+ <menuitem id="cb1_item2" label="item2"/>
+ </menupopup>
+ </menulist>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/selectable/test_select.html b/accessible/tests/mochitest/selectable/test_select.html
new file mode 100644
index 000000000..9369fdfaf
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_select.html
@@ -0,0 +1,243 @@
+<html>
+
+<head>
+ <title>nsIAccessibleSelectable HTML select testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../selectable.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="1" aka combobox
+
+ var id = "combobox";
+ var combobox = getAccessible(id);
+ var comboboxList = combobox.firstChild;
+ ok(isAccessible(comboboxList, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of " + id);
+
+ var select = getAccessible(comboboxList, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ "cb1_item1" ]);
+
+ // select 2nd item
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "cb1_item2" ], "addItemToSelection(1): ");
+
+ // unselect 2nd item, 1st item gets selected automatically
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ "cb1_item1" ],
+ "removeItemFromSelection(1): ");
+
+ // doesn't change selection
+ is(select.selectAll(), false,
+ "No way to select all items in combobox '" + id + "'");
+ testSelectableSelection(select, [ "cb1_item1" ], "selectAll: ");
+
+ // doesn't change selection
+ select.unselectAll();
+ testSelectableSelection(select, [ "cb1_item1" ], "unselectAll: ");
+
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="1" with optgroups
+
+ id = "combobox2";
+ combobox = getAccessible(id);
+ comboboxList = combobox.firstChild;
+ ok(isAccessible(comboboxList, [nsIAccessibleSelectable]),
+ "No selectable accessible for list of " + id);
+
+ select = getAccessible(comboboxList, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ "cb2_item1" ]);
+
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "cb2_item2" ]);
+
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ "cb2_item1" ]);
+
+ is(select.selectAll(), false,
+ "No way to select all items in combobox " + id + "'");
+ testSelectableSelection(select, [ "cb2_item1" ]);
+
+ select.unselectAll();
+ testSelectableSelection(select, [ "cb2_item1" ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="4" aka single selectable listbox
+
+ var id = "listbox";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ // select 2nd item
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "lb1_item2" ], "addItemToSelection(1): ");
+
+ // unselect 2nd item, 1st item gets selected automatically
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ ],
+ "removeItemFromSelection(1): ");
+
+ // doesn't change selection
+ is(select.selectAll(), false,
+ "No way to select all items in single selectable listbox '" + id + "'");
+ testSelectableSelection(select, [ ], "selectAll: ");
+
+ // doesn't change selection
+ select.unselectAll();
+ testSelectableSelection(select, [ ], "unselectAll: ");
+
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="4" with optgroups, single selectable
+
+ id = "listbox2";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ select.addItemToSelection(1);
+ testSelectableSelection(select, [ "lb2_item2" ]);
+
+ select.removeItemFromSelection(1);
+ testSelectableSelection(select, [ ]);
+
+ is(select.selectAll(), false,
+ "No way to select all items in single selectable listbox " + id + "'");
+ testSelectableSelection(select, [ ]);
+
+ select.unselectAll();
+ testSelectableSelection(select, [ ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="4" multiselect aka listbox
+
+ id = "listbox3";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ select.addItemToSelection(0);
+ testSelectableSelection(select, [ "lb3_item1" ], "addItemToSelection: ");
+
+ select.removeItemFromSelection(0);
+ testSelectableSelection(select, [ ], "removeItemFromSelection: ");
+
+ is(select.selectAll(), true,
+ "All items in listbox '" + id + "' should be selected");
+ testSelectableSelection(select, [ "lb3_item1", "lb3_item2"],
+ "selectAll: ");
+
+ select.unselectAll();
+ testSelectableSelection(select, [ ], "unselectAll: ");
+
+ //////////////////////////////////////////////////////////////////////////
+ // select@size="4" multiselect with optgroups
+
+ var id = "listbox4";
+ ok(isAccessible(id, [nsIAccessibleSelectable]),
+ "No selectable accessible for " + id);
+
+ select = getAccessible(id, [nsIAccessibleSelectable]);
+ testSelectableSelection(select, [ ]);
+
+ select.addItemToSelection(0);
+ testSelectableSelection(select, [ "lb4_item1" ]);
+
+ select.removeItemFromSelection(0);
+ testSelectableSelection(select, [ ]);
+
+ is(select.selectAll(), true,
+ "All items in listbox '" + id + "' should be selected");
+ testSelectableSelection(select, [ "lb4_item1", "lb4_item2"]);
+
+ select.unselectAll();
+ testSelectableSelection(select, [ ]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=530014"
+ title="ARIA single selectable widget should implement nsIAccessibleSelectable">
+ Mozilla Bug 530014
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=590176"
+ title="add pseudo SelectAccessible interface">
+ Mozilla Bug 590176
+ </a><br>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="combobox">
+ <option id="cb1_item1">option1</option>
+ <option id="cb1_item2">option2</option>
+ </select>
+
+ <select id="combobox2">
+ <option id="cb2_item1">option1</option>
+ <optgroup>optgroup
+ <option id="cb2_item2">option2</option>
+ </optgroup>
+ </select>
+
+ <select id="listbox" size="4">
+ <option id="lb1_item1">option1</option>
+ <option id="lb1_item2">option2</option>
+ </select>
+
+ <select id="listbox2" size="4">
+ <option id="lb2_item1">option1</option>
+ <optgroup>optgroup>
+ <option id="lb2_item2">option2</option>
+ </optgroup>
+ </select>
+
+ <select id="listbox3" size="4" multiple="true">
+ <option id="lb3_item1">option1</option>
+ <option id="lb3_item2">option2</option>
+ </select>
+
+ <select id="listbox4" size="4" multiple="true">
+ <option id="lb4_item1">option1</option>
+ <optgroup>optgroup>
+ <option id="lb4_item2">option2</option>
+ </optgroup>
+ </select>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/selectable/test_tree.xul b/accessible/tests/mochitest/selectable/test_tree.xul
new file mode 100644
index 000000000..81e082534
--- /dev/null
+++ b/accessible/tests/mochitest/selectable/test_tree.xul
@@ -0,0 +1,188 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL tree selectable tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../selectable.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+
+ /**
+ * Event queue invoker object to test accessible states for XUL tree
+ * accessible.
+ */
+ function statesChecker(aTreeID, aView)
+ {
+ this.DOMNode = getNode(aTreeID);
+
+ this.invoke = function invoke()
+ {
+ this.DOMNode.view = aView;
+ }
+ this.check = function check()
+ {
+ var tree = getAccessible(this.DOMNode);
+
+ var isTreeMultiSelectable = false;
+ var seltype = this.DOMNode.getAttribute("seltype");
+ if (seltype != "single" && seltype != "cell" && seltype != "text")
+ isTreeMultiSelectable = true;
+
+ // selectAll
+ var accSelectable = getAccessible(this.DOMNode,
+ [nsIAccessibleSelectable]);
+ ok(accSelectable, "tree is not selectable!");
+ if (accSelectable) {
+ is(accSelectable.selectAll(), isTreeMultiSelectable,
+ "SelectAll is not correct for seltype: " + seltype);
+ }
+
+ var selectedChildren = [];
+ if (isTreeMultiSelectable) {
+ var rows = tree.children;
+ for (var i = 0; i < rows.length; i++) {
+ var row = rows.queryElementAt(i, nsIAccessible);
+ if (getRole(row) == ROLE_OUTLINEITEM || getRole(row) == ROLE_ROW)
+ selectedChildren.push(row);
+ }
+ }
+ testSelectableSelection(accSelectable, selectedChildren,
+ "selectAll test. ");
+
+ // unselectAll
+ accSelectable.unselectAll();
+ testSelectableSelection(accSelectable, [], "unselectAll test. ");
+
+ // addItemToSelection
+ accSelectable.addItemToSelection(1);
+ accSelectable.addItemToSelection(3);
+
+ selectedChildren = isTreeMultiSelectable ?
+ [ accSelectable.getChildAt(2), accSelectable.getChildAt(4) ] :
+ [ accSelectable.getChildAt(2) ];
+ testSelectableSelection(accSelectable, selectedChildren,
+ "addItemToSelection test. ");
+
+ // removeItemFromSelection
+ accSelectable.removeItemFromSelection(1);
+
+ selectedChildren = isTreeMultiSelectable ?
+ [ accSelectable.getChildAt(4) ] : [ ];
+ testSelectableSelection(accSelectable, selectedChildren,
+ "removeItemFromSelection test. ");
+ }
+
+ this.getID = function getID()
+ {
+ "tree processor for " + prettyName(aTreeID);
+ }
+ }
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue(EVENT_REORDER);
+ gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("treecell", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("treetext", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=523118"
+ title="we mistake 'cell' and text' xul tree seltypes for multiselects">
+ Mozilla Bug 523118
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=624977"
+ title="Optimize nsXulTreeAccessible selectedItems()">
+ Mozilla Bug 624977
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treesingle" flex="1" seltype="single">
+ <treecols>
+ <treecol id="col_single" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treecell" flex="1" seltype="cell">
+ <treecols>
+ <treecol id="col_cell" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treetext" flex="1" seltype="text">
+ <treecols>
+ <treecol id="col_text" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="tabletree" flex="1" editable="true">
+ <treecols>
+ <treecol id="tabletree_col1" cycler="true" label="cycler"/>
+ <treecol id="tabletree_col2" flex="1" primary="true" label="column1"/>
+ <treecol id="tabletree_col3" flex="1" label="column2"/>
+ <treecol id="tabletree_col4" flex="1" label="checker"
+ type="checkbox" editable="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/states.js b/accessible/tests/mochitest/states.js
new file mode 100644
index 000000000..73ec126e3
--- /dev/null
+++ b/accessible/tests/mochitest/states.js
@@ -0,0 +1,266 @@
+////////////////////////////////////////////////////////////////////////////////
+// Helper functions for accessible states testing.
+//
+// requires:
+// common.js
+// role.js
+//
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// State constants
+
+// const STATE_BUSY is defined in common.js
+const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
+const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
+const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
+const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT;
+const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
+const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
+const STATE_FLOATING = nsIAccessibleStates.STATE_FLOATING;
+const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;
+const STATE_FOCUSED = nsIAccessibleStates.STATE_FOCUSED;
+const STATE_HASPOPUP = nsIAccessibleStates.STATE_HASPOPUP;
+const STATE_INVALID = nsIAccessibleStates.STATE_INVALID;
+const STATE_INVISIBLE = nsIAccessibleStates.STATE_INVISIBLE;
+const STATE_LINKED = nsIAccessibleStates.STATE_LINKED;
+const STATE_MIXED = nsIAccessibleStates.STATE_MIXED;
+const STATE_MULTISELECTABLE = nsIAccessibleStates.STATE_MULTISELECTABLE;
+const STATE_OFFSCREEN = nsIAccessibleStates.STATE_OFFSCREEN;
+const STATE_PRESSED = nsIAccessibleStates.STATE_PRESSED;
+const STATE_PROTECTED = nsIAccessibleStates.STATE_PROTECTED;
+const STATE_READONLY = nsIAccessibleStates.STATE_READONLY;
+const STATE_REQUIRED = nsIAccessibleStates.STATE_REQUIRED;
+const STATE_SELECTABLE = nsIAccessibleStates.STATE_SELECTABLE;
+const STATE_SELECTED = nsIAccessibleStates.STATE_SELECTED;
+const STATE_TRAVERSED = nsIAccessibleStates.STATE_TRAVERSED;
+const STATE_UNAVAILABLE = nsIAccessibleStates.STATE_UNAVAILABLE;
+
+const EXT_STATE_ACTIVE = nsIAccessibleStates.EXT_STATE_ACTIVE;
+const EXT_STATE_DEFUNCT = nsIAccessibleStates.EXT_STATE_DEFUNCT;
+const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
+const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED;
+const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
+const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
+const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL;
+const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
+const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED;
+const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE;
+const EXT_STATE_SINGLE_LINE = nsIAccessibleStates.EXT_STATE_SINGLE_LINE;
+const EXT_STATE_STALE = nsIAccessibleStates.EXT_STATE_STALE;
+const EXT_STATE_SUPPORTS_AUTOCOMPLETION =
+ nsIAccessibleStates.EXT_STATE_SUPPORTS_AUTOCOMPLETION;
+const EXT_STATE_VERTICAL = nsIAccessibleStates.EXT_STATE_VERTICAL;
+
+const kOrdinalState = false;
+const kExtraState = 1;
+
+////////////////////////////////////////////////////////////////////////////////
+// Test functions
+
+/**
+ * Tests the states and extra states of the given accessible.
+ * Also tests for unwanted states and extra states.
+ * In addition, the function performs a few plausibility checks derived from the
+ * sstates and extra states passed in.
+ *
+ * @param aAccOrElmOrID The accessible, DOM element or ID to be tested.
+ * @param aState The state bits that are wanted.
+ * @param aExtraState The extra state bits that are wanted.
+ * @param aAbsentState State bits that are not wanted.
+ * @param aAbsentExtraState Extra state bits that are not wanted.
+ * @param aTestName The test name.
+ */
+function testStates(aAccOrElmOrID, aState, aExtraState, aAbsentState,
+ aAbsentExtraState, aTestName)
+{
+ var [state, extraState] = getStates(aAccOrElmOrID);
+ var role = getRole(aAccOrElmOrID);
+ var id = prettyName(aAccOrElmOrID) + (aTestName ? " [" + aTestName + "]": "");
+
+ // Primary test.
+ if (aState) {
+ isState(state & aState, aState, false,
+ "wrong state bits for " + id + "!");
+ }
+
+ if (aExtraState)
+ isState(extraState & aExtraState, aExtraState, true,
+ "wrong extra state bits for " + id + "!");
+
+ if (aAbsentState)
+ isState(state & aAbsentState, 0, false,
+ "state bits should not be present in ID " + id + "!");
+
+ if (aAbsentExtraState)
+ isState(extraState & aAbsentExtraState, 0, true,
+ "extraState bits should not be present in ID " + id + "!");
+
+ // Additional test.
+
+ // focused/focusable
+ if (state & STATE_FOCUSED)
+ isState(state & STATE_FOCUSABLE, STATE_FOCUSABLE, false,
+ "Focussed " + id + " must be focusable!");
+
+ if (aAbsentState && (aAbsentState & STATE_FOCUSABLE)) {
+ isState(state & STATE_FOCUSED, 0, false,
+ "Not focusable " + id + " must be not focused!");
+ }
+
+ // multiline/singleline
+ if (extraState & EXT_STATE_MULTI_LINE)
+ isState(extraState & EXT_STATE_SINGLE_LINE, 0, true,
+ "Multiline " + id + " cannot be singleline!");
+
+ if (extraState & EXT_STATE_SINGLE_LINE)
+ isState(extraState & EXT_STATE_MULTI_LINE, 0, true,
+ "Singleline " + id + " cannot be multiline!");
+
+ // expanded/collapsed/expandable
+ if (state & STATE_COLLAPSED || state & STATE_EXPANDED)
+ isState(extraState & EXT_STATE_EXPANDABLE, EXT_STATE_EXPANDABLE, true,
+ "Collapsed or expanded " + id + " must be expandable!");
+
+ if (state & STATE_COLLAPSED)
+ isState(state & STATE_EXPANDED, 0, false,
+ "Collapsed " + id + " cannot be expanded!");
+
+ if (state & STATE_EXPANDED)
+ isState(state & STATE_COLLAPSED, 0, false,
+ "Expanded " + id + " cannot be collapsed!");
+
+ if (aAbsentState && (extraState & EXT_STATE_EXPANDABLE)) {
+ if (aAbsentState & STATE_EXPANDED) {
+ isState(state & STATE_COLLAPSED, STATE_COLLAPSED, false,
+ "Not expanded " + id + " must be collapsed!");
+ } else if (aAbsentState & STATE_COLLAPSED) {
+ isState(state & STATE_EXPANDED, STATE_EXPANDED, false,
+ "Not collapsed " + id + " must be expanded!");
+ }
+ }
+
+ // checked/mixed/checkable
+ if (state & STATE_CHECKED || state & STATE_MIXED &&
+ role != ROLE_TOGGLE_BUTTON && role != ROLE_PROGRESSBAR)
+ isState(state & STATE_CHECKABLE, STATE_CHECKABLE, false,
+ "Checked or mixed element must be checkable!");
+
+ if (state & STATE_CHECKED)
+ isState(state & STATE_MIXED, 0, false,
+ "Checked element cannot be state mixed!");
+
+ if (state & STATE_MIXED)
+ isState(state & STATE_CHECKED, 0, false,
+ "Mixed element cannot be state checked!");
+
+ // selected/selectable
+ if (state & STATE_SELECTED) {
+ isState(state & STATE_SELECTABLE, STATE_SELECTABLE, false,
+ "Selected element must be selectable!");
+ }
+}
+
+/**
+ * Tests an acessible and its sub tree for the passed in state bits.
+ * Used to make sure that states are propagated to descendants, for example the
+ * STATE_UNAVAILABLE from a container to its children.
+ *
+ * @param aAccOrElmOrID The accessible, DOM element or ID to be tested.
+ * @param aState The state bits that are wanted.
+ * @param aExtraState The extra state bits that are wanted.
+ * @param aAbsentState State bits that are not wanted.
+ */
+function testStatesInSubtree(aAccOrElmOrID, aState, aExtraState, aAbsentState)
+{
+ // test accessible and its subtree for propagated states.
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return;
+
+ if (getRole(acc) != ROLE_TEXT_LEAF)
+ // Right now, text leafs don't get tested because the states are not being
+ // propagated.
+ testStates(acc, aState, aExtraState, aAbsentState);
+
+ // Iterate over its children to see if the state got propagated.
+ var children = null;
+ try {
+ children = acc.children;
+ } catch(e) {}
+ ok(children, "Could not get children for " + aAccOrElmOrID +"!");
+
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ var childAcc = children.queryElementAt(i, nsIAccessible);
+ testStatesInSubtree(childAcc, aState, aExtraState, aAbsentState);
+ }
+ }
+}
+
+/**
+ * Fails if no defunct state on the accessible.
+ */
+function testIsDefunct(aAccessible, aTestName)
+{
+ var id = prettyName(aAccessible) + (aTestName ? " [" + aTestName + "]" : "");
+ var [state, extraState] = getStates(aAccessible);
+ isState(extraState & EXT_STATE_DEFUNCT, EXT_STATE_DEFUNCT, true,
+ "no defuct state for " + id + "!");
+}
+
+function getStringStates(aAccOrElmOrID)
+{
+ var [state, extraState] = getStates(aAccOrElmOrID);
+ return statesToString(state, extraState);
+}
+
+function getStates(aAccOrElmOrID)
+{
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return [0, 0];
+
+ var state = {}, extraState = {};
+ acc.getState(state, extraState);
+
+ return [state.value, extraState.value];
+}
+
+/**
+ * Return true if the accessible has given states.
+ */
+function hasState(aAccOrElmOrID, aState, aExtraState)
+{
+ var [state, exstate] = getStates(aAccOrElmOrID);
+ return (aState ? state & aState : true) &&
+ (aExtraState ? exstate & aExtraState : true);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private implementation details
+
+/**
+ * Analogy of SimpleTest.is function used to compare states.
+ */
+function isState(aState1, aState2, aIsExtraStates, aMsg)
+{
+ if (aState1 == aState2) {
+ ok(true, aMsg);
+ return;
+ }
+
+ var got = "0";
+ if (aState1) {
+ got = statesToString(aIsExtraStates ? 0 : aState1,
+ aIsExtraStates ? aState1 : 0);
+ }
+
+ var expected = "0";
+ if (aState2) {
+ expected = statesToString(aIsExtraStates ? 0 : aState2,
+ aIsExtraStates ? aState2 : 0);
+ }
+
+ ok(false, aMsg + "got '" + got + "', expected '" + expected + "'");
+}
diff --git a/accessible/tests/mochitest/states/a11y.ini b/accessible/tests/mochitest/states/a11y.ini
new file mode 100644
index 000000000..f07afc3e9
--- /dev/null
+++ b/accessible/tests/mochitest/states/a11y.ini
@@ -0,0 +1,37 @@
+[DEFAULT]
+support-files =
+ z_frames.html
+ z_frames_article.html
+ z_frames_checkbox.html
+ z_frames_textbox.html
+ z_frames_update.html
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/dumbfile.zip
+ !/accessible/tests/mochitest/formimage.png
+ !/accessible/tests/mochitest/treeview.css
+
+[test_aria.html]
+[test_aria.xul]
+[test_aria_imgmap.html]
+[test_aria_widgetitems.html]
+[test_buttons.html]
+[test_controls.html]
+[test_controls.xul]
+[test_doc.html]
+[test_doc_busy.html]
+skip-if = os == 'mac' && os_version == '10.6'
+[test_docarticle.html]
+[test_editablebody.html]
+[test_expandable.xul]
+[test_frames.html]
+[test_inputs.html]
+[test_link.html]
+[test_popup.xul]
+[test_selects.html]
+[test_stale.html]
+[test_tabs.xul]
+[test_textbox.xul]
+[test_tree.xul]
+[test_visibility.html]
+[test_visibility.xul]
+skip-if = buildapp == "mulet"
diff --git a/accessible/tests/mochitest/states/test_aria.html b/accessible/tests/mochitest/states/test_aria.html
new file mode 100644
index 000000000..7d1ecf650
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_aria.html
@@ -0,0 +1,629 @@
+<html>
+
+<head>
+ <title>ARIA based nsIAccessible states testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ .offscreen {
+ position: absolute;
+ left: -5000px;
+ top: -5000px;
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function testAriaDisabledTree(aAccOrElmOrID)
+ {
+ // test accessible and its subtree for propagated state.
+ var acc = getAccessible(aAccOrElmOrID);
+ if (!acc)
+ return;
+
+ var [state, extraState] = getStates(aAccOrElmOrID);
+ if (state & STATE_UNAVAILABLE) {
+ var role = getRole(acc);
+ if (role != ROLE_GROUPING) {
+ testStates(acc, STATE_FOCUSABLE);
+ }
+ }
+
+ // Iterate over its children to see if the state got propagated.
+ var children = null;
+ try {
+ children = acc.children;
+ } catch(e) {}
+ ok(children, "Could not get children for " + aAccOrElmOrID +"!");
+
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ var childAcc = children.queryElementAt(i, nsIAccessible);
+ testAriaDisabledTree(childAcc);
+ }
+ }
+ }
+
+ function doTest()
+ {
+ // aria_autocomplete
+ testStates("textbox_autocomplete_inline", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("textbox_autocomplete_list", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("textbox_autocomplete_both", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("combobox_autocomplete_inline", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("combobox_autocomplete_list", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("combobox_autocomplete_both", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+
+ testStates("htmltext_autocomplete_list", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("htmltextarea_autocomplete_list", STATE_HASPOPUP, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+
+ // aria-busy
+ testStates("textbox_busy_false", 0, 0, STATE_BUSY);
+ testStates("textbox_busy_true", STATE_BUSY);
+ testStates("textbox_busy_error", STATE_INVALID);
+
+ // aria-expanded
+ testStates("combobox", STATE_COLLAPSED);
+ testStates("combobox_expanded", STATE_EXPANDED);
+
+ // tri-state checkbox
+ var checkboxElem = getNode("check1");
+ if (checkboxElem) {
+ testStates(checkboxElem, STATE_CHECKED);
+ checkboxElem.checked = false;
+ testStates(checkboxElem, 0, 0, STATE_CHECKED);
+ checkboxElem.indeterminate = true;
+ testStates(checkboxElem, STATE_MIXED, 0);
+ }
+
+ // aria-checked
+ testStates("aria_checked_checkbox", STATE_CHECKED);
+ testStates("aria_mixed_checkbox", STATE_MIXED);
+ testStates("aria_checked_switch", STATE_CHECKED);
+ testStates("aria_mixed_switch", 0, 0, STATE_MIXED); // unsupported
+
+ // test disabled group and all its descendants to see if they are
+ // disabled, too. See bug 429285.
+ testAriaDisabledTree("group");
+
+ // aria-modal
+ testStates("aria_modal", 0, EXT_STATE_MODAL);
+ testStates("aria_modal_false", 0, 0, 0, EXT_STATE_MODAL);
+
+ // aria-multiline
+ testStates("aria_multiline_textbox", 0, EXT_STATE_MULTI_LINE);
+
+ // aria-multiselectable
+ testStates("aria_multiselectable_listbox",
+ STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+
+ // aria-pressed
+ testStates("aria_pressed_button", STATE_PRESSED, 0, STATE_CHECKABLE);
+ testStates("aria_pressed_native_button", STATE_PRESSED, 0, STATE_CHECKABLE);
+
+ // aria-readonly
+ testStates("aria_readonly_textbox", STATE_READONLY);
+
+ // readonly/editable on grid and gridcell
+ testStates("aria_grid_default", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_default_colheader_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_default_colheader_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_default_rowheader_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_default_rowheader_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_default_cell_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_default_cell_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+
+ testStates("aria_grid_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_readonly_colheader_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_readonly_colheader_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_readonly_rowheader_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_readonly_rowheader_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_grid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_grid_readonly_cell_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+
+ // readonly/editable on treegrid and gridcell
+ testStates("aria_treegrid_default", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_default_colheader_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_default_colheader_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_default_rowheader_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_default_rowheader_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_default_cell_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_default_cell_inherited", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+
+ testStates("aria_treegrid_readonly", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_readonly_colheader_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_readonly_colheader_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_readonly_rowheader_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_readonly_rowheader_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+ testStates("aria_treegrid_readonly_cell_editable", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, 0);
+ testStates("aria_treegrid_readonly_cell_inherited", STATE_READONLY, 0,
+ 0, EXT_STATE_EDITABLE);
+
+ // aria-selectable
+ testStates("aria_selectable_listitem", STATE_SELECTABLE | STATE_SELECTED);
+
+ // active state caused by aria-activedescendant
+ testStates("as_item1", 0, EXT_STATE_ACTIVE);
+ testStates("as_item2", 0, 0, 0, EXT_STATE_ACTIVE);
+
+ // universal ARIA properties inherited from file input control
+ var fileBrowseButton = getAccessible("fileinput").firstChild;
+ testStates(fileBrowseButton,
+ STATE_BUSY | STATE_UNAVAILABLE | STATE_REQUIRED | STATE_HASPOPUP | STATE_INVALID);
+ // No states on the label.
+
+ // offscreen test
+ testStates("aria_offscreen_textbox", STATE_OFFSCREEN);
+
+ //
+ // This section tests aria roles on links/anchors for underlying
+ // HTMLLinkAccessible creation. (see closed bug 494807)
+ //
+
+ // strong roles
+ testStates("aria_menuitem_link", 0, 0, STATE_LINKED);
+ testStates("aria_button_link", 0, 0, STATE_LINKED);
+ testStates("aria_checkbox_link", 0, 0, STATE_LINKED);
+
+ // strong landmark
+ testStates("aria_application_link", 0, 0, STATE_LINKED);
+ testStates("aria_application_anchor", 0, 0, STATE_SELECTABLE);
+
+ // strange cases
+ testStates("aria_link_link", STATE_LINKED);
+ testStates("aria_link_anchor", STATE_SELECTABLE);
+
+ // some weak landmarks
+ testStates("aria_main_link", STATE_LINKED);
+ testStates("aria_navigation_link", STATE_LINKED);
+ testStates("aria_main_anchor", STATE_SELECTABLE);
+ testStates("aria_navigation_anchor", STATE_SELECTABLE);
+
+ // aria-orientation
+ testStates("aria_combobox", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_hcombobox", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vcombobox", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_listbox", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_hlistbox", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vlistbox", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_menu", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_hmenu", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vmenu", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_menubar", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_hmenubar", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vmenubar", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_radiogroup", 0, 0, 0, EXT_STATE_HORIZONTAL | EXT_STATE_VERTICAL);
+ testStates("aria_hradiogroup", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vradiogroup", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_scrollbar", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_hscrollbar", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vscrollbar", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_separator", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_hseparator", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vseparator", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_slider", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_hslider", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vslider", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_tablist", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_htablist", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vtablist", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_toolbar", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_htoolbar", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vtoolbar", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_tree", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_htree", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vtree", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_treegrid", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+ testStates("aria_htreegrid", 0, EXT_STATE_HORIZONTAL, 0, EXT_STATE_VERTICAL);
+ testStates("aria_vtreegrid", 0, EXT_STATE_VERTICAL, 0, EXT_STATE_HORIZONTAL);
+
+ // indeterminate ARIA progressbars (no aria-valuenow or aria-valuetext attribute)
+ // should expose mixed state
+ testStates("aria_progressbar", STATE_MIXED);
+ testStates("aria_progressbar_valuenow", 0, 0, STATE_MIXED);
+ testStates("aria_progressbar_valuetext", 0, 0, STATE_MIXED);
+
+ testStates("aria_listbox", STATE_FOCUSABLE);
+ testStates("aria_grid", STATE_FOCUSABLE);
+ testStates("aria_tree", STATE_FOCUSABLE);
+ testStates("aria_treegrid", STATE_FOCUSABLE);
+ testStates("aria_listbox_disabled", 0, 0, STATE_FOCUSABLE);
+ testStates("aria_grid_disabled", 0, 0, STATE_FOCUSABLE);
+ testStates("aria_tree_disabled", 0, 0, STATE_FOCUSABLE);
+ testStates("aria_treegrid_disabled", 0, 0, STATE_FOCUSABLE);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=457219"
+ title="nsIAccessible states testing">
+ Mozilla Bug 457219
+ </a><br />
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=429285"
+ title="Propagate aria-disabled to descendants">
+ Mozilla Bug 429285
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=457226"
+ title="Mochitests for ARIA states">
+ Mozilla Bug 457226
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=499653"
+ title="Unify ARIA state attributes mapping rules">
+ Mozilla Bug 499653
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=681674"
+ title="aria-autocomplete not supported on standard form text input controls">
+ Mozilla Bug 681674
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=681674"
+ title="aria-orientation should be applied to separator and slider roles">
+ Mozilla Bug 681674
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=689847"
+ title="Expose active state on current item of selectable widgets">
+ Mozilla Bug 689847
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
+ title="File input control should be propogate states to descendants">
+ Mozilla Bug 699017
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=690199"
+ title="ARIA select widget should expose focusable state regardless the way they manage its children">
+ Mozilla Bug 690199
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=740851"
+ title="ARIA undetermined progressmeters should expose mixed state">
+ Mozilla Bug 740851
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=762876"
+ title="fix default horizontal / vertical state of role=scrollbar and ensure only one of horizontal / vertical states is exposed">
+ Mozilla Bug 762876
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=892091"
+ title="ARIA treegrid should be editable by default">
+ Bug 892091
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=835121"
+ title="ARIA grid should be editable by default">
+ Mozilla Bug 835121
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=989958"
+ title="Pressed state is not exposed on a button element with aria-pressed attribute">
+ Mozilla Bug 989958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
+ title="Support ARIA 1.1 switch role">
+ Mozilla Bug 1136563
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="textbox_autocomplete_inline" role="textbox" aria-autocomplete="inline"></div>
+ <div id="textbox_autocomplete_list" role="textbox" aria-autocomplete="list"></div>
+ <div id="textbox_autocomplete_both" role="textbox" aria-autocomplete="both"></div>
+ <div id="combobox_autocomplete_inline" role="combobox" aria-autocomplete="inline"></div>
+ <div id="combobox_autocomplete_list" role="combobox" aria-autocomplete="list"></div>
+ <div id="combobox_autocomplete_both" role="combobox" aria-autocomplete="both"></div>
+
+ <input id="htmltext_autocomplete_list" type="text" aria-autocomplete="list" />
+ <textarea id="htmltextarea_autocomplete_list" aria-autocomplete="list"></textarea>
+
+ <div id="textbox_busy_false" role="textbox" aria-busy="false"></div>
+ <div id="textbox_busy_true" role="textbox" aria-busy="true"></div>
+ <div id="textbox_busy_error" role="textbox" aria-busy="error"></div>
+
+ <div id="combobox" role="combobox">combobox</div>
+ <div id="combobox_expanded" role="combobox"
+ aria-expanded="true">combobox</div>
+
+ <input type="checkbox" id="check1" value="I agree" checked="true"/>
+
+ <div id="aria_checked_checkbox" role="checkbox" aria-checked="true">
+ I agree
+ </div>
+
+ <div id="aria_mixed_checkbox" role="checkbox" aria-checked="mixed">
+ I might agree
+ </div>
+
+ <div id="aria_checked_switch" role="switch" aria-checked="true">
+ I am switched on
+ </div>
+
+ <div id="aria_mixed_switch" role="switch" aria-checked="mixed">
+ I am unsupported
+ </div>
+
+ <div id="aria_modal" aria-modal="true">modal stuff</div>
+ <div id="aria_modal_false" aria-modal="false">non modal stuff</div>div>
+ <div id="aria_multiline_textbox" role="textbox" aria-multiline="true"></div>
+ <div id="aria_multiselectable_listbox" role="listbox" aria-multiselectable="true"></div>
+ <div id="aria_pressed_button" role="button" aria-pressed="true">Button</div>
+ <button id="aria_pressed_native_button" aria-pressed="true">Button</button>
+
+ <div id="aria_readonly_textbox"
+ role="textbox" aria-readonly="true">This text should be readonly</div>
+
+ <div id="aria_grid_default" role="grid">
+ <div role="row">
+ <div id="aria_grid_default_colheader_readonly"
+ role="columnheader" aria-readonly="true">colheader1</div>
+ <div id="aria_grid_default_colheader_inherited"
+ role="columnheader">colheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_grid_default_rowheader_readonly"
+ role="rowheader" aria-readonly="true">rowheader1</div>
+ <div id="aria_grid_default_rowheader_inherited"
+ role="rowheader">rowheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_grid_default_cell_readonly"
+ role="gridcell" aria-readonly="true">gridcell1</div>
+ <div id="aria_grid_default_cell_inherited"
+ role="gridcell">gridcell2</div>
+ </div>
+ </div>
+
+ <div id="aria_grid_readonly" role="grid" aria-readonly="true">
+ <div role="row">
+ <div id="aria_grid_readonly_colheader_editable"
+ role="columnheader" aria-readonly="false">colheader1</div>
+ <div id="aria_grid_readonly_colheader_inherited"
+ role="columnheader">colheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_grid_readonly_rowheader_editable"
+ role="rowheader" aria-readonly="false">rowheader1</div>
+ <div id="aria_grid_readonly_rowheader_inherited"
+ role="rowheader">rowheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_grid_readonly_cell_editable"
+ role="gridcell" aria-readonly="false">gridcell1</div>
+ <div id="aria_grid_readonly_cell_inherited"
+ role="gridcell">gridcell2</div>
+ </div>
+ </div>
+
+ <div id="aria_treegrid_default" role="grid">
+ <div role="row">
+ <div id="aria_treegrid_default_colheader_readonly"
+ role="columnheader" aria-readonly="true">colheader1</div>
+ <div id="aria_treegrid_default_colheader_inherited"
+ role="columnheader">colheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_treegrid_default_rowheader_readonly"
+ role="rowheader" aria-readonly="true">rowheader1</div>
+ <div id="aria_treegrid_default_rowheader_inherited"
+ role="rowheader">rowheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_treegrid_default_cell_readonly"
+ role="gridcell" aria-readonly="true">gridcell1</div>
+ <div id="aria_treegrid_default_cell_inherited"
+ role="gridcell">gridcell2</div>
+ </div>
+ </div>
+
+ <div id="aria_treegrid_readonly" role="grid" aria-readonly="true">
+ <div role="row">
+ <div id="aria_treegrid_readonly_colheader_editable"
+ role="columnheader" aria-readonly="false">colheader1</div>
+ <div id="aria_treegrid_readonly_colheader_inherited"
+ role="columnheader">colheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_treegrid_readonly_rowheader_editable"
+ role="rowheader" aria-readonly="false">rowheader1</div>
+ <div id="aria_treegrid_readonly_rowheader_inherited"
+ role="rowheader">rowheader2</div>
+ </div>
+ <div role="row">
+ <div id="aria_treegrid_readonly_cell_editable"
+ role="gridcell" aria-readonly="false">gridcell1</div>
+ <div id="aria_treegrid_readonly_cell_inherited"
+ role="gridcell">gridcell2</div>
+ </div>
+ </div>
+
+ <div role="listbox">
+ <div id="aria_selectable_listitem" role="option" aria-selected="true">Item1</div>
+ </div>
+
+ <!-- Test that aria-disabled state gets propagated to all descendants -->
+ <div id="group" role="group" aria-disabled="true">
+ <button>hi</button>
+ <div tabindex="0" role="listbox" aria-activedescendant="item1"
+ aria-owns="item5">
+ <div role="option" id="item1">Item 1</div>
+ <div role="option" id="item2">Item 2</div>
+ <div role="option" id="item3">Item 3</div>
+ <div role="option" id="item4">Item 4</div>
+ </div>
+ <div role="slider" tabindex="0">A slider</div>
+ </div>
+ <div role="option" id="item5">Item 5</div>
+
+ <!-- Test active state -->
+ <div id="as_listbox" tabindex="0" role="listbox"
+ aria-activedescendant="as_item1">
+ <div role="option" id="as_item1">Item 1</div>
+ <div role="option" id="as_item2">Item 2</div>
+ </div>
+
+ <!-- universal ARIA properties should be inherited by text field of file input -->
+ <input type="file" id="fileinput"
+ aria-busy="true"
+ aria-disabled="true"
+ aria-required="true"
+ aria-haspopup="true"
+ aria-invalid="true">
+
+ <div id="offscreen_log" role="log" class="offscreen">
+ <div id="aria_offscreen_textbox" role="textbox" aria-readonly="true">This text should be offscreen</div>
+ </div>
+
+ <a id="aria_menuitem_link" role="menuitem" href="foo">menuitem</a>
+ <a id="aria_button_link" role="button" href="foo">button</a>
+ <a id="aria_checkbox_link" role="checkbox" href="foo">checkbox</a>
+
+ <!-- strange edge case: please don't do this in the wild -->
+ <a id="aria_link_link" role="link" href="foo">link</a>
+ <a id="aria_link_anchor" role="link" name="link_anchor">link</a>
+
+ <!-- landmarks: links -->
+ <a id="aria_application_link" role="application" href="foo">app</a>
+ <a id="aria_main_link" role="main" href="foo">main</a>
+ <a id="aria_navigation_link" role="navigation" href="foo">nav</a>
+
+ <!-- landmarks: anchors -->
+ <a id="aria_application_anchor" role="application" name="app_anchor">app</a>
+ <a id="aria_main_anchor" role="main" name="main_anchor">main</a>
+ <a id="aria_navigation_anchor" role="navigation" name="nav_anchor">nav</a>
+
+ <!-- aria-orientation -->
+ <div id="aria_combobox" role="combobox">combobox</div>
+ <div id="aria_hcombobox" role="combobox" aria-orientation="horizontal">horizontal combobox</div>
+ <div id="aria_vcombobox" role="combobox" aria-orientation="vertical">vertical combobox</div>
+ <div id="aria_listbox" role="listbox">listbox</div>
+ <div id="aria_hlistbox" role="listbox" aria-orientation="horizontal">horizontal listbox</div>
+ <div id="aria_vlistbox" role="listbox" aria-orientation="vertical">vertical listbox</div>
+ <div id="aria_menu" role="menu">menu</div>
+ <div id="aria_hmenu" role="menu" aria-orientation="horizontal">horizontal menu</div>
+ <div id="aria_vmenu" role="menu" aria-orientation="vertical">vertical menu</div>
+ <div id="aria_menubar" role="menubar">menubar</div>
+ <div id="aria_hmenubar" role="menubar" aria-orientation="horizontal">horizontal menubar</div>
+ <div id="aria_vmenubar" role="menubar" aria-orientation="vertical">vertical menubar</div>
+ <div id="aria_radiogroup" role="radiogroup">radiogroup</div>
+ <div id="aria_hradiogroup" role="radiogroup" aria-orientation="horizontal">horizontal radiogroup</div>
+ <div id="aria_vradiogroup" role="radiogroup" aria-orientation="vertical">vertical radiogroup</div>
+ <div id="aria_scrollbar" role="scrollbar">scrollbar</div>
+ <div id="aria_hscrollbar" role="scrollbar" aria-orientation="horizontal">horizontal scrollbar</div>
+ <div id="aria_vscrollbar" role="scrollbar" aria-orientation="vertical">vertical scrollbar</div>
+ <div id="aria_separator" role="separator">separator</div>
+ <div id="aria_hseparator" role="separator" aria-orientation="horizontal">horizontal separator</div>
+ <div id="aria_vseparator" role="separator" aria-orientation="vertical">vertical separator</div>
+ <div id="aria_slider" role="slider">slider</div>
+ <div id="aria_hslider" role="slider" aria-orientation="horizontal">horizontal slider</div>
+ <div id="aria_vslider" role="slider" aria-orientation="vertical">vertical slider</div>
+
+ <div id="aria_tablist" role="tablist">tablist</div>
+ <div id="aria_htablist" role="tablist" aria-orientation="horizontal">horizontal tablist</div>
+ <div id="aria_vtablist" role="tablist" aria-orientation="vertical">vertical tablist</div>
+ <div id="aria_toolbar" role="toolbar">toolbar</div>
+ <div id="aria_htoolbar" role="toolbar" aria-orientation="horizontal">horizontal toolbar</div>
+ <div id="aria_vtoolbar" role="toolbar" aria-orientation="vertical">vertical toolbar</div>
+ <div id="aria_tree" role="tree">tree</div>
+ <div id="aria_htree" role="tree" aria-orientation="horizontal">horizontal tree</div>
+ <div id="aria_vtree" role="tree" aria-orientation="vertical">vertical tree</div>
+ <div id="aria_treegrid" role="treegrid">treegrid</div>
+ <div id="aria_htreegrid" role="treegrid" aria-orientation="horizontal">horizontal treegrid</div>
+ <div id="aria_vtreegrid" role="treegrid" aria-orientation="vertical">vertical treegrid</div>
+
+ <!-- indeterminate ARIA progressbars should expose mixed state -->
+ <div id="aria_progressbar" role="progressbar"></div>
+ <div id="aria_progressbar_valuenow" role="progressbar" aria-valuenow="1"></div>
+ <div id="aria_progressbar_valuetext" role="progressbar" aria-valuetext="value"></div>
+
+ <!-- ARIA select widget should expose focusable state regardless the way they manage its children -->
+ <div id="aria_listbox" role="listbox">
+ <div role="option" tabindex="0">A</div>
+ <div role="option" tabindex="0">a</div>
+ </div>
+ <div id="aria_grid" role="grid">
+ <div role="row"><div role="gridcell" tabindex="0">B</div></div></div>
+ <div role="row"><div role="gridcell" tabindex="0">b</div></div></div>
+ <div id="aria_tree" role="tree">
+ <div role="treeitem" tabindex="0">C</div>
+ <div role="treeitem" tabindex="0">c</div>
+ </div>
+ <div id="aria_treegrid" role="treegrid">
+ <div role="row"><div role="gridcell" tabindex="0">D</div></div>
+ <div role="row"><div role="gridcell" tabindex="0">d</div></div>
+ </div>
+ <div id="aria_listbox_disabled" role="listbox" aria-disabled="true">
+ <div role="option">E</div>
+ <div role="option">e</div>
+ </div>
+ <div id="aria_grid_disabled" role="grid" aria-disabled="true">
+ <div role="row"><div role="gridcell">F</div></div>
+ <div role="row"><div role="gridcell">f</div></div>
+ </div>
+ <div id="aria_tree_disabled" role="tree" aria-disabled="true">
+ <div role="treeitem">G</div>
+ <div role="treeitem">g</div>
+ </div>
+ <div id="aria_treegrid_disabled" role="treegrid" aria-disabled="true">
+ <div role="row"><div role="gridcell">H</div></div>
+ <div role="row"><div role="gridcell">h</div></div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_aria.xul b/accessible/tests/mochitest/states/test_aria.xul
new file mode 100644
index 000000000..b15cc7781
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_aria.xul
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL ARIA state tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // aria-pressed
+ testStates("pressed_button", STATE_PRESSED, 0, STATE_CHECKABLE);
+ testStates("pressed_menu_button", STATE_PRESSED | STATE_HASPOPUP, 0, STATE_CHECKABLE);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1033283"
+ title="Expose pressed state on XUL menu toggle buttons">
+ Mozilla Bug 1033283
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <button id="pressed_button" aria-pressed="true" label="I am pressed" />
+ <button id="pressed_menu_button" aria-pressed="true" label="I am pressed" type="menu-button">
+ <menupopup>
+ <menuitem label="I am a menu item" />
+ </menupopup>
+ </button>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/test_aria_imgmap.html b/accessible/tests/mochitest/states/test_aria_imgmap.html
new file mode 100644
index 000000000..1dfe355bd
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_aria_imgmap.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test usemap elements and ARIA</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ function doPreTest()
+ {
+ waitForImageMap("imagemap", doTest);
+ }
+
+ function doTest()
+ {
+ var imageMap = getAccessible("imagemap");
+
+ var t1 = imageMap.getChildAt(0);
+ testStates(t1, 0, EXT_STATE_EDITABLE, STATE_LINKED);
+ var t2 = imageMap.getChildAt(1);
+ testStates(t2, 0, EXT_STATE_EDITABLE, STATE_LINKED);
+ var rb1 = imageMap.getChildAt(2);
+ testStates(rb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
+ var rb2 = imageMap.getChildAt(3);
+ testStates(rb2, STATE_CHECKABLE, 0, STATE_CHECKED, STATE_LINKED);
+ var cb1 = imageMap.getChildAt(4);
+ testStates(cb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
+ var cbox = imageMap.getChildAt(5);
+ testStates(cbox, (STATE_HASPOPUP | STATE_COLLAPSED),
+ EXT_STATE_EXPANDABLE, STATE_LINKED);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body>
+
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291"
+ title="ARIA states on image maps">
+Mozilla Bug 548291
+</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+</pre>
+
+<img id="imagemap" src="../formimage.png" width="219" height="229" border="0" usemap="#ariaMap">
+<map id="ariaMap" name="ariaMap">
+ <area id="t1" role="textbox" shape="rect" tabindex="0" alt="" title="first name" coords="4,20,108,48" href="#" />
+ <area id="t2" role="textbox" shape="rect" alt="" title="last name" coords="111,21,215,50" href="#" />
+ <area id="rb1" role="radio" aria-checked="true" shape="circle" alt="" title="male" coords="60,75,11" href="#" />
+ <area id="rb2" role="radio" shape="circle" alt="" title="female" coords="73,94,11" href="#" />
+ <area id="cb1" role="checkbox" aria-checked="true" shape="rect" alt="" title="have bike" coords="95,123,118,145" href="#" />
+ <area id="cbox" role="combobox" shape="rect" alt="" title="bike model" coords="120,124,184,146" href="#" />
+ <area id="cb2" role="checkbox" shape="rect" alt="" title="have car" coords="90,145,114,164" href="#" />
+ <area id="cb3" role="checkbox" shape="rect" alt="" title="have airplane" coords="130,163,152,184" href="#" />
+ <area id="b1" role="button" shape="rect" alt="" title="submit" coords="4,198,67,224" href="#" />
+</map>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_aria_widgetitems.html b/accessible/tests/mochitest/states/test_aria_widgetitems.html
new file mode 100644
index 000000000..81bce4194
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_aria_widgetitems.html
@@ -0,0 +1,162 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test ARIA tab accessible selected state</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function focusARIAItem(aID, aIsSelected)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function focusARIAItem_invoke()
+ {
+ this.DOMNode.focus();
+ }
+
+ this.check = function focusARIAItem_check(aEvent)
+ {
+ testStates(this.DOMNode, aIsSelected ? STATE_SELECTED : 0, 0,
+ aIsSelected ? 0 : STATE_SELECTED);
+ }
+
+ this.getID = function focusARIAItem_getID()
+ {
+ return "Focused ARIA widget item with aria-selected='" +
+ (aIsSelected ? "true', should" : "false', shouldn't") +
+ " have selected state on " + prettyName(aID);
+ }
+ }
+
+ function focusActiveDescendantItem(aItemID, aWidgetID, aIsSelected)
+ {
+ this.DOMNode = getNode(aItemID);
+ this.widgetDOMNode = getNode(aWidgetID);
+
+ this.invoke = function focusActiveDescendantItem_invoke()
+ {
+ this.widgetDOMNode.setAttribute("aria-activedescendant", aItemID);
+ this.widgetDOMNode.focus();
+ }
+
+ this.check = function focusActiveDescendantItem_check(aEvent)
+ {
+ testStates(this.DOMNode, aIsSelected ? STATE_SELECTED : 0, 0,
+ aIsSelected ? 0 : STATE_SELECTED);
+ }
+
+ this.getID = function tabActiveDescendant_getID()
+ {
+ return "ARIA widget item managed by activedescendant " +
+ (aIsSelected ? "should" : "shouldn't") +
+ " have the selected state on " + prettyName(aItemID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ // aria-selected
+ testStates("aria_tab1", 0, 0, STATE_SELECTED);
+ testStates("aria_tab2", STATE_SELECTED);
+ testStates("aria_tab3", 0, 0, STATE_SELECTED);
+ testStates("aria_option1", 0, 0, STATE_SELECTED);
+ testStates("aria_option2", STATE_SELECTED);
+ testStates("aria_option3", 0, 0, STATE_SELECTED);
+ testStates("aria_treeitem1", 0, 0, STATE_SELECTED);
+ testStates("aria_treeitem2", STATE_SELECTED);
+ testStates("aria_treeitem3", 0, 0, STATE_SELECTED);
+
+ // selected state when widget item is focused
+ gQueue = new eventQueue(EVENT_FOCUS);
+
+ gQueue.push(new focusARIAItem("aria_tab1", true));
+ gQueue.push(new focusARIAItem("aria_tab2", true));
+ gQueue.push(new focusARIAItem("aria_tab3", false));
+ gQueue.push(new focusARIAItem("aria_option1", true));
+ gQueue.push(new focusARIAItem("aria_option2", true));
+ gQueue.push(new focusARIAItem("aria_option3", false));
+ gQueue.push(new focusARIAItem("aria_treeitem1", true));
+ gQueue.push(new focusARIAItem("aria_treeitem2", true));
+ gQueue.push(new focusARIAItem("aria_treeitem3", false));
+
+ // selected state when widget item is focused (by aria-activedescendant)
+ gQueue.push(new focusActiveDescendantItem("aria_tab5", "aria_tablist2", true));
+ gQueue.push(new focusActiveDescendantItem("aria_tab6", "aria_tablist2", true));
+ gQueue.push(new focusActiveDescendantItem("aria_tab4", "aria_tablist2", false));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=653601"
+ title="aria-selected ignored for ARIA tabs">
+ Mozilla Bug 653601
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=526703"
+ title="Focused widget item should expose selected state by default">
+ Mozilla Bug 526703
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- tab -->
+ <div id="aria_tablist" role="tablist">
+ <div id="aria_tab1" role="tab" tabindex="0">unselected tab</div>
+ <div id="aria_tab2" role="tab" tabindex="0" aria-selected="true">selected tab</div>
+ <div id="aria_tab3" role="tab" tabindex="0" aria-selected="false">focused explicitly unselected tab</div>
+ </div>
+
+ <!-- listbox -->
+ <div id="aria_listbox" role="listbox">
+ <div id="aria_option1" role="option" tabindex="0">unselected option</div>
+ <div id="aria_option2" role="option" tabindex="0" aria-selected="true">selected option</div>
+ <div id="aria_option3" role="option" tabindex="0" aria-selected="false">focused explicitly unselected option</div>
+ </div>
+
+ <!-- tree -->
+ <div id="aria_tree" role="tree">
+ <div id="aria_treeitem1" role="treeitem" tabindex="0">unselected treeitem</div>
+ <div id="aria_treeitem2" role="treeitem" tabindex="0" aria-selected="true">selected treeitem</div>
+ <div id="aria_treeitem3" role="treeitem" tabindex="0" aria-selected="false">focused explicitly unselected treeitem</div>
+ </div>
+
+ <!-- tab managed by active-descendant -->
+ <div id="aria_tablist2" role="tablist" tabindex="0">
+ <div id="aria_tab4" role="tab" aria-selected="false">focused explicitly unselected tab</div>
+ <div id="aria_tab5" role="tab">initially selected tab</div>
+ <div id="aria_tab6" role="tab">later selected tab</div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_buttons.html b/accessible/tests/mochitest/states/test_buttons.html
new file mode 100644
index 000000000..52c642ce5
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_buttons.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML button accessible states</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Default state.
+ testStates("f1_image", STATE_DEFAULT | STATE_FOCUSABLE);
+ testStates("f2_submit", STATE_DEFAULT | STATE_FOCUSABLE);
+ testStates("f3_submitbutton", STATE_DEFAULT | STATE_FOCUSABLE);
+ testStates("f3_disabled_reset", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE, 0);
+ testStates("f4_button", STATE_FOCUSABLE, 0, STATE_DEFAULT);
+ testStates("f4_disabled_button", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE, 0);
+ testStates("f4_image1", STATE_DEFAULT | STATE_FOCUSABLE);
+ testStates("f4_image2", STATE_FOCUSABLE, 0, STATE_DEFAULT);
+ testStates("f4_submit", STATE_FOCUSABLE, 0, STATE_DEFAULT);
+ testStates("f4_submitbutton", STATE_FOCUSABLE, 0, STATE_DEFAULT);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=664142"
+ title="DEFAULT state exposed incorrectly for HTML">
+ Mozilla Bug 664142
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p>A form with an image button</p>
+ <form name="form1" method="get">
+ <input type="text" name="hi">
+
+ <input id="f1_image" type="image" value="image-button">
+ </form>
+
+ <p>A form with a submit button:</p>
+ <form name="form2" method="get">
+ <input type="text" name="hi">
+ <input id="f2_submit" type="submit">
+ </form>
+
+ <p>A form with a HTML4 submit button:</p>
+ <form name="form3" method="get">
+ <input type="text" name="hi">
+ <button id="f3_submitbutton" type="submit">submit</button>
+ <button id="f3_disabled_reset" type="reset" disabled>reset</button>
+ </form>
+
+ <p>A form with normal button, two image buttons, submit button,
+ HTML4 submit button:</p>
+ <form name="form4" method="get">
+ <input type="text" name="hi">
+ <input id="f4_button" type="button" value="normal" name="normal-button">
+ <input id="f4_disabled_button" type="button" value="disabled" name="disabled-button" disabled>
+ <input id="f4_image1" type="image" value="image-button1" name="image-button1">
+ <input id="f4_image2" type="image" value="image-button2" name="image-button2">
+ <input id="f4_submit" type="submit" value="real-submit" name="real-submit">
+ <button id="f4_submitbutton" type="submit">submit</button>
+ </form>
+
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_controls.html b/accessible/tests/mochitest/states/test_controls.html
new file mode 100644
index 000000000..7369f6d01
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_controls.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML control states</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Undetermined progressbar (no value or aria-value attribute): mixed state
+ testStates("progress", STATE_MIXED);
+ // Determined progressbar (has value): shouldn't have mixed state
+ testStates("progress2", 0, 0, STATE_MIXED);
+ // Determined progressbar (has aria-value): shouldn't have mixed state
+ // testStates("progress3", 0, 0, STATE_MIXED);
+ todo(false, "we should respect ARIA");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=670853"
+ title="Bug 670853 - undetermined progressmeters should expose mixed state">
+ Mozilla Bug 670853
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <progress id="progress"></progress>
+ <progress id="progress2" value="1"></progress>
+ <progress id="progress3" aria-valuenow="1"></progress>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_controls.xul b/accessible/tests/mochitest/states/test_controls.xul
new file mode 100644
index 000000000..83de45256
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_controls.xul
@@ -0,0 +1,182 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL input control state tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function openColorpicker(aID)
+ {
+ this.popupNode = getNode(aID).mPicker.parentNode;
+ this.popup = getAccessible(this.popupNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.popupNode)
+ ];
+
+ this.invoke = function openColorpicker_invoke()
+ {
+ getNode(aID).showPopup();
+ }
+
+ this.finalCheck = function openColorpicker_finalCheck()
+ {
+ testStates(this.popup.firstChild,
+ STATE_FOCUSABLE | STATE_SELECTABLE, 0,
+ STATE_UNAVAILABLE);
+ }
+
+ this.getID = function openColorpicker_getID()
+ {
+ return "open colorpicker";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ testStates("checkbox", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("checkbox2", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+ testStates("radiogroup", 0, 0, STATE_FOCUSABLE | STATE_UNAVAILABLE);
+ testStates("radio", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("radio-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+ testStates("radiogroup-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+ testStates("radio-disabledradiogroup", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+ testStates("button", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("button-disabled", STATE_UNAVAILABLE, 0 , STATE_FOCUSABLE);
+ testStates("colorpicker", STATE_FOCUSABLE | STATE_HASPOPUP, 0, STATE_UNAVAILABLE);
+ testStates("colorpicker-disabled", STATE_HASPOPUP, 0, STATE_FOCUSABLE);
+ testStates("combobox", STATE_FOCUSABLE | STATE_HASPOPUP, 0, STATE_UNAVAILABLE);
+ testStates("combobox-disabled", STATE_UNAVAILABLE | STATE_HASPOPUP, 0, STATE_FOCUSABLE);
+ testStates("listbox", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("listitem", STATE_FOCUSABLE | STATE_SELECTABLE, 0, STATE_UNAVAILABLE);
+ testStates("listbox-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+ testStates("listitem-disabledlistbox", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+ testStates("menubar", 0, 0, STATE_FOCUSABLE);
+ testStates("menu", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("menu-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE);
+ testStates("scale", STATE_FOCUSABLE, 0, STATE_UNAVAILABLE);
+ testStates("scale-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE);
+ testStates("tab", STATE_FOCUSABLE | STATE_SELECTABLE | STATE_SELECTED, 0, STATE_UNAVAILABLE);
+ testStates("tab-disabled", STATE_UNAVAILABLE, 0, STATE_FOCUSABLE | STATE_SELECTABLE | STATE_SELECTED);
+
+ gQueue = new eventQueue();
+ gQueue.push(new openColorpicker("colorpicker"));
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=599163"
+ title="check disabled state instead of attribute">
+ Mozilla Bug 599163
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=756983"
+ title="Isolate focusable and unavailable states from State()">
+ Mozilla Bug 756983
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <checkbox id="checkbox" checked="true" label="Steak"/>
+ <checkbox id="checkbox2" checked="true" label="Salad" disabled="true"/>
+
+ <radiogroup id="radiogroup">
+ <radio id="radio" label="Orange"/>
+ <radio id="radio-disabled" selected="true" label="Violet" disabled="true"/>
+ </radiogroup>
+
+ <radiogroup id="radiogroup-disabled" disabled="true">
+ <radio id="radio-disabledradiogroup" label="Orange"/>
+ <radio id="violet2" selected="true" label="Violet"/>
+ </radiogroup>
+
+ <button id="button" value="button"/>
+ <button id="button-disabled" disabled="true" value="button"/>
+
+ <colorpicker id="colorpicker" type="button"/>
+ <colorpicker id="colorpicker-disabled" type="button" disabled="true"/>
+
+ <menulist id="combobox">
+ <menupopup>
+ <menuitem label="item1"/>
+ </menupopup>
+ </menulist>
+
+ <menulist id="combobox-disabled" disabled="true">
+ <menupopup>
+ <menuitem label="item1"/>
+ </menupopup>
+ </menulist>
+
+ <listbox id="listbox">
+ <listitem id="listitem" label="list item"/>
+ </listbox>
+
+ <listbox id="listbox-disabled" disabled="true">
+ <listitem id="listitem-disabledlistbox" label="list item"/>
+ </listbox>
+
+ <toolbox>
+ <menubar id="menubar">
+ <menu id="menu" label="menu1">
+ <menupopup>
+ <menuitem id="menu1-item1" label="menuitem1.1"/>
+ </menupopup>
+ </menu>
+ <menu id="menu-disabled" label="menu2" disabled="true">
+ <menupopup>
+ <menuitem id="menu-disabled-item1" label="menuitem2.1"/>
+ </menupopup>
+ </menu>
+ </menubar>
+ </toolbox>
+
+ <scale id="scale" min="1" max="10"/>
+ <scale id="scale-disabled" min="1" max="10" disabled="true"/>
+
+ <tabbox>
+ <tabs>
+ <tab id="tab" label="tab1" tooltip="tooltip"/>
+ <tab id="tab-disabled" label="tab1" disabled="true"/>
+ </tabs>
+ <tabpanels>
+ <tabpanel/>
+ <tabpanel/>
+ </tabpanels>
+ </tabbox>
+
+ <tooltip id="tooltip"><description>tooltip</description></tooltip>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/test_doc.html b/accessible/tests/mochitest/states/test_doc.html
new file mode 100644
index 000000000..83edd9dac
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_doc.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>states of document</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Bug 566542: root accesible should expose active state when focused.
+ testStates(getRootAccessible(), 0, EXT_STATE_ACTIVE);
+
+ // Bug 509696, 607219.
+ testStates(document, STATE_READONLY, 0); // role=""
+
+ document.body.setAttribute("role","banner"); // no platform mapping
+ testStates(document, STATE_READONLY);
+ document.body.setAttribute("role","foo"); // bogus role
+ testStates(document, STATE_READONLY);
+ document.body.removeAttribute("role");
+ testStates(document, STATE_READONLY);
+
+ // Bugs 454997 and 467387
+ testStates(document, STATE_READONLY);
+ testStates("document", STATE_READONLY);
+ testStates("editable_document", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ document.designMode = "on";
+
+ testStates(document, 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("p", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("document", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("editable_document", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ document.designMode = "off";
+
+ testStates(document, STATE_READONLY);
+ testStates("document", STATE_READONLY);
+ testStates("editable_document", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body role="">
+
+ <a target="_blank"
+ title="<body contenteditable='true'> exposed incorrectly"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=454997">Mozilla Bug 454997</a>
+ <a target="_blank"
+ title="nsIAccessible states tests of editable document"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387">Mozilla Bug 467387</a>
+ <a target="_blank"
+ title="Role attribute on body with empty string causes DocAccessible not to have read-only state."
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=509696">Mozilla Bug 509696</a>
+ <a target="_blank"
+ title="Frame for firefox does not implement the state "active" when firefox is the active frame"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566542">Mozilla Bug 566542</a>
+ <a target="_blank"
+ title="Dynamic role attribute change on body doesn't affect on document role"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=607219">Mozilla Bug 607219</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p">hello</p>
+
+ <div id="document" role="document">document</div>
+ <div id="editable_document" role="document" contentEditable="true">editable document</doc>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_doc_busy.html b/accessible/tests/mochitest/states/test_doc_busy.html
new file mode 100644
index 000000000..8e1422da1
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_doc_busy.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>states of document</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debugging stuff
+
+ function loadFile()
+ {
+ var eventSeq = [
+ new stateChangeChecker(STATE_BUSY, false, true, document, null, false, true),
+ new stateChangeChecker(STATE_BUSY, false, false, document)
+ ];
+ defineScenario(this, eventSeq); // both events were fired
+
+ var unexpectedEventSeq = [
+ new stateChangeChecker(STATE_BUSY, false, true, document),
+ new stateChangeChecker(STATE_BUSY, false, false, document)
+ ];
+ defineScenario(this, [], unexpectedEventSeq); // events were coalesced
+
+ this.invoke = function loadFile_invoke()
+ {
+ synthesizeMouse(getNode("link"), 1, 1, {});
+ }
+
+ this.getID = function loadFile_getID()
+ {
+ return "load file: state busy change events on document";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ // State busy change event on file loading.
+ //enableLogging("docload"); // debugging
+ gQueue = new eventQueue();
+ gQueue.push(new loadFile());
+ //gQueue.onFinish = function() { disableLogging(); } // debugging
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ title="Missing busy state change event when downloading files"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=446469">Bug 446469</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <a id="link" href="http://example.com/a11y/accessible/tests/mochitest/dumbfile.zip">a file</a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_docarticle.html b/accessible/tests/mochitest/states/test_docarticle.html
new file mode 100644
index 000000000..1ac62ee4b
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_docarticle.html
@@ -0,0 +1,80 @@
+<html>
+<head>
+ <title>states of document article</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var docAcc = getAccessible(document, [nsIAccessibleDocument]);
+ if (docAcc) {
+ testStates(docAcc, STATE_READONLY);
+ testStates("aria_article", STATE_READONLY);
+ testStates("editable_aria_article", 0, EXT_STATE_EDITABLE,
+ STATE_READONLY);
+ testStates("article", STATE_READONLY);
+ testStates("editable_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ document.designMode = "on";
+
+ testStates(docAcc, 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("editable_aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("editable_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ document.designMode = "off";
+
+ testStates(docAcc, STATE_READONLY);
+ testStates("aria_article", STATE_READONLY);
+ testStates("editable_aria_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("article", STATE_READONLY);
+ testStates("editable_article", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ }
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body role="article">
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387"
+ title="Expose non-editable documents as readonly, regardless of role">
+ Mozilla Bug 467387
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=613502"
+ title="Map <article> like we do aria role article">
+ Mozilla Bug 613502
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="aria_article" role="article">aria article</div>
+ <div id="editable_aria_article" role="article" contentEditable="true">
+ editable aria article</div>
+
+ <article id="article">article</article>
+ <article id="editable_article" contentEditable="true">
+ editable article</article>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_editablebody.html b/accessible/tests/mochitest/states/test_editablebody.html
new file mode 100644
index 000000000..3b1486876
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_editablebody.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=454997
+-->
+<head>
+ <title>nsIAccessible states tests of contenteditable body</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ testStates(document, 0, EXT_STATE_EDITABLE);
+ testStates("p", 0, EXT_STATE_EDITABLE);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body contentEditable="true">
+
+ <a target="_blank"
+ title="nsIAccessible states tests of contenteditable body"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=454997">Mozilla Bug 454997</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p">hello</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_expandable.xul b/accessible/tests/mochitest/states/test_expandable.xul
new file mode 100644
index 000000000..1e210a0ef
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_expandable.xul
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<!-- Firefox searchbar -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+ type="text/css"?>
+<!-- SeaMonkey searchbar -->
+<?xml-stylesheet href="chrome://navigator/content/navigator.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Expanded state change events tests for comboboxes and autocompletes.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+ <script type="application/javascript"
+ src="../autocomplete.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new openCombobox("menulist"));
+ gQueue.push(new closeCombobox("menulist"));
+
+ todo(false, "Autocompletes don't fire expanded state change events when popup open. See bug 688480!");
+ //gQueue.push(new openCombobox("autocomplete"));
+ //gQueue.push(new closeCombobox("autocomplete"));
+
+ // XXX: searchbar doesn't fire state change events because accessible
+ // parent of combobox_list accessible is pushbutton accessible.
+ //var searchbar = document.getElementById("searchbar");
+ //gQueue.push(new openHideCombobox(searchbar, true));
+ //gQueue.push(new openHideCombobox(searchbar, false));
+ todo(false, "Enable states test for XUL searchbar widget!");
+
+ gQueue.onFinish = function()
+ {
+ // unregister 'test-a11y-search' autocomplete search
+ shutdownAutoComplete();
+ }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ // This is the hacks needed to use a searchbar without browser.js.
+ function getBrowser()
+ {
+ return {
+ mCurrentBrowser: { engines: new Array() }
+ };
+ }
+ var BrowserSearch = {
+ updateOpenSearchBadge: function() {}
+ };
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Register 'test-a11y-search' autocomplete search.
+ // XPFE AutoComplete needs to register early.
+ initAutoComplete([ "hello", "hi" ],
+ [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;" flex="1">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467057"
+ title="xul menulist doesn't fire expand/collapse state change events">
+ Mozilla Bug 467057
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menulist id="menulist">
+ <menupopup>
+ <menuitem label="item1"/>
+ <menuitem label="item2"/>
+ <menuitem label="item3"/>
+ </menupopup>
+ </menulist>
+
+ <textbox id="autocomplete" type="autocomplete"
+ autocompletesearch="test-a11y-search"/>
+
+ <searchbar id="searchbar"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/test_frames.html b/accessible/tests/mochitest/states/test_frames.html
new file mode 100644
index 000000000..cb3bca844
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_frames.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>frame based document testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 2);
+ }
+
+ function doTest()
+ {
+ frameDoc = document.getElementById("frame_doc").contentDocument;
+ frameDocArticle = document.getElementById("frame_doc_article").contentDocument;
+ frameDocCheckbox = document.getElementById("frame_doc_checkbox").contentDocument;
+ frameDocTextbox = document.getElementById("frame_doc_textbox").contentDocument;
+
+ testStates(frameDoc, STATE_READONLY, 0, 0, 0,
+ "test1: frameDoc");
+ testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
+ "test1: frameDocArticle");
+ testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
+ "test1: frameDocCheckbox");
+ testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
+ "test1: frameDocTextbox");
+ frameDoc.designMode = "on";
+ testStates(frameDoc, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
+ "test2: frameDoc");
+ testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
+ "test2: frameDocArticle");
+ testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
+ "test2: frameDocCheckbox");
+ testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
+ "test2: frameDocTextbox");
+
+ frameDocArticle.designMode = "on";
+ testStates(frameDocArticle, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
+ "test3: frameDocArticle");
+
+ frameDocCheckbox.designMode = "on";
+ testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
+ "test4: frameDocCheckbox");
+
+ // Replace iframe document body before the document accessible tree is
+ // created. Check the states are updated for new body.
+ var frameUpdateDoc =
+ document.getElementById("frame_updatedoc").contentDocument;
+ testStates(frameUpdateDoc, 0, EXT_STATE_EDITABLE,
+ STATE_READONLY, EXT_STATE_STALE, "test5: frameUpdateDoc");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467387"
+ title="Expose non-editable documents as readonly, regardless of role">
+ Mozilla Bug 467387
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=638106"
+ title="CKEditor document should be editable">
+ Mozilla Bug 638106
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe id="frame_doc" src="z_frames.html"></iframe>
+ <iframe id="frame_doc_article" src="z_frames_article.html"></iframe>
+ <iframe id="frame_doc_checkbox" src="z_frames_checkbox.html"></iframe>
+ <iframe id="frame_doc_textbox" src="z_frames_textbox.html"></iframe>
+ <iframe id="frame_updatedoc" src="z_frames_update.html"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_inputs.html b/accessible/tests/mochitest/states/test_inputs.html
new file mode 100644
index 000000000..20c6deaf8
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_inputs.html
@@ -0,0 +1,271 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML input states</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ ////////////////////////////////////////////////////////////////////////////
+ // 'editable' and 'multiline' states.
+ testStates("input", 0, EXT_STATE_EDITABLE, 0, EXT_STATE_MULTI_LINE);
+ testStates("textarea", 0, EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE);
+
+ testStates("input_readonly", 0, EXT_STATE_EDITABLE);
+ testStates("input_disabled", 0, EXT_STATE_EDITABLE);
+ testStates("textarea_readonly", 0, EXT_STATE_EDITABLE);
+ testStates("textarea_disabled", 0, EXT_STATE_EDITABLE);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 'required', 'readonly' and 'unavailable' states.
+ var maybe_required = ["input","search","radio","checkbox","textarea"];
+ var never_required = ["submit","button","reset","image"];
+
+ var i;
+ for (i in maybe_required) {
+ testStates(maybe_required[i],
+ STATE_FOCUSABLE, 0,
+ STATE_REQUIRED | STATE_READONLY | STATE_UNAVAILABLE);
+
+ testStates(maybe_required[i] + "_required",
+ STATE_FOCUSABLE | STATE_REQUIRED, 0,
+ STATE_UNAVAILABLE | STATE_READONLY);
+
+ var readonlyID = maybe_required[i] + "_readonly";
+ if (document.getElementById(readonlyID)) {
+ testStates(readonlyID,
+ STATE_FOCUSABLE | STATE_READONLY, 0,
+ STATE_UNAVAILABLE | STATE_REQUIRED);
+ }
+
+ testStates(maybe_required[i] + "_disabled",
+ STATE_UNAVAILABLE, 0,
+ STATE_FOCUSABLE | STATE_READONLY | STATE_REQUIRED);
+ }
+
+ for (i in never_required) {
+ testStates(never_required[i], 0, 0, STATE_REQUIRED | EXT_STATE_EDITABLE);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // inherited 'unavailable' state
+ testStates("f", STATE_UNAVAILABLE);
+ testStates("f_input", STATE_UNAVAILABLE);
+ testStates("f_input_disabled", STATE_UNAVAILABLE);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // inherited from file control
+ var fileBrowseButton = getAccessible("file").firstChild;
+ testStates(fileBrowseButton, STATE_UNAVAILABLE | STATE_REQUIRED);
+ // No states on the label.
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 'invalid' state
+ var invalid = ["pattern","email","url"];
+ for (i in invalid) {
+ testStates(invalid[i], STATE_INVALID);
+ testStates(invalid[i] + "2", 0, 0, STATE_INVALID);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // not 'invalid' state
+ // (per spec, min/maxlength are always valid until interactively edited)
+ var validInput = document.createElement("input");
+ validInput.maxLength = '0';
+ validInput.value = 'a';
+ ok(validInput.validity.valid,
+ "input should be valid despite maxlength (no interactive edits)");
+
+ var validInput2 = document.createElement("input");
+ validInput2.minLength = '1';
+ validInput2.value = '';
+ ok(validInput2.validity.valid,
+ "input should be valid despite minlength (no interactive edits)");
+
+ var valid = ["minlength","maxlength"];
+ for (i in valid) {
+ testStates(valid[i], 0, 0, STATE_INVALID);
+ testStates(valid[i] + "2", 0, 0, STATE_INVALID);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // 'invalid' state
+ // (per spec, min/maxlength validity is affected by interactive edits)
+ var mininp = document.getElementById("minlength");
+ mininp.focus();
+ mininp.setSelectionRange(mininp.value.length, mininp.value.length);
+ synthesizeKey("VK_BACK_SPACE", {});
+ ok(!mininp.validity.valid,
+ "input should be invalid after interactive edits");
+ testStates(mininp, STATE_INVALID);
+ // inputs currently cannot be made longer than maxlength interactively,
+ // so we're not testing that case.
+
+ ////////////////////////////////////////////////////////////////////////////
+ // autocomplete states
+ testStates("autocomplete-default", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-off", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-formoff", 0, 0, 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-list", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-list2", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-tel", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-email", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+ testStates("autocomplete-search", 0, EXT_STATE_SUPPORTS_AUTOCOMPLETION);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // haspopup
+ testStates("autocomplete-list", STATE_HASPOPUP);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559275"
+ title="map attribute required to STATE_REQUIRED">
+ Bug 559275
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=389238"
+ title="Support disabled state on fieldset">
+ Bug 389238
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=599163"
+ title="check disabled state instead of attribute">
+ Bug 599163
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=601205"
+ title="Expose intrinsic invalid state to accessibility API">
+ Bug 601205
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=601205"
+ title="Expose intrinsic invalid state to accessibility API">
+ Bug 601205
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559766"
+ title="Add accessibility support for @list on HTML input and for HTML datalist">
+ Bug 559766
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
+ title="File input control should be propogate states to descendants">
+ Bug 699017
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=733382"
+ title="Editable state bit should be present on readonly inputs">
+ Bug 733382
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=878590"
+ title="HTML5 datalist is not conveyed by haspopup property">
+ Bug 878590
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+
+ <form>
+ <input id="input" type="input">
+ <input id="input_required" type="input" required>
+ <input id="input_readonly" type="input" readonly>
+ <input id="input_disabled" type="input" disabled>
+ <input id="search" type="search">
+ <input id="search_required" type="search" required>
+ <input id="search_readonly" type="search" readonly>
+ <input id="search_disabled" type="search" disabled>
+ <input id="radio" type="radio">
+ <input id="radio_required" type="radio" required>
+ <input id="radio_disabled" type="radio" disabled>
+ <input id="checkbox" type="checkbox">
+ <input id="checkbox_required" type="checkbox" required>
+ <input id="checkbox_disabled" type="checkbox" disabled>
+ <textarea id="textarea"></textarea>
+ <textarea id="textarea_required" required></textarea>
+ <textarea id="textarea_readonly" readonly></textarea>
+ <textarea id="textarea_disabled" disabled></textarea>
+ </form>
+
+ <!-- bogus required usage -->
+ <input id="submit" type="submit" required>
+ <input id="button" type="button" required>
+ <input id="reset" type="reset" required>
+ <input id="image" type="image" required>
+
+ <!-- inherited disabled -->
+ <fieldset id="f" disabled>
+ <input id="f_input">
+ <input id="f_input_disabled" disabled>
+ </fieldset>
+
+ <!-- inherited from input@type="file" -->
+ <input id="file" type="file" required disabled>
+
+ <!-- invalid/valid -->
+ <input id="maxlength" maxlength="1" value="f">
+ <input id="maxlength2" maxlength="100" value="foo">
+ <input id="minlength" minlength="2" value="fo">
+ <input id="minlength2" minlength="1" value="foo">
+ <input id="pattern" pattern="bar" value="foo">
+ <input id="pattern2" pattern="bar" value="bar">
+ <input id="email" type="email" value="foo">
+ <input id="email2" type="email" value="foo@bar.com">
+ <input id="url" type="url" value="foo">
+ <input id="url2" type="url" value="http://mozilla.org/">
+
+ <!-- autocomplete -->
+ <input id="autocomplete-default">
+ <input id="autocomplete-off" autocomplete="off">
+ <form autocomplete="off">
+ <input id="autocomplete-formoff">
+ </form>
+ <datalist id="cities">
+ <option>Paris</option>
+ <option>San Francisco</option>
+ </datalist>
+ <input id="autocomplete-list" list="cities">
+ <input id="autocomplete-list2" list="cities" autocomplete="off">
+ <input id="autocomplete-tel" type="tel">
+
+ Email Address:
+ <input id="autocomplete-email" type="email" list="contacts" value="xyzzy">
+ <datalist id="contacts">
+ <option>xyzzy@plughs.com</option>
+ <option>nobody@mozilla.org</option>
+ </datalist>
+
+ </br>Search for:
+ <input id="autocomplete-search" type="search" list="searchhisty" value="Gamma">
+ <datalist id="searchhisty">
+ <option>Gamma Rays</option>
+ <option>Gamma Ray Bursts</option>
+ </datalist>
+
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_link.html b/accessible/tests/mochitest/states/test_link.html
new file mode 100644
index 000000000..3f89f9f7f
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_link.html
@@ -0,0 +1,144 @@
+<html>
+
+<head>
+ <title>HTML link states testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gLinkWindow = null;
+ function closeDocChecker()
+ {
+ this.__proto__ = new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE);
+
+ this.check = function closeDocChecker_check(aEvent)
+ {
+ gLinkWindow = aEvent.accessible.rootDocument.window;
+ }
+
+ this.match = function closeDocChecker_match(aEvent)
+ {
+ // A temporary about:blank document gets loaded before 'example.com'
+ // document.
+ return aEvent.DOMNode.URL == "http://www.example.com/";
+ }
+ }
+
+ function clickLink(aID)
+ {
+ this.eventSeq = [
+ new stateChangeChecker(STATE_TRAVERSED, false, true, "link_traversed"),
+ new closeDocChecker()
+ ];
+
+ this.invoke = function clickLink_invoke()
+ {
+ synthesizeMouse(getNode("link_traversed"), 1, 1, { shiftKey: true });
+ }
+
+ this.getID = function clickLink_getID()
+ {
+ return "link + '" + aID + "' clicked.";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ // a@href and its text node
+ testStates("link_href", STATE_LINKED);
+ testStates(getAccessible("link_href").firstChild, STATE_LINKED);
+
+ // a@onclick
+ testStates("link_click", STATE_LINKED);
+
+ // a@onmousedown
+ testStates("link_mousedown", STATE_LINKED);
+
+ // a@onmouseup
+ testStates("link_mouseup", STATE_LINKED);
+
+ // a@role="link"
+ testStates("link_arialink", STATE_LINKED);
+
+ // a@role="button"
+ testStates("link_ariabutton", 0, 0, STATE_LINKED);
+
+ // a (no @href, no click event listener)
+ testStates("link_notlink", 0, 0, STATE_LINKED);
+
+ // a: no traversed state
+ testStates("link_traversed", 0, 0, STATE_TRAVERSED);
+
+ // a: traversed state
+ //enableLogging("docload"); // debug stuff
+
+ gQueue = new eventQueue();
+ gQueue.push(new clickLink("link_traversed"));
+ gQueue.onFinish =
+ function()
+ {
+ gLinkWindow.close();
+ //disableLogging(); // debug stuff
+ }
+
+ gQueue.invoke(); // will call SimpleTest.finsih();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=423409"
+ title="Expose click action if mouseup and mousedown are registered">
+ Mozilla Bug 423409
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754830"
+ title="Calculate link states separately">
+ Mozilla Bug 754830
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=757774"
+ title="Fire state change event when link is traversed">
+ Mozilla Bug 757774
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <a id="link_href" href="http://mozilla.org">link</a>
+ <a id="link_click" onclick="">link</a>
+ <a id="link_mousedown" onmousedown="">link</a>
+ <a id="link_mouseup" onmouseup="">link</a>
+ <a id="link_arialink" role="link">aria link</a>
+ <a id="link_ariabutton" role="button">aria button</a>
+ <a id="link_notlink">not link</a>
+
+ <a id="link_traversed" href="http://www.example.com" target="_top">example.com</a>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_popup.xul b/accessible/tests/mochitest/states/test_popup.xul
new file mode 100644
index 000000000..bc40a8b70
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_popup.xul
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL popup attribute test">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // label with popup
+ testStates("labelWithPopup", STATE_HASPOPUP);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=504252"
+ title="Expose STATE_HASPOPUP on XUL elements that have an @popup attribute">
+ Mozilla Bug 504252
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <!-- label with popup attribute -->
+ <label id="labelWithPopup" value="file name"
+ popup="fileContext"
+ tabindex="0"/>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/test_selects.html b/accessible/tests/mochitest/states/test_selects.html
new file mode 100644
index 000000000..0029fcbe4
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_selects.html
@@ -0,0 +1,203 @@
+<html>
+
+<head>
+ <title>HTML selects accessible states tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function openComboboxNCheckStates(aID)
+ {
+ this.combobox = getAccessible(aID);
+ this.comboboxList = this.combobox.firstChild;
+ this.comboboxOption = this.comboboxList.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.comboboxOption)
+ ];
+
+ this.invoke = function openComboboxNCheckStates_invoke()
+ {
+ getNode(aID).focus();
+ synthesizeKey("VK_DOWN", { altKey: true });
+ }
+
+ this.finalCheck = function openComboboxNCheckStates_invoke()
+ {
+ // Expanded state on combobox.
+ testStates(this.combobox, STATE_EXPANDED);
+
+ // Floating state on combobox list.
+ testStates(this.comboboxList, STATE_FLOATING);
+ }
+
+ this.getID = function openComboboxNCheckStates_getID()
+ {
+ return "open combobox and test states";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTest()
+ {
+ // combobox
+ var combobox = getAccessible("combobox");
+ testStates(combobox,
+ STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSABLE, 0,
+ STATE_FOCUSED, 0);
+
+ var comboboxList = combobox.firstChild;
+ testStates(comboboxList, STATE_INVISIBLE, 0, STATE_FOCUSABLE, 0);
+
+ var opt1 = comboboxList.firstChild;
+ testStates(opt1, STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+ EXT_STATE_ACTIVE, STATE_FOCUSED, 0);
+
+ var opt2 = comboboxList.lastChild;
+ testStates(opt2, STATE_SELECTABLE | STATE_FOCUSABLE, 0, STATE_SELECTED, 0,
+ STATE_FOCUSED, EXT_STATE_ACTIVE);
+
+ // collapsed combobox
+ testStates("collapsedcombobox",
+ STATE_COLLAPSED | STATE_FOCUSABLE, 0,
+ STATE_FOCUSED, 0);
+
+ testStates("collapsed-1",
+ STATE_FOCUSABLE | STATE_SELECTABLE, 0,
+ STATE_OFFSCREEN | STATE_INVISIBLE, 0);
+
+ testStates("collapsed-2",
+ STATE_OFFSCREEN, 0,
+ STATE_INVISIBLE, 0);
+
+ // listbox
+ testStates("listbox",
+ STATE_FOCUSABLE, 0,
+ STATE_HASPOPUP | STATE_COLLAPSED | STATE_FOCUSED);
+
+ testStates("listitem-active",
+ STATE_FOCUSABLE | STATE_SELECTABLE, EXT_STATE_ACTIVE,
+ STATE_SELECTED | STATE_FOCUSED);
+
+ testStates("listitem",
+ STATE_FOCUSABLE | STATE_SELECTABLE, 0,
+ STATE_SELECTED | STATE_FOCUSED, EXT_STATE_ACTIVE);
+
+ testStates("listitem-disabled",
+ STATE_UNAVAILABLE, 0,
+ STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+ EXT_STATE_ACTIVE);
+
+ testStates("listgroup",
+ 0, 0,
+ STATE_UNAVAILABLE | STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+ EXT_STATE_ACTIVE);
+
+ testStates("listgroup-disabled",
+ STATE_UNAVAILABLE, 0,
+ STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+ EXT_STATE_ACTIVE);
+
+ todo(false, "no unavailable state on option in disabled group (bug 759666)");
+// testStates("listitem-disabledgroup",
+// STATE_UNAVAILABLE, 0,
+// STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+// EXT_STATE_ACTIVE);
+
+ testStates("listbox-disabled",
+ STATE_UNAVAILABLE, 0,
+ STATE_FOCUSABLE);
+
+ todo(false, "no unavailable state on option in disabled select (bug 759666)");
+// testStates("listitem-disabledlistbox",
+// STATE_UNAVAILABLE, 0,
+// STATE_SELECTABLE | STATE_SELECTED | STATE_FOCUSABLE,
+// EXT_STATE_ACTIVE);
+
+ // open combobox
+ gQueue = new eventQueue();
+ gQueue.push(new openComboboxNCheckStates("combobox"));
+ gQueue.invoke(); // Will call */SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=443889"
+ title="mochitest for selects and lists">
+ Mozilla Bug 443889
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=640716"
+ title="mochitest for selects and lists">
+ Mozilla Bug 640716
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=689847"
+ title="Expose active state on current item of selectable widgets">
+ Mozilla Bug 689847
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=756983"
+ title="Isolate focusable and unavailable states from State()">
+ Mozilla Bug 756983
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=907682"
+ title=" HTML:option group position is not correct when select is collapsed">
+ Mozilla Bug 907682
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="combobox">
+ <option>item 1</option>
+ <option>item 2</option>
+ </select>
+
+ <select id="collapsedcombobox">
+ <option id="collapsed-1">item 1</option>
+ <option id="collapsed-2">item 2</option>
+ </select>
+
+ <select id="listbox" name="component" size="3">
+ <option id="listitem-active">Build</option>
+ <option id="listitem">Disability Access APIs</option>
+ <option id="listitem-disabled" disabled>General</option>
+ <optgroup id="listgroup" label="group">
+ <option>option</option>
+ </optgroup>
+ <optgroup id="listgroup-disabled" disabled label="group2">
+ <option id="listitem-disabledgroup">UI</option>
+ </optgroup>
+ </select>
+
+ <select id="listbox-disabled" size="3" disabled>
+ <option id="listitem-disabledlistbox">option</option>
+ </select>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_stale.html b/accessible/tests/mochitest/states/test_stale.html
new file mode 100644
index 000000000..8f85a1995
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_stale.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Stale state testing</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function addChild(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.childNode = null;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function addChild_invoke()
+ {
+ this.childNode = document.createElement("div");
+ this.containerNode.appendChild(this.childNode);
+ }
+
+ this.finalCheck = function addChild_finalCheck()
+ {
+ // no stale state should be set
+ testStates(this.childNode, 0, 0, 0, EXT_STATE_STALE);
+ }
+
+ this.getID = function addChild_getID()
+ {
+ return "add child for " + prettyName(aContainerID);
+ }
+ }
+
+ function removeChildChecker(aInvoker)
+ {
+ this.type = EVENT_HIDE;
+ this.__defineGetter__("target", function() { return aInvoker.child; });
+
+ this.check = function removeChildChecker_check()
+ {
+ // stale state should be set
+ testStates(aInvoker.child, 0, EXT_STATE_STALE);
+ }
+ }
+
+ function removeChild(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.child = null;
+
+ this.eventSeq = [
+ new removeChildChecker(this)
+ ];
+
+ this.invoke = function removeChild_invoke()
+ {
+ var childNode = this.containerNode.firstChild;
+ this.child = getAccessible(childNode);
+
+ this.containerNode.removeChild(childNode);
+ }
+
+ this.getID = function removeChild_getID()
+ {
+ return "remove child from " + prettyName(aContainerID);
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; //debugging
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new addChild("container"));
+ gQueue.push(new removeChild("container"));
+
+ gQueue.invoke(); // will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body role="">
+
+ <a target="_blank"
+ title="Expose stale state on accessibles unattached from tree"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=676267">Mozilla Bug 676267</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container"></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_tabs.xul b/accessible/tests/mochitest/states/test_tabs.xul
new file mode 100644
index 000000000..a596e178b
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_tabs.xul
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tabbox hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ testStates("tab1", 0, EXT_STATE_PINNED);
+ testStates("tab2", 0, 0, 0, EXT_STATE_PINNED);
+ testStates("tab3", 0, 0, 0, EXT_STATE_PINNED);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=577727"
+ title="Make pinned tabs distinguishable from other tabs for accessibility">
+ Mozilla Bug 577727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tabbox>
+ <tabs id="tabs">
+ <tab id="tab1" label="tab1" pinned="true"/>
+ <tab id="tab2" label="tab2" pinned="false"/>
+ <tab id="tab3" label="tab3"/>
+ </tabs>
+ <tabpanels id="tabpanels">
+ <tabpanel/>
+ <tabpanel/>
+ </tabpanels>
+ </tabbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/test_textbox.xul b/accessible/tests/mochitest/states/test_textbox.xul
new file mode 100644
index 000000000..3daf2abe1
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_textbox.xul
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessible XUL textboxes states tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function getInput(aID)
+ {
+ return getNode(aID).inputField;
+ }
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // Ordinary textbox
+ testStates(getInput("textbox"),
+ STATE_FOCUSABLE,
+ EXT_STATE_EDITABLE,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "ordinary textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Password textbox
+ testStates(getInput("password"),
+ STATE_FOCUSABLE | STATE_PROTECTED,
+ EXT_STATE_EDITABLE,
+ STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "password textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Textarea
+ testStates(getInput("textarea"),
+ STATE_FOCUSABLE,
+ EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "multiline textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Readonly textbox
+ testStates(getInput("readonly_textbox"),
+ STATE_FOCUSABLE | STATE_READONLY,
+ EXT_STATE_EDITABLE,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "readonly textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Disabled textbox
+ testStates(getInput("disabled_textbox"),
+ STATE_UNAVAILABLE,
+ EXT_STATE_EDITABLE,
+ STATE_FOCUSABLE | STATE_PROTECTED,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "readonly textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Readonly textarea
+ testStates(getInput("readonly_textarea"),
+ STATE_FOCUSABLE | STATE_READONLY,
+ EXT_STATE_EDITABLE | EXT_STATE_MULTI_LINE,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "readonly multiline textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Disabled textarea
+ testStates(getInput("disabled_textarea"),
+ STATE_UNAVAILABLE,
+ EXT_STATE_EDITABLE| EXT_STATE_MULTI_LINE,
+ STATE_PROTECTED | STATE_FOCUSABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "readonly multiline textbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Search textbox without search button, searches as you type and filters
+ // a separate control.
+ testStates(getInput("searchbox"),
+ STATE_FOCUSABLE,
+ EXT_STATE_EDITABLE | EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ 0,
+ "searchbox");
+
+ //////////////////////////////////////////////////////////////////////////
+ // Search textbox with search button, does not support autoCompletion.
+ testStates(getInput("searchfield"),
+ STATE_FOCUSABLE,
+ EXT_STATE_EDITABLE,
+ STATE_PROTECTED | STATE_UNAVAILABLE,
+ EXT_STATE_SUPPORTS_AUTOCOMPLETION,
+ "searchfield");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=442648">
+ Mozilla Bug 442648
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=648235"
+ title="XUL textbox can inherit more states from underlying HTML input">
+ Mozilla Bug 648235
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="textbox"/>
+ <textbox id="password" type="password"/>
+ <textbox id="textarea" multiline="true" cols="80" rows="5"/>
+
+ <textbox id="readonly_textbox" readonly="true"/>
+ <textbox id="disabled_textbox" disabled="true"/>
+ <textbox id="readonly_textarea" multiline="true" readonly="true"
+ cols="80" rows="5"/>
+ <textbox id="disabled_textarea" multiline="true" disabled="true"
+ cols="80" rows="5"/>
+
+ <textbox id="searchbox" flex="1" type="search" results="historyTree"/>
+ <textbox id="searchfield" placeholder="Search all add-ons"
+ type="search" searchbutton="true"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/states/test_tree.xul b/accessible/tests/mochitest/states/test_tree.xul
new file mode 100644
index 000000000..878a8d25b
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_tree.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<?xml-stylesheet href="../treeview.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree states tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ /**
+ * Event queue invoker object to test accessible states for XUL tree
+ * accessible.
+ */
+ function statesChecker(aTreeID, aView)
+ {
+ this.DOMNode = getNode(aTreeID);
+
+ this.invoke = function statesChecker_invoke()
+ {
+ this.DOMNode.view = aView;
+ }
+
+ this.check = function statesChecker_check()
+ {
+ var tree = getAccessible(this.DOMNode);
+
+ // tree states
+ testStates(tree, STATE_READONLY);
+
+ if (this.DOMNode.getAttribute("seltype") != "single")
+ testStates(tree, STATE_MULTISELECTABLE);
+ else
+ testStates(tree, 0, 0, STATE_MULTISELECTABLE);
+
+ // tree item states
+ var expandedItem = tree.getChildAt(2);
+ testStates(expandedItem,
+ STATE_SELECTABLE | STATE_FOCUSABLE | STATE_EXPANDED);
+
+ var collapsedItem = tree.getChildAt(5);
+ testStates(collapsedItem,
+ STATE_SELECTABLE | STATE_FOCUSABLE | STATE_COLLAPSED);
+
+ // cells states if any
+ var cells = collapsedItem.children;
+ if (cells && cells.length) {
+ for (var idx = 0; idx < cells.length; idx++) {
+ var cell = cells.queryElementAt(idx, nsIAccessible);
+ testStates(cell, STATE_SELECTABLE);
+ }
+
+ var checkboxCell = cells.queryElementAt(3, nsIAccessible);
+ testStates(checkboxCell, STATE_CHECKABLE | STATE_CHECKED);
+ }
+ }
+
+ this.getID = function statesChecker_getID()
+ {
+ return "tree processor for " + prettyName(aTreeID);
+ }
+ }
+
+ gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue(EVENT_REORDER);
+ gQueue.push(new statesChecker("tree", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("treesingle", new nsTreeTreeView()));
+ gQueue.push(new statesChecker("tabletree", new nsTreeTreeView()));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ if (MAC && (navigator.userAgent.indexOf("Mac OS X 10.6") != -1)) {
+ todo(false,
+ "Re-enable on Mac OS 10.6 after fixing bug 845095 - intermittent orange");
+ } else {
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ }
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treesingle" flex="1" seltype="single">
+ <treecols>
+ <treecol id="col_single" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="tabletree" flex="1" editable="true">
+ <treecols>
+ <treecol id="tabletree_col1" cycler="true" label="cycler"/>
+ <treecol id="tabletree_col2" flex="1" primary="true" label="column1"/>
+ <treecol id="tabletree_col3" flex="1" label="column2"/>
+ <treecol id="tabletree_col4" flex="1" label="checker"
+ type="checkbox" editable="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/states/test_visibility.html b/accessible/tests/mochitest/states/test_visibility.html
new file mode 100644
index 000000000..a2e4a34e6
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_visibility.html
@@ -0,0 +1,175 @@
+<html>
+<head>
+ <title>visibility state testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadURIInvoker(aURI, aFunc)
+ {
+ this.invoke = function loadURIInvoker_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
+ ];
+
+ this.finalCheck = function loadURIInvoker_finalCheck()
+ {
+ aFunc.call();
+ }
+
+ this.getID = function loadURIInvoker_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ function addTabInvoker(aURL, aFunc)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.invoke = function addTabInvoker_invoke()
+ {
+ tabBrowser().loadOneTab(aURL, null, "", null, false);
+ }
+
+ this.finalCheck = function addTabInvoker_finalCheck()
+ {
+ aFunc.call();
+ }
+
+ this.getID = function addTabInvoker_getID()
+ {
+ return "add tab: " + aURL;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ function testBackgroundTab()
+ {
+ // Accessibles in background tab should have offscreen state and no
+ // invisible state.
+ var tabDoc = tabDocumentAt(0);
+ var input = getAccessible(tabDoc.getElementById("input"));
+ testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ }
+
+ function testScrolledOff()
+ {
+ var tabDoc = tabDocumentAt(1);
+
+ // scrolled off
+ input = getAccessible(tabDoc.getElementById("input_scrolledoff"));
+ testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+ // scrolled off item (twice)
+ var lastLiNode = tabDoc.getElementById("li_last");
+ var lastLi = getAccessible(lastLiNode);
+ testStates(lastLi, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+ // scroll into view the item
+ lastLiNode.scrollIntoView(true);
+ testStates(lastLi, 0, 0, STATE_OFFSCREEN | STATE_INVISIBLE);
+
+ // first item is scrolled off now (testcase for bug 768786)
+ var firstLi = getAccessible(tabDoc.getElementById("li_first"));
+ testStates(firstLi, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ }
+
+ var gInputDocURI = "data:text/html,<html><body>";
+ gInputDocURI += "<input id='input'></body></html>";
+
+ var gDocURI = "data:text/html,<html><body>";
+ gDocURI += "<div style='border:2px solid blue; width: 500px; height: 600px;'></div>";
+ gDocURI += "<input id='input_scrolledoff'>";
+ gDocURI += "<ul style='border:2px solid red; width: 100px; height: 50px; overflow: auto;'>";
+ gDocURI += " <li id='li_first'>item1</li><li>item2</li><li>item3</li>";
+ gDocURI += " <li>item4</li><li>item5</li><li id='li_last'>item6</li>";
+ gDocURI += "</ul>";
+ gDocURI += "</body></html>";
+
+ function doTests()
+ {
+ testStates("div", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates("div_off", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ testStates("div_transformed", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ testStates("div_abschild", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new addTabInvoker("about:blank", testBackgroundTab));
+ gQueue.push(new loadURIInvoker(gDocURI, testScrolledOff));
+
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, gInputDocURI, { width: 600, height: 600 });
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=591363"
+ title="(in)visible state is not always correct?">
+ Mozilla Bug 591363
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=768786"
+ title="Offscreen state is not exposed under certain circumstances">
+ Mozilla Bug 768786
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="outer_div">
+
+ <!-- trivial cases -->
+ <div id="div">div</div>
+ <div id="div_off" style="position: absolute; left:-999px; top:-999px">
+ offscreen!
+ </div>
+ <div id="div_transformed" style="transform: translate(-999px, -999px);">
+ transformed!
+ </div>
+
+ <!-- edge case: no rect but has out of flow child -->
+ <div id="div_abschild">
+ <p style="position: absolute; left: 120px; top:120px;">absolute</p>
+ </div>
+
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/states/test_visibility.xul b/accessible/tests/mochitest/states/test_visibility.xul
new file mode 100644
index 000000000..8b2ac990c
--- /dev/null
+++ b/accessible/tests/mochitest/states/test_visibility.xul
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL elements visibility states testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function openMenu(aID, aSubID, aOffscreenSubID)
+ {
+ this.menuNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ testStates(aID, 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates(aSubID, 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ if (aOffscreenSubID)
+ testStates(aOffscreenSubID, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ }
+
+ this.getID = function openMenu_invoke()
+ {
+ return "open menu '" + aID + "' and test states";
+ }
+ }
+
+ function closeMenu(aID, aSubID, aSub2ID)
+ {
+ this.menuNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, document)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ this.menuNode.open = false;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ testStates(aID, 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates(aSubID, STATE_INVISIBLE, 0, STATE_OFFSCREEN);
+ testStates(aSub2ID, STATE_INVISIBLE, 0, STATE_OFFSCREEN);
+ }
+
+ this.getID = function openMenu_invoke()
+ {
+ return "open menu and test states";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ testStates("deck_pane2", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates("tabs_pane1", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
+ testStates("tabs_pane2", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+ gQueue = new eventQueue();
+ gQueue.push(new openMenu("mi_file1", "mi_file1.1"));
+ gQueue.push(new openMenu("mi_file1.2", "mi_file1.2.1", "mi_file1.2.4"));
+ gQueue.push(new closeMenu("mi_file1", "mi_file1.1", "mi_file1.2.1"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=810260"
+ title="xul:deck hidden pages shouldn't be offscreen">
+ Mozilla Bug 810260
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=865591"
+ title="Visible menu item have offscreen state">
+ Mozilla Bug 865591
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <deck selectedIndex="1">
+ <description value="This is the first page" id="deck_pane1"/>
+ <button label="This is the second page" id="deck_pane2"/>
+ </deck>
+
+ <tabbox>
+ <tabs>
+ <tab>tab1</tab>
+ <tab>tab2</tab>
+ </tabs>
+ <tabpanels>
+ <description value="This is the first page" id="tabs_pane1"/>
+ <button label="This is the second page" id="tabs_pane2"/>
+ </tabpanels>
+ </tabbox>
+
+ <menubar>
+ <menu label="File" id="mi_file1">
+ <menupopup>
+ <menuitem label="SubFile" id="mi_file1.1"/>
+ <menu label="SubFile2" id="mi_file1.2">
+ <menupopup style="max-height: 5em;">
+ <menuitem label="SubSubFile" id="mi_file1.2.1"/>
+ <menuitem label="SubSubFile2" id="mi_file1.2.2"/>
+ <menuitem label="SubSubFile3" id="mi_file1.2.3"/>
+ <menuitem label="SubSubFile4" id="mi_file1.2.4"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menubar>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/states/z_frames.html b/accessible/tests/mochitest/states/z_frames.html
new file mode 100644
index 000000000..819adee63
--- /dev/null
+++ b/accessible/tests/mochitest/states/z_frames.html
@@ -0,0 +1,11 @@
+<html>
+<!--
+Auxilliary file used as frame source.
+-->
+<head>
+</head>
+<body>
+<p>Frame source body has no role</p>
+</body>
+</html>
+
diff --git a/accessible/tests/mochitest/states/z_frames_article.html b/accessible/tests/mochitest/states/z_frames_article.html
new file mode 100644
index 000000000..a7a69b4da
--- /dev/null
+++ b/accessible/tests/mochitest/states/z_frames_article.html
@@ -0,0 +1,11 @@
+<html>
+<!--
+Auxilliary file used as frame source.
+-->
+<head>
+</head>
+<body role="article">
+<p>Article</p>
+</body>
+</html>
+
diff --git a/accessible/tests/mochitest/states/z_frames_checkbox.html b/accessible/tests/mochitest/states/z_frames_checkbox.html
new file mode 100644
index 000000000..799764424
--- /dev/null
+++ b/accessible/tests/mochitest/states/z_frames_checkbox.html
@@ -0,0 +1,11 @@
+<html>
+<!--
+Auxilliary file used as frame source.
+-->
+<head>
+</head>
+<body role="checkbox">
+<p>Checkbox</p>
+</body>
+</html>
+
diff --git a/accessible/tests/mochitest/states/z_frames_textbox.html b/accessible/tests/mochitest/states/z_frames_textbox.html
new file mode 100644
index 000000000..0f4e1b9d6
--- /dev/null
+++ b/accessible/tests/mochitest/states/z_frames_textbox.html
@@ -0,0 +1,11 @@
+<html>
+<!--
+Auxilliary file used as frame source.
+-->
+<head>
+</head>
+<body role="textbox">
+<p>Texbox</p>
+</body>
+</html>
+
diff --git a/accessible/tests/mochitest/states/z_frames_update.html b/accessible/tests/mochitest/states/z_frames_update.html
new file mode 100644
index 000000000..90d756afe
--- /dev/null
+++ b/accessible/tests/mochitest/states/z_frames_update.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+<script>
+function replaceBody()
+{
+ var accService = Components.classes["@mozilla.org/accessibilityService;1"].
+ getService(Components.interfaces.nsIAccessibilityService);
+ accService.getAccessibleFor(document);
+
+ var newBody = document.createElement("body");
+ newBody.setAttribute("contentEditable", "true");
+ newBody.textContent = "New Hello";
+ document.documentElement.replaceChild(newBody, document.body);
+ getComputedStyle(newBody, "").color;
+}
+</script>
+</head>
+<body onload="replaceBody();">
+OLD hello
+</body>
+</html>
+
diff --git a/accessible/tests/mochitest/table.js b/accessible/tests/mochitest/table.js
new file mode 100644
index 000000000..e171594d6
--- /dev/null
+++ b/accessible/tests/mochitest/table.js
@@ -0,0 +1,778 @@
+/**
+ * This file provides set of helper functions to test nsIAccessibleTable
+ * interface.
+ *
+ * Required:
+ * common.js
+ * role.js
+ * states.js
+ */
+
+/**
+ * Constants used to describe cells array.
+ */
+const kDataCell = 1; // Indicates the cell is origin data cell
+const kRowHeaderCell = 2; // Indicates the cell is row header cell
+const kColHeaderCell = 4; // Indicated the cell is column header cell
+const kOrigin = kDataCell | kRowHeaderCell | kColHeaderCell;
+
+const kRowSpanned = 8; // Indicates the cell is not origin and row spanned
+const kColSpanned = 16; // Indicates the cell is not origin and column spanned
+const kSpanned = kRowSpanned | kColSpanned;
+
+/**
+ * Constants to define column header type.
+ */
+const kNoColumnHeader = 0;
+const kListboxColumnHeader = 1;
+const kTreeColumnHeader = 2;
+
+/**
+ * Constants to define table type.
+ */
+const kTable = 0;
+const kTreeTable = 1;
+const kMathTable = 2;
+
+/**
+ * Test table structure and related methods.
+ *
+ * @param aIdentifier [in] table accessible identifier
+ * @param aCellsArray [in] two dimensional array (row X columns) of
+ * cell types (see constants defined above).
+ * @param aColHeaderType [in] specifies wether column header cells are
+ * arranged into the list.
+ * @param aCaption [in] caption text if any
+ * @param aSummary [in] summary text if any
+ * @param aTableType [in] specifies the table type.
+ * @param aRowRoles [in] array of row roles.
+ */
+function testTableStruct(aIdentifier, aCellsArray, aColHeaderType,
+ aCaption, aSummary, aTableType, aRowRoles)
+{
+ var tableNode = getNode(aIdentifier);
+ var isGrid = tableNode.getAttribute("role") == "grid" ||
+ tableNode.getAttribute("role") == "treegrid" ||
+ tableNode.localName == "tree";
+
+ var rowCount = aCellsArray.length;
+ var colsCount = aCellsArray[0] ? aCellsArray[0].length : 0;
+
+ // Test table accessible tree.
+ var tableObj = {
+ children: []
+ };
+ switch (aTableType) {
+ case kTable:
+ tableObj.role = ROLE_TABLE;
+ break;
+ case kTreeTable:
+ tableObj.role = ROLE_TREE_TABLE;
+ break;
+ case kMathTable:
+ tableObj.role = ROLE_MATHML_TABLE;
+ break;
+ }
+
+ // caption accessible handling
+ if (aCaption) {
+ var captionObj = {
+ role: ROLE_CAPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: aCaption
+ }
+ ]
+ };
+
+ tableObj.children.push(captionObj);
+ }
+
+ // special types of column headers handling
+ if (aColHeaderType) {
+ var headersObj = {
+ role: ROLE_LIST,
+ children: []
+ };
+
+ for (var idx = 0; idx < colsCount; idx++) {
+ var headerCellObj = {
+ role: ROLE_COLUMNHEADER
+ };
+ headersObj.children.push(headerCellObj);
+ }
+
+ if (aColHeaderType == kTreeColumnHeader) {
+ var columnPickerObj = {
+ role: ROLE_PUSHBUTTON
+ };
+
+ headersObj.children.push(columnPickerObj);
+ }
+
+ tableObj.children.push(headersObj);
+ }
+
+ // rows and cells accessibles
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ var rowObj = {
+ role: aRowRoles ? aRowRoles[rowIdx] : ROLE_ROW,
+ children: []
+ };
+
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var celltype = aCellsArray[rowIdx][colIdx];
+
+ var role = ROLE_NOTHING;
+ switch (celltype) {
+ case kDataCell:
+ role = (aTableType == kMathTable ? ROLE_MATHML_CELL :
+ (isGrid ? ROLE_GRID_CELL : ROLE_CELL));
+ break;
+ case kRowHeaderCell:
+ role = ROLE_ROWHEADER;
+ break;
+ case kColHeaderCell:
+ role = ROLE_COLUMNHEADER;
+ break;
+ }
+
+ if (role != ROLE_NOTHING) {
+ var cellObj = {
+ role: role
+ };
+ rowObj.children.push(cellObj);
+ }
+ }
+
+ tableObj.children.push(rowObj);
+ }
+
+ testAccessibleTree(aIdentifier, tableObj);
+
+ // Test table table interface.
+ var table = getAccessible(aIdentifier, [nsIAccessibleTable]);
+
+ // summary
+ if (aSummary)
+ is(table.summary, aSummary,
+ "Wrong summary of the table " + prettyName(aIdentifier));
+
+ // rowCount and columnCount
+ is(table.rowCount, rowCount,
+ "Wrong rows count of " + prettyName(aIdentifier));
+ is(table.columnCount, colsCount,
+ "Wrong columns count of " + prettyName(aIdentifier));
+
+ // rows and columns extents
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var celltype = aCellsArray[rowIdx][colIdx];
+ if (celltype & kOrigin) {
+
+ // table getRowExtentAt
+ var rowExtent = table.getRowExtentAt(rowIdx, colIdx);
+ for (var idx = rowIdx + 1;
+ idx < rowCount && (aCellsArray[idx][colIdx] & kRowSpanned);
+ idx++);
+
+ var expectedRowExtent = idx - rowIdx;
+ is(rowExtent, expectedRowExtent,
+ "getRowExtentAt: Wrong number of spanned rows at (" + rowIdx + ", " +
+ colIdx + ") for " + prettyName(aIdentifier));
+
+ // table getColumnExtentAt
+ var colExtent = table.getColumnExtentAt(rowIdx, colIdx);
+ for (var idx = colIdx + 1;
+ idx < colsCount && (aCellsArray[rowIdx][idx] & kColSpanned);
+ idx++);
+
+ var expectedColExtent = idx - colIdx;
+ is(colExtent, expectedColExtent,
+ "getColumnExtentAt: Wrong number of spanned columns at (" + rowIdx +
+ ", " + colIdx + ") for " + prettyName(aIdentifier));
+
+ // cell rowExtent and columnExtent
+ var cell = getAccessible(table.getCellAt(rowIdx, colIdx),
+ [nsIAccessibleTableCell]);
+
+ is(cell.rowExtent, expectedRowExtent,
+ "rowExtent: Wrong number of spanned rows at (" + rowIdx + ", " +
+ colIdx + ") for " + prettyName(aIdentifier));
+
+ is(cell.columnExtent, expectedColExtent,
+ "columnExtent: Wrong number of spanned column at (" + rowIdx + ", " +
+ colIdx + ") for " + prettyName(aIdentifier));
+ }
+ }
+ }
+}
+
+/**
+ * Test table indexes.
+ *
+ * @param aIdentifier [in] table accessible identifier
+ * @param aIdxes [in] two dimensional array of cell indexes
+ */
+function testTableIndexes(aIdentifier, aIdxes)
+{
+ var tableAcc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!tableAcc)
+ return;
+
+ var obtainedRowIdx, obtainedColIdx, obtainedIdx;
+ var cellAcc;
+
+ var id = prettyName(aIdentifier);
+
+ var rowCount = aIdxes.length;
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ var colCount = aIdxes[rowIdx].length;
+ for (var colIdx = 0; colIdx < colCount; colIdx++) {
+ var idx = aIdxes[rowIdx][colIdx];
+
+ // getCellAt
+ try {
+ cellAcc = null;
+ cellAcc = tableAcc.getCellAt(rowIdx, colIdx);
+ } catch (e) { }
+
+ ok(idx != -1 && cellAcc || idx == -1 && !cellAcc,
+ id + ": Can't get cell accessible at row = " + rowIdx + ", column = " + colIdx);
+
+ if (idx != - 1) {
+
+ // getRowIndexAt
+ var origRowIdx = rowIdx;
+ while (origRowIdx > 0 &&
+ aIdxes[rowIdx][colIdx] == aIdxes[origRowIdx - 1][colIdx])
+ origRowIdx--;
+
+ try {
+ obtainedRowIdx = tableAcc.getRowIndexAt(idx);
+ } catch (e) {
+ ok(false, id + ": can't get row index for cell index " + idx + "," + e);
+ }
+
+ is(obtainedRowIdx, origRowIdx,
+ id + ": row for index " + idx + " is not correct (getRowIndexAt)");
+
+ // getColumnIndexAt
+ var origColIdx = colIdx;
+ while (origColIdx > 0 &&
+ aIdxes[rowIdx][colIdx] == aIdxes[rowIdx][origColIdx - 1])
+ origColIdx--;
+
+ try {
+ obtainedColIdx = tableAcc.getColumnIndexAt(idx);
+ } catch (e) {
+ ok(false, id + ": can't get column index for cell index " + idx + "," + e);
+ }
+
+ is(obtainedColIdx, origColIdx,
+ id + ": column for index " + idx + " is not correct (getColumnIndexAt)");
+
+ // getRowAndColumnIndicesAt
+ var obtainedRowIdxObj = { }, obtainedColIdxObj = { };
+ try {
+ tableAcc.getRowAndColumnIndicesAt(idx, obtainedRowIdxObj,
+ obtainedColIdxObj);
+ } catch (e) {
+ ok(false, id + ": can't get row and column indices for cell index " + idx + "," + e);
+ }
+
+ is(obtainedRowIdxObj.value, origRowIdx,
+ id + ": row for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
+ is(obtainedColIdxObj.value, origColIdx,
+ id + ": column for index " + idx + " is not correct (getRowAndColumnIndicesAt)");
+
+ if (cellAcc) {
+
+ var cellId = prettyName(cellAcc);
+ cellAcc = getAccessible(cellAcc, [nsIAccessibleTableCell]);
+
+ // cell: 'table-cell-index' attribute
+ var attrs = cellAcc.attributes;
+ var strIdx = "";
+ try {
+ strIdx = attrs.getStringProperty("table-cell-index");
+ } catch (e) {
+ ok(false,
+ cellId + ": no cell index from object attributes on the cell accessible at index " + idx + ".");
+ }
+
+ if (strIdx) {
+ is (parseInt(strIdx), idx,
+ cellId + ": cell index from object attributes of cell accessible isn't corrent.");
+ }
+
+ // cell: table
+ try {
+ is(cellAcc.table, tableAcc,
+ cellId + ": wrong table accessible for the cell.");
+
+ } catch (e) {
+ ok(false,
+ cellId + ": can't get table accessible from the cell.");
+ }
+
+ // cell: getRowIndex
+ try {
+ obtainedRowIdx = cellAcc.rowIndex;
+ } catch (e) {
+ ok(false,
+ cellId + ": can't get row index of the cell at index " + idx + "," + e);
+ }
+
+ is(obtainedRowIdx, origRowIdx,
+ cellId + ": row for the cell at index " + idx +" is not correct");
+
+ // cell: getColumnIndex
+ try {
+ obtainedColIdx = cellAcc.columnIndex;
+ } catch (e) {
+ ok(false,
+ cellId + ": can't get column index of the cell at index " + idx + "," + e);
+ }
+
+ is(obtainedColIdx, origColIdx,
+ id + ": column for the cell at index " + idx +" is not correct");
+ }
+ }
+
+ // getCellIndexAt
+ try {
+ obtainedIdx = tableAcc.getCellIndexAt(rowIdx, colIdx);
+ } catch (e) {
+ obtainedIdx = -1;
+ }
+
+ is(obtainedIdx, idx,
+ id + ": row " + rowIdx + " /column " + colIdx + " and index " + obtainedIdx + " aren't inconsistent.");
+ }
+ }
+}
+
+/**
+ * Test table getters selection methods.
+ *
+ * @param aIdentifier [in] table accessible identifier
+ * @param aCellsArray [in] two dimensional array (row X columns) of cells
+ * states (either boolean (selected/unselected) if cell is
+ * origin, otherwise kRowSpanned or kColSpanned constant).
+ * @param aMsg [in] text appended before every message
+ */
+function testTableSelection(aIdentifier, aCellsArray, aMsg)
+{
+ var msg = aMsg ? aMsg : "";
+ var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!acc)
+ return;
+
+ var rowCount = aCellsArray.length;
+ var colsCount = aCellsArray[0].length;
+
+ // Columns selection tests.
+ var selCols = new Array();
+
+ // isColumnSelected test
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var isColSelected = true;
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ if (aCellsArray[rowIdx][colIdx] == false ||
+ aCellsArray[rowIdx][colIdx] == undefined) {
+ isColSelected = false;
+ break;
+ }
+ }
+
+ is(acc.isColumnSelected(colIdx), isColSelected,
+ msg + "Wrong selection state of " + colIdx + " column for " +
+ prettyName(aIdentifier));
+
+ if (isColSelected)
+ selCols.push(colIdx);
+ }
+
+ // selectedColsCount test
+ is(acc.selectedColumnCount, selCols.length,
+ msg + "Wrong count of selected columns for " + prettyName(aIdentifier));
+
+ // getSelectedColumns test
+ var actualSelColsCountObj = { value: null };
+ var actualSelCols = acc.getSelectedColumnIndices(actualSelColsCountObj);
+
+ var actualSelColsCount = actualSelColsCountObj.value;
+ is (actualSelColsCount, selCols.length,
+ msg + "Wrong count of selected columns for " + prettyName(aIdentifier) +
+ "from getSelectedColumns.");
+
+ for (var i = 0; i < actualSelColsCount; i++) {
+ is (actualSelCols[i], selCols[i],
+ msg + "Column at index " + selCols[i] + " should be selected.");
+ }
+
+ // Rows selection tests.
+ var selRows = new Array();
+
+ // isRowSelected test
+ var selrowCount = 0;
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ var isRowSelected = true;
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ if (aCellsArray[rowIdx][colIdx] == false ||
+ aCellsArray[rowIdx][colIdx] == undefined) {
+ isRowSelected = false;
+ break;
+ }
+ }
+
+ is(acc.isRowSelected(rowIdx), isRowSelected,
+ msg + "Wrong selection state of " + rowIdx + " row for " +
+ prettyName(aIdentifier));
+
+ if (isRowSelected)
+ selRows.push(rowIdx);
+ }
+
+ // selectedRowCount test
+ is(acc.selectedRowCount, selRows.length,
+ msg + "Wrong count of selected rows for " + prettyName(aIdentifier));
+
+ // getSelectedRows test
+ var actualSelrowCountObj = { value: null };
+ var actualSelRows = acc.getSelectedRowIndices(actualSelrowCountObj);
+
+ var actualSelrowCount = actualSelrowCountObj.value;
+ is (actualSelrowCount, selRows.length,
+ msg + "Wrong count of selected rows for " + prettyName(aIdentifier) +
+ "from getSelectedRows.");
+
+ for (var i = 0; i < actualSelrowCount; i++) {
+ is (actualSelRows[i], selRows[i],
+ msg + "Row at index " + selRows[i] + " should be selected.");
+ }
+
+ // Cells selection tests.
+ var selCells = new Array();
+
+ // isCellSelected test
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ if (aCellsArray[rowIdx][colIdx] & kSpanned)
+ continue;
+
+ var isSelected = aCellsArray[rowIdx][colIdx] == true;
+ is(acc.isCellSelected(rowIdx, colIdx), isSelected,
+ msg + "Wrong selection state of cell at " + rowIdx + " row and " +
+ colIdx + " column for " + prettyName(aIdentifier));
+
+ if (aCellsArray[rowIdx][colIdx])
+ selCells.push(acc.getCellIndexAt(rowIdx, colIdx));
+ }
+ }
+
+ // selectedCellCount tests
+ is(acc.selectedCellCount, selCells.length,
+ msg + "Wrong count of selected cells for " + prettyName(aIdentifier));
+
+ // getSelectedCellIndices test
+ var actualSelCellsCountObj = { value: null };
+ var actualSelCells = acc.getSelectedCellIndices(actualSelCellsCountObj);
+
+ var actualSelCellsCount = actualSelCellsCountObj.value;
+ is(actualSelCellsCount, selCells.length,
+ msg + "Wrong count of selected cells for " + prettyName(aIdentifier) +
+ "from getSelectedCells.");
+
+ for (var i = 0; i < actualSelCellsCount; i++) {
+ is(actualSelCells[i], selCells[i],
+ msg + "getSelectedCellIndices: Cell at index " + selCells[i] +
+ " should be selected.");
+ }
+
+ // selectedCells and isSelected tests
+ var actualSelCellsArray = acc.selectedCells;
+ for (var i = 0; i < actualSelCellsCount; i++) {
+ var actualSelCellAccessible =
+ actualSelCellsArray.queryElementAt(i, nsIAccessibleTableCell);
+
+ var colIdx = acc.getColumnIndexAt(selCells[i]);
+ var rowIdx = acc.getRowIndexAt(selCells[i]);
+ var expectedSelCellAccessible = acc.getCellAt(rowIdx, colIdx);
+
+ ok(actualSelCellAccessible, expectedSelCellAccessible,
+ msg + "getSelectedCells: Cell at index " + selCells[i] +
+ " should be selected.");
+
+ ok(actualSelCellAccessible.isSelected(),
+ "isSelected: Cell at index " + selCells[i] + " should be selected.");
+ }
+
+ // selected states tests
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ if (aCellsArray[rowIdx][colIdx] & kSpanned)
+ continue;
+
+ var cell = acc.getCellAt(rowIdx, colIdx);
+ var isSel = aCellsArray[rowIdx][colIdx];
+ if (isSel == undefined)
+ testStates(cell, 0, 0, STATE_SELECTABLE | STATE_SELECTED);
+ else if (isSel == true)
+ testStates(cell, STATE_SELECTED);
+ else
+ testStates(cell, STATE_SELECTABLE, 0, STATE_SELECTED);
+ }
+ }
+}
+
+/**
+ * Test unselectColumn method of accessible table.
+ */
+function testUnselectTableColumn(aIdentifier, aColIdx, aCellsArray)
+{
+ var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!acc)
+ return;
+
+ var rowCount = aCellsArray.length;
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ var cellState = aCellsArray[rowIdx][aColIdx];
+ // Unselect origin cell.
+ var [origRowIdx, origColIdx] =
+ getOrigRowAndColumn(aCellsArray, rowIdx, aColIdx);
+ aCellsArray[origRowIdx][origColIdx] = false;
+ }
+
+ acc.unselectColumn(aColIdx);
+ testTableSelection(aIdentifier, aCellsArray,
+ "Unselect " + aColIdx + " column: ");
+}
+
+/**
+ * Test selectColumn method of accessible table.
+ */
+function testSelectTableColumn(aIdentifier, aColIdx, aCellsArray)
+{
+ var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!acc)
+ return;
+
+ var rowCount = aCellsArray.length;
+ var colsCount = aCellsArray[0].length;
+
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var cellState = aCellsArray[rowIdx][colIdx];
+
+ if (colIdx == aColIdx) { // select target column
+ if (!(cellState & kSpanned)) {
+ // Select the cell if it is origin.
+ aCellsArray[rowIdx][colIdx] = true;
+
+ } else {
+ // If the cell is spanned then search origin cell and select it.
+ var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
+ rowIdx, colIdx);
+ aCellsArray[origRowIdx][origColIdx] = true;
+ }
+
+ } else if (!(cellState & kSpanned)) { // unselect other columns
+ if (colIdx > aColIdx) {
+ // Unselect the cell if traversed column index is greater than column
+ // index of target cell.
+ aCellsArray[rowIdx][colIdx] = false;
+
+ } else if (!(aCellsArray[rowIdx][aColIdx] & kColSpanned)) {
+ // Unselect the cell if the target cell is not row spanned.
+ aCellsArray[rowIdx][colIdx] = false;
+
+ } else {
+ // Unselect the cell if it is not spanned to the target cell.
+ for (var spannedColIdx = colIdx + 1; spannedColIdx < aColIdx;
+ spannedColIdx++) {
+ var spannedCellState = aCellsArray[rowIdx][spannedColIdx];
+ if (!(spannedCellState & kRowSpanned)) {
+ aCellsArray[rowIdx][colIdx] = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ acc.selectColumn(aColIdx);
+ testTableSelection(aIdentifier, aCellsArray,
+ "Select " + aColIdx + " column: ");
+}
+
+/**
+ * Test unselectRow method of accessible table.
+ */
+function testUnselectTableRow(aIdentifier, aRowIdx, aCellsArray)
+{
+ var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!acc)
+ return;
+
+ var colsCount = aCellsArray[0].length;
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ // Unselect origin cell.
+ var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
+ aRowIdx, colIdx);
+ aCellsArray[origRowIdx][origColIdx] = false;
+ }
+
+ acc.unselectRow(aRowIdx);
+ testTableSelection(aIdentifier, aCellsArray,
+ "Unselect " + aRowIdx + " row: ");
+}
+
+/**
+ * Test selectRow method of accessible table.
+ */
+function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray)
+{
+ var acc = getAccessible(aIdentifier, [nsIAccessibleTable]);
+ if (!acc)
+ return;
+
+ var rowCount = aCellsArray.length;
+ var colsCount = aCellsArray[0].length;
+
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ for (var colIdx = 0; colIdx < colsCount; colIdx++) {
+ var cellState = aCellsArray[rowIdx][colIdx];
+
+ if (rowIdx == aRowIdx) { // select the given row
+ if (!(cellState & kSpanned)) {
+ // Select the cell if it is origin.
+ aCellsArray[rowIdx][colIdx] = true;
+
+ } else {
+ // If the cell is spanned then search origin cell and select it.
+ var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
+ rowIdx, colIdx);
+
+ aCellsArray[origRowIdx][origColIdx] = true;
+ }
+
+ } else if (!(cellState & kSpanned)) { // unselect other rows
+ if (rowIdx > aRowIdx) {
+ // Unselect the cell if traversed row index is greater than row
+ // index of target cell.
+ aCellsArray[rowIdx][colIdx] = false;
+
+ } else if (!(aCellsArray[aRowIdx][colIdx] & kRowSpanned)) {
+ // Unselect the cell if the target cell is not row spanned.
+ aCellsArray[rowIdx][colIdx] = false;
+
+ } else {
+ // Unselect the cell if it is not spanned to the target cell.
+ for (var spannedRowIdx = rowIdx + 1; spannedRowIdx < aRowIdx;
+ spannedRowIdx++) {
+ var spannedCellState = aCellsArray[spannedRowIdx][colIdx];
+ if (!(spannedCellState & kRowSpanned)) {
+ aCellsArray[rowIdx][colIdx] = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ acc.selectRow(aRowIdx);
+ testTableSelection(aIdentifier, aCellsArray,
+ "Select " + aRowIdx + " row: ");
+}
+
+/**
+ * Test columnHeaderCells and rowHeaderCells of accessible table.
+ */
+function testHeaderCells(aHeaderInfoMap)
+{
+ for (var testIdx = 0; testIdx < aHeaderInfoMap.length; testIdx++) {
+ var dataCellIdentifier = aHeaderInfoMap[testIdx].cell;
+ var dataCell = getAccessible(dataCellIdentifier, [nsIAccessibleTableCell]);
+
+ // row header cells
+ var rowHeaderCells = aHeaderInfoMap[testIdx].rowHeaderCells;
+ var rowHeaderCellsCount = rowHeaderCells.length;
+ var actualRowHeaderCells = dataCell.rowHeaderCells;
+ var actualRowHeaderCellsCount = actualRowHeaderCells.length;
+
+ is(actualRowHeaderCellsCount, rowHeaderCellsCount,
+ "Wrong number of row header cells for the cell " +
+ prettyName(dataCellIdentifier));
+
+ if (actualRowHeaderCellsCount == rowHeaderCellsCount) {
+ for (var idx = 0; idx < rowHeaderCellsCount; idx++) {
+ var rowHeaderCell = getAccessible(rowHeaderCells[idx]);
+ var actualRowHeaderCell =
+ actualRowHeaderCells.queryElementAt(idx, nsIAccessible);
+ isObject(actualRowHeaderCell, rowHeaderCell,
+ "Wrong row header cell at index " + idx + " for the cell " +
+ dataCellIdentifier);
+ }
+ }
+
+ // column header cells
+ var colHeaderCells = aHeaderInfoMap[testIdx].columnHeaderCells;
+ var colHeaderCellsCount = colHeaderCells.length;
+ var actualColHeaderCells = dataCell.columnHeaderCells;
+ var actualColHeaderCellsCount = actualColHeaderCells.length;
+
+ is(actualColHeaderCellsCount, colHeaderCellsCount,
+ "Wrong number of column header cells for the cell " +
+ prettyName(dataCellIdentifier));
+
+ if (actualColHeaderCellsCount == colHeaderCellsCount) {
+ for (var idx = 0; idx < colHeaderCellsCount; idx++) {
+ var colHeaderCell = getAccessible(colHeaderCells[idx]);
+ var actualColHeaderCell =
+ actualColHeaderCells.queryElementAt(idx, nsIAccessible);
+ isObject(actualColHeaderCell, colHeaderCell,
+ "Wrong column header cell at index " + idx + " for the cell " +
+ dataCellIdentifier);
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private implementation
+
+/**
+ * Return row and column of orig cell for the given spanned cell.
+ */
+function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx)
+{
+ var cellState = aCellsArray[aRowIdx][aColIdx];
+
+ var origRowIdx = aRowIdx, origColIdx = aColIdx;
+ if (cellState & kRowSpanned) {
+ for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) {
+ var prevCellState = aCellsArray[prevRowIdx][aColIdx];
+ if (!(prevCellState & kRowSpanned)) {
+ origRowIdx = prevRowIdx;
+ break;
+ }
+ }
+ }
+
+ if (cellState & kColSpanned) {
+ for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) {
+ var prevCellState = aCellsArray[aRowIdx][prevColIdx];
+ if (!(prevCellState & kColSpanned)) {
+ origColIdx = prevColIdx;
+ break;
+ }
+ }
+ }
+
+ return [origRowIdx, origColIdx];
+}
diff --git a/accessible/tests/mochitest/table/a11y.ini b/accessible/tests/mochitest/table/a11y.ini
new file mode 100644
index 000000000..ba8e6ba98
--- /dev/null
+++ b/accessible/tests/mochitest/table/a11y.ini
@@ -0,0 +1,27 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_css_tables.html]
+[test_headers_ariagrid.html]
+[test_headers_ariatable.html]
+[test_headers_listbox.xul]
+[test_headers_table.html]
+[test_headers_tree.xul]
+[test_indexes_ariagrid.html]
+[test_indexes_listbox.xul]
+[test_indexes_table.html]
+[test_indexes_tree.xul]
+[test_layoutguess.html]
+[test_mtable.html]
+[test_sels_ariagrid.html]
+[test_sels_listbox.xul]
+[test_sels_table.html]
+[test_sels_tree.xul]
+[test_struct_ariagrid.html]
+[test_struct_ariatreegrid.html]
+[test_struct_listbox.xul]
+[test_struct_table.html]
+[test_struct_tree.xul]
+[test_table_1.html]
+[test_table_2.html]
diff --git a/accessible/tests/mochitest/table/test_css_tables.html b/accessible/tests/mochitest/table/test_css_tables.html
new file mode 100644
index 000000000..f93b0770a
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_css_tables.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <title>CSS display:table is not a table</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // elements with display:table
+
+ // only display:table
+ var accTree =
+ { SECTION: [
+ { TEXT_LEAF: [ ] }
+ ] };
+ testAccessibleTree("table1", accTree);
+
+ // only display:table and display:table-cell
+ accTree =
+ { SECTION: [
+ { SECTION: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("table2", accTree);
+
+ // display:table, display:table-row, and display:table-cell
+ accTree =
+ { SECTION: [
+ { SECTION: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("table3", accTree);
+
+ // display:table, display:table-row-group, display:table-row, and display:table-cell
+ accTree =
+ { SECTION: [
+ { SECTION: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("table4", accTree);
+
+ // display:inline-table
+ accTree =
+ { TEXT_CONTAINER: [
+ { TEXT_CONTAINER: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("table5", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title=" div with display:table exposes table semantics"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1007975">Mozilla Bug 1007975</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="table1" style="display:table">
+ table1
+ </div>
+
+ <div id="table2" style="display:table">
+ <div style="display:table-cell">table2</div>
+ </div>
+
+ <div id="table3" style="display:table">
+ <div style="display:table-row">
+ <div style="display:table-cell">table3</div>
+ </div>
+ </div>
+
+ <div id="table4" style="display:table">
+ <div style="display:table-row-group">
+ <div style="display:table-row">
+ <div style="display:table-cell">table4</div>
+ </div>
+ </div>
+ </div>
+
+ <div>
+ <span id="table5" style="display:inline-table">
+ <span style="display:table-row">
+ <span style="display:table-cell">table5</div>
+ </span>
+ </span>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_headers_ariagrid.html b/accessible/tests/mochitest/table/test_headers_ariagrid.html
new file mode 100644
index 000000000..88abb248a
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_ariagrid.html
@@ -0,0 +1,185 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <title>Table header information cells for ARIA grid</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // column and row headers from markup
+
+ headerInfoMap = [
+ {
+ cell: "table_dc_1",
+ rowHeaderCells: [ "table_rh_1" ],
+ columnHeaderCells: [ "table_ch_2" ]
+ },
+ {
+ cell: "table_dc_2",
+ rowHeaderCells: [ "table_rh_1" ],
+ columnHeaderCells: [ "table_ch_3" ]
+ },
+ {
+ cell: "table_dc_3",
+ rowHeaderCells: [ "table_rh_2" ],
+ columnHeaderCells: [ "table_ch_2" ]
+ },
+ {
+ cell: "table_dc_4",
+ rowHeaderCells: [ "table_rh_2" ],
+ columnHeaderCells: [ "table_ch_3" ]
+ },
+ {
+ cell: "table_rh_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table_ch_1" ]
+ },
+ {
+ cell: "table_rh_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table_ch_1" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // column and row headers from markup for crazy grid.
+
+ headerInfoMap = [
+ {
+ // not focusable cell (ARIAGridCellAccessible is used)
+ cell: "table2_dc_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table2_ch_1" ]
+ },
+ {
+ // focusable cell (ARIAGridCellAccessible is used)
+ cell: "table2_dc_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table2_ch_2" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // column and row headers from markup for one more crazy grid.
+
+ headerInfoMap = [
+ {
+ // ARIAGridCellAccessible is used
+ cell: "t3_dc_1",
+ rowHeaderCells: [ "t3_rh_1" ],
+ columnHeaderCells: [ ]
+ },
+ {
+ // ARIAGridCellAccessible is used (inside rowgroup)
+ cell: "t3_dc_2",
+ rowHeaderCells: [ "t3_rh_2" ],
+ columnHeaderCells: [ ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="implement IAccessibleTable2"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">Mozilla Bug 512424</a>
+ <a target="_blank"
+ title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Mozilla Bug 513848</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="grid">
+ <div role="row">
+ <span id="table_ch_1" role="columnheader">col_1</span>
+ <span id="table_ch_2" role="columnheader">col_2</span>
+ <span id="table_ch_3" role="columnheader">col_3</span>
+ </div>
+ <div role="row">
+ <span id="table_rh_1" role="rowheader">row_1</span>
+ <span id="table_dc_1" role="gridcell">cell1</span>
+ <span id="table_dc_2" role="gridcell">cell2</span>
+ </div>
+ <div role="row">
+ <span id="table_rh_2" role="rowheader">row_2</span>
+ <span id="table_dc_3" role="gridcell">cell3</span>
+ <span id="table_dc_4" role="gridcell">cell4</span>
+ </div>
+ </div>
+
+ <div role="grid">
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td id="table2_ch_1" role="columnheader">header1</td>
+ <td id="table2_ch_2" role="columnheader">header2</td>
+ </tr>
+ </table>
+ </div>
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td id="table2_dc_1" role="gridcell">cell1</td>
+ <td id="table2_dc_2" role="gridcell" tabindex="-1">cell2</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <div role="grid">
+ <table role="presentation">
+ <tbody role="presentation">
+ <tr role="row">
+ <th id="t3_rh_1" role="rowheader">Row 1</th>
+ <td id="t3_dc_1" role="gridcell" tabindex="-1">
+ Apple Inc.
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <div role="rowgroup" tabindex="0">
+ <table role="presentation">
+ <tbody role="presentation">
+ <tr role="row">
+ <th id="t3_rh_2" role="rowheader">Row 2</th>
+ <td id="t3_dc_2" role="gridcell" tabindex="-1">
+ Apple-Shmapple Inc.
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_headers_ariatable.html b/accessible/tests/mochitest/table/test_headers_ariatable.html
new file mode 100644
index 000000000..d6d3b1a97
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_ariatable.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <title>Table header information cells for ARIA table</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // column and row headers from markup
+
+ headerInfoMap = [
+ {
+ cell: "table_dc_1",
+ rowHeaderCells: [ "table_rh_1" ],
+ columnHeaderCells: [ "table_ch_2" ]
+ },
+ {
+ cell: "table_dc_2",
+ rowHeaderCells: [ "table_rh_1" ],
+ columnHeaderCells: [ "table_ch_3" ]
+ },
+ {
+ cell: "table_dc_3",
+ rowHeaderCells: [ "table_rh_2" ],
+ columnHeaderCells: [ "table_ch_2" ]
+ },
+ {
+ cell: "table_dc_4",
+ rowHeaderCells: [ "table_rh_2" ],
+ columnHeaderCells: [ "table_ch_3" ]
+ },
+ {
+ cell: "table_rh_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table_ch_1" ]
+ },
+ {
+ cell: "table_rh_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table_ch_1" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="support ARIA table and cell roles"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">Bug 1173364</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="table">
+ <div role="row">
+ <span id="table_ch_1" role="columnheader">col_1</span>
+ <span id="table_ch_2" role="columnheader">col_2</span>
+ <span id="table_ch_3" role="columnheader">col_3</span>
+ </div>
+ <div role="row">
+ <span id="table_rh_1" role="rowheader">row_1</span>
+ <span id="table_dc_1" role="cell">cell1</span>
+ <span id="table_dc_2" role="cell">cell2</span>
+ </div>
+ <div role="row">
+ <span id="table_rh_2" role="rowheader">row_2</span>
+ <span id="table_dc_3" role="cell">cell3</span>
+ <span id="table_dc_4" role="cell">cell4</span>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_headers_listbox.xul b/accessible/tests/mochitest/table/test_headers_listbox.xul
new file mode 100644
index 000000000..343f112db
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_listbox.xul
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Table header information cells for XUL listbox">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // XUL listbox
+
+ var headerInfoMap = [
+ {
+ cell: "lb1_cell0",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header1" ]
+ },
+ {
+ cell: "lb1_cell1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header2" ]
+ },
+ {
+ cell: "lb1_cell2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header3" ]
+ },
+ {
+ cell: "lb1_cell3",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header1" ]
+ },
+ {
+ cell: "lb1_cell4",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header2" ]
+ },
+ {
+ cell: "lb1_cell5",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb1_header3" ]
+ },
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // XUL listbox with ARIA
+
+ headerInfoMap = [
+ {
+ cell: "lb2_cell0",
+ rowHeaderCells: [],
+ columnHeaderCells: []
+ },
+ {
+ cell: "lb2_cell1",
+ rowHeaderCells: [],
+ columnHeaderCells: []
+ },
+ {
+ cell: "lb2_cell2",
+ rowHeaderCells: [],
+ columnHeaderCells: []
+ },
+ {
+ cell: "lb2_cell3",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb2_cell0" ]
+ },
+ {
+ cell: "lb2_cell4",
+ rowHeaderCells: [ "lb2_cell3" ],
+ columnHeaderCells: [ "lb2_cell1" ]
+ },
+ {
+ cell: "lb2_cell5",
+ rowHeaderCells: [ "lb2_cell3" ],
+ columnHeaderCells: [ "lb2_cell2" ]
+ },
+ {
+ cell: "lb2_cell6",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "lb2_cell0" ]
+ },
+ {
+ cell: "lb2_cell7",
+ rowHeaderCells: [ "lb2_cell6" ],
+ columnHeaderCells: [ "lb2_cell1" ]
+ },
+ {
+ cell: "lb2_cell8",
+ rowHeaderCells: [ "lb2_cell6" ],
+ columnHeaderCells: [ "lb2_cell2" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <label control="listbox" value="multicolumn listbox with header"/>
+ <listbox id="listbox">
+ <listhead>
+ <listheader id="lb1_header1" label="header1"/>
+ <listheader id="lb1_header2" label="header2"/>
+ <listheader id="lb1_header3" label="header3"/>
+ </listhead>
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell id="lb1_cell0" label="cell0"/>
+ <listcell id="lb1_cell1" label="cell1"/>
+ <listcell id="lb1_cell2" label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell id="lb1_cell3" label="cell3"/>
+ <listcell id="lb1_cell4" label="cell4"/>
+ <listcell id="lb1_cell5" label="cell5"/>
+ </listitem>
+ <listitem>
+ <listcell id="lb1_cell6" label="cell6"/>
+ <listcell id="lb1_cell7" label="cell7"/>
+ <listcell id="lb1_cell8" label="cell8"/>
+ </listitem>
+ </listbox>
+
+ <label control="listbox2" value="multicolumn listbox with ARIA headers"/>
+ <listbox id="listbox2">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell role="columnheader" id="lb2_cell0" label="cell0"/>
+ <listcell role="columnheader" id="lb2_cell1" label="cell1"/>
+ <listcell role="columnheader" id="lb2_cell2" label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell role="rowheader" id="lb2_cell3" label="cell3"/>
+ <listcell id="lb2_cell4" label="cell4"/>
+ <listcell id="lb2_cell5" label="cell5"/>
+ </listitem>
+ <listitem>
+ <listcell role="rowheader" id="lb2_cell6" label="cell6"/>
+ <listcell id="lb2_cell7" label="cell7"/>
+ <listcell id="lb2_cell8" label="cell8"/>
+ </listitem>
+ </listbox>
+
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_headers_table.html b/accessible/tests/mochitest/table/test_headers_table.html
new file mode 100644
index 000000000..26691fbfb
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_table.html
@@ -0,0 +1,713 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <title>Table header information cells for HTML table</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // column header from thead and row header from @scope inside of tfoot
+
+ var headerInfoMap = [
+ {
+ cell: "table1_cell_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_weekday", "table1_date" ]
+ },
+ {
+ cell: "table1_cell_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_day", "table1_date" ]
+ },
+ {
+ cell: "table1_cell_3",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_qty" ]
+ },
+ {
+ cell: "table1_cell_4",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_weekday", "table1_date" ]
+ },
+ {
+ cell: "table1_cell_5",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_day", "table1_date" ]
+ },
+ {
+ cell: "table1_cell_6",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table1_qty" ]
+ },
+ {
+ cell: "table1_cell_7",
+ rowHeaderCells: [ "table1_total" ],
+ columnHeaderCells: [ "table1_qty" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // column and row headers from thead and @scope
+
+ headerInfoMap = [
+ {
+ cell: "table2_cell_2",
+ rowHeaderCells: [ "table2_rh_1" ],
+ columnHeaderCells: [ "table2_ch_2" ]
+ },
+ {
+ cell: "table2_cell_3",
+ rowHeaderCells: [ "table2_rh_1" ],
+ columnHeaderCells: [ "table2_ch_3" ]
+ },
+ {
+ cell: "table2_cell_5",
+ rowHeaderCells: [ "table2_rh_2" ],
+ columnHeaderCells: [ "table2_ch_2" ]
+ },
+ {
+ cell: "table2_cell_6",
+ rowHeaderCells: [ "table2_rh_2" ],
+ columnHeaderCells: [ "table2_ch_3" ]
+ },
+ {
+ cell: "table2_rh_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table2_ch_1" ]
+ },
+ {
+ cell: "table2_rh_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table2_ch_1" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // column headers from @headers
+
+ headerInfoMap = [
+ {
+ cell: "table3_cell_1",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table3_ch_1" ]
+ },
+ {
+ cell: "table3_cell_2",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table3_ch_2" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // table consisted of one column
+
+ headerInfoMap = [
+ {
+ cell: "table4_cell",
+ rowHeaderCells: [],
+ columnHeaderCells: [ "table4_ch" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // table consisted of one row
+
+ headerInfoMap = [
+ {
+ cell: "table5_cell",
+ rowHeaderCells: [ "table5_rh" ],
+ columnHeaderCells: [ ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // @headers points to table cells
+
+ headerInfoMap = [
+ {
+ cell: "table6_cell",
+ rowHeaderCells: [ "table6_rh" ],
+ columnHeaderCells: [ "table6_ch" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // @scope="rowgroup" and @scope="row"
+
+ headerInfoMap = [
+ {
+ cell: "t7_r1c1",
+ rowHeaderCells: [ "t7_Mary", "t7_Females" ],
+ columnHeaderCells: [ "t7_1km" ]
+ },
+ {
+ cell: "t7_r1c2",
+ rowHeaderCells: [ "t7_Mary", "t7_Females" ],
+ columnHeaderCells: [ "t7_5km" ]
+ },
+ {
+ cell: "t7_r1c3",
+ rowHeaderCells: [ "t7_Mary", "t7_Females" ],
+ columnHeaderCells: [ "t7_10km" ]
+ },
+ {
+ cell: "t7_r2c1",
+ rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
+ columnHeaderCells: [ "t7_1km" ]
+ },
+ {
+ cell: "t7_r2c2",
+ rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
+ columnHeaderCells: [ "t7_5km" ]
+ },
+ {
+ cell: "t7_r2c3",
+ rowHeaderCells: [ "t7_Betsy", "t7_Females" ],
+ columnHeaderCells: [ "t7_10km" ]
+ },
+ {
+ cell: "t7_r3c1",
+ rowHeaderCells: [ "t7_Matt", "t7_Males" ],
+ columnHeaderCells: [ "t7_1km" ]
+ },
+ {
+ cell: "t7_r3c2",
+ rowHeaderCells: [ "t7_Matt", "t7_Males" ],
+ columnHeaderCells: [ "t7_5km" ]
+ },
+ {
+ cell: "t7_r3c3",
+ rowHeaderCells: [ "t7_Matt", "t7_Males" ],
+ columnHeaderCells: [ "t7_10km" ]
+ },
+ {
+ cell: "t7_r4c1",
+ rowHeaderCells: [ "t7_Todd", "t7_Males" ],
+ columnHeaderCells: [ "t7_1km" ]
+ },
+ {
+ cell: "t7_r4c2",
+ rowHeaderCells: [ "t7_Todd", "t7_Males" ],
+ columnHeaderCells: [ "t7_5km" ]
+ },
+ {
+ cell: "t7_r4c3",
+ rowHeaderCells: [ "t7_Todd", "t7_Males" ],
+ columnHeaderCells: [ "t7_10km" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // @scope="colgroup" and @scope="col"
+
+ headerInfoMap = [
+ {
+ cell: "t8_r1c1",
+ rowHeaderCells: [ "t8_1km" ],
+ columnHeaderCells: [ "t8_Mary", "t8_Females" ]
+ },
+ {
+ cell: "t8_r1c2",
+ rowHeaderCells: [ "t8_1km" ],
+ columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
+ },
+ {
+ cell: "t8_r1c3",
+ rowHeaderCells: [ "t8_1km" ],
+ columnHeaderCells: [ "t8_Matt", "t8_Males" ]
+ },
+ {
+ cell: "t8_r1c4",
+ rowHeaderCells: [ "t8_1km" ],
+ columnHeaderCells: [ "t8_Todd", "t8_Males" ]
+ },
+ {
+ cell: "t8_r2c1",
+ rowHeaderCells: [ "t8_5km" ],
+ columnHeaderCells: [ "t8_Mary", "t8_Females" ]
+ },
+ {
+ cell: "t8_r2c2",
+ rowHeaderCells: [ "t8_5km" ],
+ columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
+ },
+ {
+ cell: "t8_r2c3",
+ rowHeaderCells: [ "t8_5km" ],
+ columnHeaderCells: [ "t8_Matt", "t8_Males" ]
+ },
+ {
+ cell: "t8_r2c4",
+ rowHeaderCells: [ "t8_5km" ],
+ columnHeaderCells: [ "t8_Todd", "t8_Males" ]
+ },
+ {
+ cell: "t8_r3c1",
+ rowHeaderCells: [ "t8_10km" ],
+ columnHeaderCells: [ "t8_Mary", "t8_Females" ]
+ },
+ {
+ cell: "t8_r3c2",
+ rowHeaderCells: [ "t8_10km" ],
+ columnHeaderCells: [ "t8_Betsy", "t8_Females" ]
+ },
+ {
+ cell: "t8_r3c3",
+ rowHeaderCells: [ "t8_10km" ],
+ columnHeaderCells: [ "t8_Matt", "t8_Males" ]
+ },
+ {
+ cell: "t8_r3c4",
+ rowHeaderCells: [ "t8_10km" ],
+ columnHeaderCells: [ "t8_Todd", "t8_Males" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // spanned table header cells (v1), @headers define header order
+
+ headerInfoMap = [
+ {
+ cell: "t9_r1c1",
+ rowHeaderCells: [ "t9_females", "t9_mary" ],
+ columnHeaderCells: [ "t9_1km" ]
+ },
+ {
+ cell: "t9_r1c2",
+ rowHeaderCells: [ "t9_females", "t9_mary" ],
+ columnHeaderCells: [ "t9_5km" ]
+ },
+ {
+ cell: "t9_r1c3",
+ rowHeaderCells: [ "t9_females", "t9_mary" ],
+ columnHeaderCells: [ "t9_10km" ]
+ },
+ {
+ cell: "t9_r2c1",
+ rowHeaderCells: [ "t9_females", "t9_betsy" ],
+ columnHeaderCells: [ "t9_1km" ]
+ },
+ {
+ cell: "t9_r2c2",
+ rowHeaderCells: [ "t9_females", "t9_betsy" ],
+ columnHeaderCells: [ "t9_5km" ]
+ },
+ {
+ cell: "t9_r2c3",
+ rowHeaderCells: [ "t9_females", "t9_betsy" ],
+ columnHeaderCells: [ "t9_10km" ]
+ },
+ {
+ cell: "t9_r3c1",
+ rowHeaderCells: [ "t9_males", "t9_matt" ],
+ columnHeaderCells: [ "t9_1km" ]
+ },
+ {
+ cell: "t9_r3c2",
+ rowHeaderCells: [ "t9_males", "t9_matt" ],
+ columnHeaderCells: [ "t9_5km" ]
+ },
+ {
+ cell: "t9_r3c3",
+ rowHeaderCells: [ "t9_males", "t9_matt" ],
+ columnHeaderCells: [ "t9_10km" ]
+ },
+ {
+ cell: "t9_r4c1",
+ rowHeaderCells: [ "t9_males", "t9_todd" ],
+ columnHeaderCells: [ "t9_1km" ]
+ },
+ {
+ cell: "t9_r4c2",
+ rowHeaderCells: [ "t9_males", "t9_todd" ],
+ columnHeaderCells: [ "t9_5km" ]
+ },
+ {
+ cell: "t9_r4c3",
+ rowHeaderCells: [ "t9_males", "t9_todd" ],
+ columnHeaderCells: [ "t9_10km" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ //////////////////////////////////////////////////////////////////////////
+ // spanned table header cells (v2), @headers define header order
+
+ headerInfoMap = [
+ {
+ cell: "t10_r1c1",
+ rowHeaderCells: [ "t10_1km" ],
+ columnHeaderCells: [ "t10_females", "t10_mary" ]
+ },
+ {
+ cell: "t10_r1c2",
+ rowHeaderCells: [ "t10_1km" ],
+ columnHeaderCells: [ "t10_females", "t10_betsy" ]
+ },
+ {
+ cell: "t10_r1c3",
+ rowHeaderCells: [ "t10_1km" ],
+ columnHeaderCells: [ "t10_males", "t10_matt" ]
+ },
+ {
+ cell: "t10_r1c4",
+ rowHeaderCells: [ "t10_1km" ],
+ columnHeaderCells: [ "t10_males", "t10_todd" ]
+ },
+ {
+ cell: "t10_r2c1",
+ rowHeaderCells: [ "t10_5km" ],
+ columnHeaderCells: [ "t10_females", "t10_mary" ]
+ },
+ {
+ cell: "t10_r2c2",
+ rowHeaderCells: [ "t10_5km" ],
+ columnHeaderCells: [ "t10_females", "t10_betsy" ]
+ },
+ {
+ cell: "t10_r2c3",
+ rowHeaderCells: [ "t10_5km" ],
+ columnHeaderCells: [ "t10_males", "t10_matt" ]
+ },
+ {
+ cell: "t10_r2c4",
+ rowHeaderCells: [ "t10_5km" ],
+ columnHeaderCells: [ "t10_males", "t10_todd" ]
+ },
+ {
+ cell: "t10_r3c1",
+ rowHeaderCells: [ "t10_10km" ],
+ columnHeaderCells: [ "t10_females", "t10_mary" ]
+ },
+ {
+ cell: "t10_r3c2",
+ rowHeaderCells: [ "t10_10km" ],
+ columnHeaderCells: [ "t10_females", "t10_betsy" ]
+ },
+ {
+ cell: "t10_r3c3",
+ rowHeaderCells: [ "t10_10km" ],
+ columnHeaderCells: [ "t10_males", "t10_matt" ]
+ },
+ {
+ cell: "t10_r3c4",
+ rowHeaderCells: [ "t10_10km" ],
+ columnHeaderCells: [ "t10_males", "t10_todd" ]
+ }
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="implement IAccessibleTable2"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">
+ Bug 512424
+ </a>
+ <a target="_blank"
+ title="Table headers not associated when header is a td element with no scope"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=704465">
+ Bug 704465
+ </a>
+ <a target="_blank"
+ title="Support rowgroup and colgroup HTML scope"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1141978">
+ Bug 1141978
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table id="table1" border="1">
+ <thead>
+ <tr>
+ <th id="table1_date" colspan="2">Date</th>
+ <th id="table1_qty" rowspan="2">Qty</th>
+ </tr>
+ <tr>
+ <th id="table1_weekday">Weekday</th>
+ <th id="table1_day">Day</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table1_cell_1">Mon</td>
+ <td id="table1_cell_2">1</td>
+ <td id="table1_cell_3">20</td>
+ </tr>
+ <tr>
+ <td id="table1_cell_4">Thu</td>
+ <td id="table1_cell_5">2</td>
+ <td id="table1_cell_6">15</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th id="table1_total" scope="row" colspan="2">Total</th>
+ <td id="table1_cell_7">35</td>
+ </tr>
+ </tfoot>
+ </table>
+
+ <table id="table2" border="1">
+ <thead>
+ <tr>
+ <th id="table2_ch_1">col1</th>
+ <th id="table2_ch_2">col2</th>
+ <td id="table2_ch_3" scope="col">col3</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="table2_rh_1">row1</th>
+ <td id="table2_cell_2">cell1</td>
+ <td id="table2_cell_3">cell2</td>
+ </tr>
+ <tr>
+ <td id="table2_rh_2" scope="row">row2</td>
+ <td id="table2_cell_5">cell3</td>
+ <td id="table2_cell_6">cell4</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="table3" border="1">
+ <tr>
+ <td id="table3_cell_1" headers="table3_ch_1">cell1</td>
+ <td id="table3_cell_2" headers="table3_ch_2">cell2</td>
+ </tr>
+ <tr>
+ <td id="table3_ch_1" scope="col">col1</td>
+ <td id="table3_ch_2" scope="col">col2</td>
+ </tr>
+ </table>
+
+ <table id="table4">
+ <thead>
+ <tr>
+ <th id="table4_ch">colheader</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table4_cell">bla</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="table5">
+ <tr>
+ <th id="table5_rh">rowheader</th>
+ <td id="table5_cell">cell</td>
+ </tr>
+ </table>
+
+ <table id="table6">
+ <tr>
+ <td>empty cell</th>
+ <td id="table6_ch">colheader</td>
+ </tr>
+ <tr>
+ <td id="table6_rh">rowheader</th>
+ <td id="table6_cell" headers="table6_ch table6_rh">cell</td>
+ </tr>
+ </table>
+
+ <table id="table7" class="data complex" border="1">
+ <caption>Version 1 with rowgroup</caption>
+ <thead>
+ <tr>
+ <td colspan="2">&nbsp;</td>
+ <th id="t7_1km" scope="col">1 km</th>
+ <th id="t7_5km" scope="col">5 km</th>
+ <th id="t7_10km" scope="col">10 km</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="t7_Females" rowspan="2" scope="rowgroup">Females</th>
+ <th id="t7_Mary" scope="row">Mary</th>
+ <td id="t7_r1c1">8:32</td>
+ <td id="t7_r1c2">28:04</td>
+ <td id="t7_r1c3">1:01:16</td>
+ </tr>
+ <tr>
+ <th id="t7_Betsy" scope="row">Betsy</th>
+ <td id="t7_r2c1">7:43</td>
+ <td id="t7_r2c2">26:47</td>
+ <td id="t7_r2c3">55:38</td>
+ </tr>
+ <tr>
+ <th id="t7_Males" rowspan="2" scope="rowgroup">Males</th>
+ <th id="t7_Matt" scope="row">Matt</th>
+ <td id="t7_r3c1">7:55</td>
+ <td id="t7_r3c2">27:29</td>
+ <td id="t7_r3c3">57:04</td>
+ </tr>
+ <tr>
+ <th id="t7_Todd" scope="row">Todd</th>
+ <td id="t7_r4c1">7:01</td>
+ <td id="t7_r4c2">24:21</td>
+ <td id="t7_r4c3">50:35</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="table8" class="data complex" border="1">
+ <caption>Version 2 with colgroup</caption>
+ <thead>
+ <tr>
+ <td rowspan="2">&nbsp;</td>
+ <th id="t8_Females" colspan="2" scope="colgroup">Females</th>
+ <th id="t8_Males" colspan="2" scope="colgroup">Males</th>
+ </tr>
+ <tr>
+ <th id="t8_Mary" scope="col">Mary</th>
+ <th id="t8_Betsy" scope="col">Betsy</th>
+ <th id="t8_Matt" scope="col">Matt</th>
+ <th id="t8_Todd" scope="col">Todd</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="t8_1km" scope="row">1 km</th>
+ <td id="t8_r1c1">8:32</td>
+ <td id="t8_r1c2">7:43</td>
+ <td id="t8_r1c3">7:55</td>
+ <td id="t8_r1c4">7:01</td>
+ </tr>
+ <tr>
+ <th id="t8_5km" scope="row">5 km</th>
+ <td id="t8_r2c1">28:04</td>
+ <td id="t8_r2c2">26:47</td>
+ <td id="t8_r2c3">27:27</td>
+ <td id="t8_r2c4">24:21</td>
+ </tr>
+ <tr>
+ <th id="t8_10km" scope="row">10 km</th>
+ <td id="t8_r3c1">1:01:16</td>
+ <td id="t8_r3c2">55:38</td>
+ <td id="t8_r3c3">57:04</td>
+ <td id="t8_r3c4">50:35</td>
+ </tr>
+
+ </tbody>
+ </table>
+
+ <table id="table9" border="1">
+ <caption>
+ Example 1 (row group headers):
+ </caption>
+ <tr>
+ <td colspan="2"><span class="offscreen">empty</span></td>
+ <th id="t9_1km" width="40">1 km</th>
+ <th id="t9_5km" width="35">5 km</th>
+ <th id="t9_10km" width="42">10 km</th>
+ </tr>
+ <tr>
+ <th id="t9_females" width="56" rowspan="2">Females</th>
+ <th id="t9_mary" width="39">Mary</th>
+ <td id="t9_r1c1" headers="t9_females t9_mary t9_1km">8:32</td>
+ <td id="t9_r1c2" headers="t9_females t9_mary t9_5km">28:04</td>
+ <td id="t9_r1c3" headers="t9_females t9_mary t9_10km">1:01:16</td>
+ </tr>
+ <tr>
+ <th id="t9_betsy">Betsy</th>
+ <td id="t9_r2c1" headers="t9_females t9_betsy t9_1km">7:43</td>
+ <td id="t9_r2c2" headers="t9_females t9_betsy t9_5km">26:47</td>
+ <td id="t9_r2c3" headers="t9_females t9_betsy t9_10km">55:38</td>
+ </tr>
+ <tr>
+ <th id="t9_males" rowspan="2">Males</th>
+ <th id="t9_matt">Matt</th>
+ <td id="t9_r3c1" headers="t9_males t9_matt t9_1km">7:55</td>
+ <td id="t9_r3c2" headers="t9_males t9_matt t9_5km">27:29</td>
+ <td id="t9_r3c3" headers="t9_males t9_matt t9_10km">57:04</td>
+ </tr>
+ <tr>
+ <th id="t9_todd">Todd</th>
+ <td id="t9_r4c1" headers="t9_males t9_todd t9_1km">7:01</td>
+ <td id="t9_r4c2" headers="t9_males t9_todd t9_5km">24:21</td>
+ <td id="t9_r4c3" headers="t9_males t9_todd t9_10km">50:35</td>
+ </tr>
+ </table>
+
+ <table id="table10" border="1">
+ <caption>
+ Example 2 (column group headers):
+ </caption>
+ <tr>
+ <td rowspan="2"><span class="offscreen">empty</span></td>
+ <th colspan="2" id="t10_females">Females</th>
+ <th colspan="2" id="t10_males">Males</th>
+ </tr>
+ <tr>
+ <th width="40" id="t10_mary">Mary</th>
+ <th width="35" id="t10_betsy">Betsy</th>
+ <th width="42" id="t10_matt">Matt</th>
+ <th width="42" id="t10_todd">Todd</th>
+ </tr>
+ <tr>
+ <th width="39" id="t10_1km">1 km</th>
+ <td headers="t10_females t10_mary t10_1km" id="t10_r1c1">8:32</td>
+ <td headers="t10_females t10_betsy t10_1km" id="t10_r1c2">7:43</td>
+ <td headers="t10_males t10_matt t10_1km" id="t10_r1c3">7:55</td>
+ <td headers="t10_males t10_todd t10_1km" id="t10_r1c4">7:01</td>
+ </tr>
+ <tr>
+ <th id="t10_5km">5 km</th>
+ <td headers="t10_females t10_mary t10_5km" id="t10_r2c1">28:04</td>
+ <td headers="t10_females t10_betsy t10_5km" id="t10_r2c2">26:47</td>
+ <td headers="t10_males t10_matt t10_5km" id="t10_r2c3">27:29</td>
+ <td headers="t10_males t10_todd t10_5km" id="t10_r2c4">24:21</td>
+ </tr>
+ <tr>
+ <th id="t10_10km">10 km</th>
+ <td headers="t10_females t10_mary t10_10km" id="t10_r3c1">1:01:16</td>
+ <td headers="t10_females t10_betsy t10_10km" id="t10_r3c2">55:38</td>
+ <td headers="t10_males t10_matt t10_10km" id="t10_r3c3">57:04</td>
+ <td headers="t10_males t10_todd t10_10km" id="t10_r3c4">50:35</td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_headers_tree.xul b/accessible/tests/mochitest/table/test_headers_tree.xul
new file mode 100644
index 000000000..9b69e8a0d
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_headers_tree.xul
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Table header information cells for XUL tree">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../table.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ var treeAcc = getAccessible("tree", [nsIAccessibleTable]);
+
+ var headerInfoMap = [
+ {
+ cell: treeAcc.getCellAt(0, 0),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "col" ]
+ },
+ {
+ cell: treeAcc.getCellAt(0, 1),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "scol" ]
+ },
+ {
+ cell: treeAcc.getCellAt(1, 0),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "col" ]
+ },
+ {
+ cell: treeAcc.getCellAt(1, 1),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "scol" ]
+ },
+ {
+ cell: treeAcc.getCellAt(2, 0),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "col" ]
+ },
+ {
+ cell: treeAcc.getCellAt(2, 1),
+ rowHeaderCells: [],
+ columnHeaderCells: [ "scol" ]
+ },
+ ];
+
+ testHeaderCells(headerInfoMap);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTableTreeView(3));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="scol" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_indexes_ariagrid.html b/accessible/tests/mochitest/table/test_indexes_ariagrid.html
new file mode 100644
index 000000000..62ef0ed9d
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_indexes_ariagrid.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Table indexes for ARIA grid tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid
+ var idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8],
+ [9, 10, 11]
+ ];
+ testTableIndexes("grid", idxes);
+
+ idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8],
+ [9, 10, 11]
+ ];
+ testTableIndexes("grid-rowgroups", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // a bit crazy ARIA grid
+ idxes = [
+ [0, 1],
+ [2, 3]
+ ];
+ testTableIndexes("grid2", idxes);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=386813"
+ title="support nsIAccessibleTable on ARIA grid/treegrid">Mozilla Bug 386813</a>
+ <a target="_blank"
+ title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Mozilla Bug 513848</a>
+ <a target="_blank"
+ title="ARIA grid with rowgroup breaks table row/col counting and indices"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761853">Mozilla Bug 761853</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="grid" id="grid">
+ <div role="row">
+ <span role="columnheader">column1</span>
+ <span role="columnheader">column2</span>
+ <span role="columnheader">column3</span>
+ </div>
+ <div role="row">
+ <span role="rowheader">row1</span>
+ <span role="gridcell">cell1</span>
+ <span role="gridcell">cell2</span>
+ </div>
+ <div role="row">
+ <span role="rowheader">row2</span>
+ <span role="gridcell">cell3</span>
+ <span role="gridcell">cell4</span>
+ </div>
+ <div role="row">
+ <span role="rowheader">row3</span>
+ <span role="gridcell">cell5</span>
+ <span role="gridcell">cell6</span>
+ </div>
+ </div>
+
+ <div role="grid" id="grid-rowgroups">
+ <div role="row">
+ <span role="columnheader">grid-rowgroups-col1</span>
+ <span role="columnheader">grid-rowgroups-col2</span>
+ <span role="columnheader">grid-rowgroups-col3</span>
+ </div>
+ <div role="rowgroup">
+ <div role="row">
+ <span role="rowheader">grid-rowgroups-row1</span>
+ <span role="gridcell">grid-rowgroups-cell1</span>
+ <span role="gridcell">grid-rowgroups-cell2</span>
+ </div>
+ <div role="row">
+ <span role="rowheader">grid-rowgroups-row2</span>
+ <span role="gridcell">grid-rowgroups-cell3</span>
+ <span role="gridcell">grid-rowgroups-cell4</span>
+ </div>
+ </div>
+ <div role="row">
+ <span role="rowheader">grid-rowgroups-row3</span>
+ <span role="gridcell">grid-rowgroups-cell5</span>
+ <span role="gridcell">grid-rowgroups-cell6</span>
+ </div>
+ </div>
+
+ <div role="grid" id="grid2">
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="columnheader">header1</td>
+ <td role="columnheader">header2</td>
+ </tr>
+ </table>
+ </div>
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="gridcell">cell1</td>
+ <td role="gridcell" tabindex="-1">cell2</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_indexes_listbox.xul b/accessible/tests/mochitest/table/test_indexes_listbox.xul
new file mode 100644
index 000000000..997d687f4
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_indexes_listbox.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Table indices of accessible table for XUL listbox">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ var idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 8]
+ ];
+ testTableIndexes("listbox", idxes);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <label control="listbox" value="multicolumn listbox with header"/>
+ <listbox id="listbox">
+ <listhead>
+ <listheader label="header1"/>
+ <listheader label="header2"/>
+ <listheader label="header3"/>
+ </listhead>
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell label="cell0"/>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell3"/>
+ <listcell label="cell4"/>
+ <listcell label="cell5"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell6"/>
+ <listcell label="cell7"/>
+ <listcell label="cell8"/>
+ </listitem>
+ </listbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_indexes_table.html b/accessible/tests/mochitest/table/test_indexes_table.html
new file mode 100644
index 000000000..356a55933
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_indexes_table.html
@@ -0,0 +1,410 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=410052
+-->
+<head>
+ <title>Table indexes chrome tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // table
+ var idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 7],
+ [6, 8, 9]
+ ];
+
+ testTableIndexes("table", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableborder
+ idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 7],
+ [6, 8, 9]
+ ];
+
+ testTableIndexes("tableborder", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // table
+ var idxes = [
+ [ 0, 1, 2, 2, 3, 4, 5, 6],
+ [ 7, 8, 9, 10, 11, 12, 13, 6],
+ [14, 15, 15, 16, 17, 18, 19, 6],
+ [20, 15, 15, 21, 22, 18, 23, 6]
+ ];
+
+ testTableIndexes("table2", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane1 (empty row groups)
+ idxes = [
+ [0, 1, 2],
+ [3, 4, 5],
+ [6, 7, 7],
+ [6, 8, 9]
+ ];
+
+ testTableIndexes("tableinsane1", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane2 (empry rows)
+ idxes = [
+ [-1, -1, -1],
+ [-1, -1, -1],
+ [ 0, 1, 2]
+ ];
+
+ testTableIndexes("tableinsane2", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane3 (cell holes)
+ idxes = [
+ [0, 1, -1],
+ [2, 3, 4]
+ ];
+
+ testTableIndexes("tableinsane3", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane3.2 (cell holes, row spans, fixed in bug 417912)
+ idxes = [
+ [0, 1, 2],
+ [3, -1, 2],
+ [4, 5, 2]
+ ];
+
+ testTableIndexes("tableinsane3.2", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane4 (empty row groups/rows and cell holes)
+ idxes = [
+ [ 0, 1, 2],
+ [-1, -1, -1],
+ [ 3, 4, 5],
+ [ 6, 6, 7],
+ [ 8, -1, 7],
+ [ 9, 9, 9]
+ ];
+ testTableIndexes("tableinsane4", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane5 (just a crazy table)
+ idxes = [
+ [ 0, 1, 2, -1, -1],
+ [-1, -1, -1, -1, -1],
+ [ 3, 4, 5, -1, -1],
+ [ 6, 7, -1, -1, -1],
+ [ 6, 8, 9, -1, -1],
+ [ 6, 10, 9, 11, 12]
+ ];
+ testTableIndexes("tableinsane5", idxes);
+
+ //////////////////////////////////////////////////////////////////////////
+ // tableinsane6 (overlapping cells, mad table)
+ idxes = [
+ [ 0, 1, 2, -1, -1],
+ [-1, -1, -1, -1, -1],
+ [ 3, 4, 5, -1, -1],
+ [ 6, 6, 7, -1, -1],
+ [ 8, 9, 7, -1, -1],
+ [ 10, 9, 7, 11, 12]
+ ];
+ testTableIndexes("tableinsane6", idxes);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="GetIndexAt and GetRowAtIndex and GetColumnAtIndex on HTML tables are inconsistent"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">
+ Bug 410052
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!--
+ If you change the structure of the table please make sure to change
+ the indexes count in 'for' statement in the script above.
+ -->
+ <table border="1" id="table">
+ <caption><strong><b><font size="29">this is a caption for this table</font></b></strong></caption>
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td rowspan="0">4</td>
+ <td colspan="2">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td>7</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="1" id="tableborder" style="border-collapse:collapse">
+ <caption><strong><b><font size="29">this is a caption for this bc table</font></b></strong></caption>
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td rowspan="2">4</td>
+ <td colspan="2">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td>7</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table cellpadding="2" cellspacing="2" border="1" width="50%" id="table2">
+ <caption>column and row spans</caption>
+ <tbody>
+ <tr>
+ <td>0</td>
+ <td>1</td>
+ <td rowspan="1" colspan="2">2</td>
+ <td>3</td>
+ <td>4</td>
+ <td>5</td>
+ <td rowspan="4" colspan="1">6</td>
+ </tr>
+ <tr>
+ <td>7</td>
+ <td>8</td>
+ <td>8</td>
+ <td>10</td>
+ <td>11</td>
+ <td>12</td>
+ <td>13</td>
+ </tr>
+ <tr>
+ <td>14</td>
+ <td rowspan="2" colspan="2">15</td>
+ <td>16</td>
+ <td>17</td>
+ <td rowspan="2" colspan="1">18</td>
+ <td>19</td>
+ </tr>
+ <tr>
+ <td>20</td>
+ <td>21</td>
+ <td>22</td>
+ <td>23</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="1" id="tableinsane1">
+ <caption>test empty row groups</caption>
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody></tbody>
+ <tbody></tbody>
+ <tbody></tbody>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td rowspan="2">4</td>
+ <td colspan="2">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td>7</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="1" id="tableinsane2">
+ <caption>empty rows</caption>
+ <tbody><tr></tr><tr></tr></tbody>
+ <tbody></tbody>
+ <tbody>
+ <tr>
+ <td>0</td>
+ <td>1</td>
+ <td>2</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="1" id="tableinsane3">
+ <caption>missed cell</caption>
+ <tbody>
+ <tr>
+ <td>0</td>
+ <td>1</td>
+ </tr>
+ </tbody>
+ <tbody>
+ <tr>
+ <td>2</td>
+ <td>3</td>
+ <td>4</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table cellpadding="2" cellspacing="2" border="1" id="tableinsane3.2">
+ <tr><td>1</td><td>2</td><td rowspan=3>3</td>
+ <tr><td>4</td>
+ <tr><td>5</td><td>6</td>
+ </table>
+
+ <table border="1" id="tableinsane4">
+ <caption>test empty rows + cellmap holes</caption>
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody><tr></tr></tbody>
+ <tbody></tbody>
+ <tbody></tbody>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td colspan="2">4</td>
+ <td rowspan="2">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ </tr>
+ <tr>
+ <td colspan="3">7</td>
+ </tr>
+
+ </tbody>
+ </table>
+
+ <table border="1" id="tableinsane5">
+ <caption>just a crazy table</caption>
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody><tr></tr></tbody>
+ <tbody></tbody>
+ <tbody></tbody>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td rowspan="0">4</td>
+ <td colspan="0">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td rowspan="0">7</td>
+ </tr>
+ <tr>
+ <td>8</td>
+ <td>9</td>
+ <td>10</td>
+ </tr>
+
+ </tbody>
+
+ <table border="1" id="tableinsane6" >
+ <caption>overlapping cells</caption>
+ <thead>
+ <tr>
+ <th>header cell 0</th>
+ <th>header cell 1</th>
+ <th>header cell 2</th>
+ </tr>
+ </thead>
+ <tbody><tr></tr></tbody>
+ <tbody></tbody>
+ <tbody></tbody>
+ <tbody>
+ <tr>
+ <td>3</td>
+ <td>4</td>
+ <td>5</td>
+ </tr>
+ <tr>
+ <td colspan="2">6</td>
+ <td rowspan="0">7</td>
+ </tr>
+ <tr>
+ <td>8</td>
+ <td rowspan="0">9</td>
+ </tr>
+ <tr>
+ <td colspan="3">10</td>
+ <td>11</td>
+ <td>12</td>
+ </tr>
+ </tbody>
+ </table>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_indexes_tree.xul b/accessible/tests/mochitest/table/test_indexes_tree.xul
new file mode 100644
index 000000000..89a751419
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_indexes_tree.xul
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible Table indexes tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../table.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ var idxes = [
+ [0, 1],
+ [2, 3],
+ [4, 5]
+ ];
+ testTableIndexes("tree", idxes);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTableTreeView(3));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="scol" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_layoutguess.html b/accessible/tests/mochitest/table/test_layoutguess.html
new file mode 100644
index 000000000..e35193c85
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_layoutguess.html
@@ -0,0 +1,506 @@
+<html>
+<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=495388 -->
+<head>
+ <title>test HTMLTableAccessible::IsProbablyForLayout implementation</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Attribute we're looking for
+ var attr = {
+ "layout-guess": "true"
+ };
+
+ // table with role of grid
+ testAbsentAttrs("table1", attr);
+ // table with role of grid and datatable="0"
+ testAbsentAttrs("table1.1", attr);
+
+ // table with landmark role
+ testAbsentAttrs("table2", attr);
+
+ // table with summary
+ testAbsentAttrs("table3", attr);
+
+ // table with caption
+ testAbsentAttrs("table4", attr);
+
+ // layout table with empty caption
+ testAttrs("table4.2", attr, true);
+
+ // table with thead element
+ testAbsentAttrs("table5", attr);
+
+ // table with tfoot element
+ testAbsentAttrs("table5.1", attr);
+
+ // table with colgroup or col elements
+ testAbsentAttrs("table5.2", attr);
+ testAbsentAttrs("table5.3", attr);
+
+ // table with th element
+ testAbsentAttrs("table6", attr);
+
+ // table with headers attribute
+ testAbsentAttrs("table6.2", attr);
+
+ // table with scope attribute
+ testAbsentAttrs("table6.2.2", attr);
+
+ // table with abbr attribute
+ testAbsentAttrs("table6.2.3", attr);
+
+ // table with abbr element
+ testAbsentAttrs("table6.3", attr);
+
+ // table with abbr element having empty text node
+ testAbsentAttrs("table6.4", attr);
+
+ // table with abbr element and non-empty text node
+ testAttrs("table6.5", attr, true);
+
+ // layout table with nested table
+ testAttrs("table9", attr, true);
+
+ // layout table with 1 column
+ testAttrs("table10", attr, true);
+
+ // layout table with 1 row
+ testAttrs("table11", attr, true);
+
+ // table with 5 columns
+ testAbsentAttrs("table12", attr);
+
+ // table with a bordered cell
+ testAbsentAttrs("table13", attr);
+
+ // table with alternating row background colors
+ testAbsentAttrs("table14", attr);
+
+ // table with 3 columns and 21 rows
+ testAbsentAttrs("table15", attr);
+
+ // layout table that has a 100% width
+ testAttrs("table16", attr, true);
+
+ // layout table that has a 95% width in pixels
+ testAttrs("table17", attr, true);
+
+ // layout table with less than 10 columns
+ testAttrs("table18", attr, true);
+
+ // layout table with embedded iframe
+ testAttrs("table19", attr, true);
+
+ // tree grid, no layout table
+ testAbsentAttrs("table20", attr);
+
+ // layout table containing nested data table (having data structures)
+ testAttrs("table21", attr, true);
+ testAttrs("table21.2", attr, true);
+ testAttrs("table21.3", attr, true);
+ testAttrs("table21.4", attr, true);
+ testAttrs("table21.5", attr, true);
+ testAttrs("table21.6", attr, true);
+
+ // layout table having datatable="0" attribute and containing data table structure (tfoot element)
+ testAttrs("table22", attr, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=495388"
+ title="Don't treat tables that have a landmark role as layout table">
+ Mozilla Bug 495388
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=690222"
+ title="Data table elements used to determine layout-guess attribute shouldn't be picked from nested tables">
+ Mozilla Bug 690222
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=696975"
+ title="Extend the list of legitimate data table structures">
+ Mozilla Bug 696975
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Table with role of grid -->
+ <table id="table1" role="grid">
+ <tr>
+ <th>Sender</th>
+ <th>Subject</th>
+ <th>Date</th>
+ </tr>
+ <tr>
+ <td>Marco</td>
+ <td>Test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>David</td>
+ <td>Another test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>Alex</td>
+ <td>Third test</td>
+ <td>June 12</td>
+ </tr>
+ </table>
+ <!-- table with role of grid and datatable="0"-->
+ <table id="table1.1" role="grid" datatable="0">
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <!-- table with landmark role -->
+ <table id="table2" role="main">
+ <tr>
+ <th>Sender</th>
+ <th>Subject</th>
+ <th>Date</th>
+ </tr>
+ <tr>
+ <td>Marco</td>
+ <td>Test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>David</td>
+ <td>Another test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>Alex</td>
+ <td>Third test</td>
+ <td>June 12</td>
+ </tr>
+ </table>
+
+ <!-- table with summary -->
+ <table id="table3" summary="This is a table">
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <!-- table with caption -->
+ <table id="table4">
+ <caption>This is a table</caption>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <!-- layout table with empty caption -->
+ <table id="table4.2">
+ <caption> </caption>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <!-- table with thead element -->
+ <table id="table5">
+ <thead>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </thead>
+ </table>
+
+ <!-- table with tfoot element -->
+ <table id="table5.1">
+ <tfoot>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </tfoot>
+ </table>
+
+ <!-- table with colgroup and col elements -->
+ <table id="table5.2">
+ <colgroup width="20"></colgroup>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+ <table id="table5.3">
+ <col width="20">
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <!-- table with th element -->
+ <table id="table6">
+ <tr>
+ <th>Cell1</th><th>cell2</th>
+ </tr>
+ </table>
+
+ <!-- table with headers attribute -->
+ <table id="table6.2">
+ <tr>
+ <td headers="a">table6.2 cell</td>
+ </tr>
+ </table>
+
+ <!-- table with scope attribute -->
+ <table id="table6.2.2">
+ <tr>
+ <td scope="a">table6.2.2 cell</td>
+ </tr>
+ </table>
+
+ <!-- table with abbr attribute -->
+ <table id="table6.2.3">
+ <tr>
+ <td abbr="table6.2.3">table6.2.3 cell1</td>
+ </tr>
+ </table>
+
+ <!-- table with abbr element -->
+ <table id="table6.3">
+ <tr>
+ <td>table6.3 cell1</td>
+ <td><abbr>table6.3 cell2</abbr></td>
+ </tr>
+ </table>
+
+ <!-- table with abbr element having empty text node -->
+ <table id="table6.4">
+ <tr>
+ <td>
+ <abbr>abbr</abbr>
+ </td>
+ </tr>
+ </table>
+
+ <!-- table with abbr element and non-empty text node -->
+ <table id="table6.5">
+ <tr>
+ <td>
+ This is a really long text (<abbr>tiarlt</abbr>) inside layout table
+ </td>
+ </tr>
+ </table>
+
+ <!-- layout table with nested table -->
+ <table id="table9">
+ <tr>
+ <td><table><tr><td>Cell</td></tr></table></td>
+ </tr>
+ </table>
+
+ <!-- layout table with 1 column -->
+ <table id="table10">
+ <tr><td>Row1</td></tr>
+ <tr><td>Row2</td></tr>
+ </table>
+
+ <!-- layout table with 1 row and purposely many columns -->
+ <table id="table11">
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td><td>Col4</td><td>Col5</td></tr>
+ </table>
+
+ <!-- table with 5 columns -->
+ <table id="table12">
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td><td>Col4</td><td>Col5</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td><td>Col4</td><td>Col5</td></tr>
+ </table>
+
+ <!-- table with a bordered cell -->
+ <table id="table13" border="1" width="100%" bordercolor="#0000FF">
+ <tr>
+ <td bordercolor="#000000"> </td>
+ <td bordercolor="#000000"> </td>
+ <td bordercolor="#000000"> </td>
+ </tr>
+ <tr>
+ <td bordercolor="#000000"> </td>
+ <td bordercolor="#000000"> </td>
+ <td bordercolor="#000000"> </td>
+ </tr>
+ </table>
+
+ <!-- table with alternating row background colors -->
+ <table id="table14" width="100%">
+ <tr style="background-color: #0000FF;">
+ <td> </td>
+ <td> </td>
+ <td> </td>
+ </tr>
+ <tr style="background-color: #00FF00;">
+ <td> </td>
+ <td> </td>
+ <td> </td>
+ </tr>
+ </table>
+
+ <!-- table with 3 columns and 21 rows -->
+ <table id="table15" border="0">
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ </table>
+
+ <!-- layout table that has a 100% width -->
+ <table id="table16" width="100%">
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ </table>
+
+ <!-- layout table that has a 95% width in pixels -->
+ <table id="table17" width="98%">
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ <tr><td>Col1</td><td>Col2</td><td>Col3</td></tr>
+ </table>
+
+ <!-- layout table with less than 10 columns -->
+ <table id="table18">
+ <tr>
+ <td>Marco</td>
+ <td>Test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>David</td>
+ <td>Another test</td>
+ <td>June 12</td>
+ </tr>
+ <tr>
+ <td>Alex</td>
+ <td>Third test</td>
+ <td>June 12</td>
+ </tr>
+ </table>
+
+ <!-- layout table with embedded iframe -->
+ <table id="table19">
+ <tr><td><iframe id="frame"></iframe></td><td> </td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ <tr><td> </td><td> </td><td> </td></tr>
+ </table>
+
+ <!-- tree grid, no layout table -->
+ <table id="table20" role="treegrid">
+ <tr role="treeitem"><td>Cell1</td><td>Cell2</td></tr>
+ </table>
+
+ <!-- layout table with nested data table containing data table elements -->
+ <table id="table21">
+ <tr>
+ <td>
+ <table>
+ <caption>table</caption>
+ <tr><td>Cell</td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table id="table21.2">
+ <tr>
+ <td>
+ <table>
+ <colgroup width="20"></colgroup>
+ <tr><th>Cell</th></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table id="table21.3">
+ <tr>
+ <td>
+ <table>
+ <col width="20"></col>
+ <tr><th>Cell</th></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table id="table21.4">
+ <tr>
+ <td>
+ <table>
+ <tr><th>Cell</th></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table id="table21.5">
+ <tr>
+ <td>
+ <table>
+ <thead>
+ <tr><td>Cell</td></tr>
+ </thead>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <table id="table21.6">
+ <tr>
+ <td>
+ <table>
+ <tfoot>
+ <tr><td>Cell</td></tr>
+ </tfoot>
+ </table>
+ </td>
+ </tr>
+ </table>
+
+ <!-- layout table with datatable="0" and tfoot element-->
+ <table id="table22" datatable="0">
+ <tfoot>
+ <tr>
+ <td>Cell1</td><td>cell2</td>
+ </tr>
+ </tfoot>
+ </table>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_mtable.html b/accessible/tests/mochitest/table/test_mtable.html
new file mode 100644
index 000000000..37ea8af50
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_mtable.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>MathML table tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // 'Simple' table
+ var idxes = [
+ [0, 1],
+ [2, 3]
+ ];
+ testTableIndexes("simple", idxes);
+ var cellsArray = [
+ [kDataCell, kDataCell],
+ [kDataCell, kDataCell]
+ ];
+ var rowsArray = [ROLE_MATHML_TABLE_ROW, ROLE_MATHML_TABLE_ROW];
+ testTableStruct("simple", cellsArray, kNoColumnHeader,
+ "", "", kMathTable, rowsArray);
+
+ // 'Complex' table
+ idxes = [
+ [0, 0, 0],
+ [1, 1, 2],
+ [1, 1, 3]
+ ];
+ testTableIndexes("complex", idxes);
+ cellsArray = [
+ [kDataCell, kColSpanned, kColSpanned],
+ [kDataCell, kColSpanned, kDataCell],
+ [kRowSpanned, kSpanned, kDataCell],
+ ];
+ rowsArray = [
+ ROLE_MATHML_TABLE_ROW,
+ ROLE_MATHML_TABLE_ROW,
+ ROLE_MATHML_TABLE_ROW
+ ];
+ testTableStruct("complex", cellsArray, kNoColumnHeader,
+ "", "", kMathTable, rowsArray);
+
+ // 'Simple' table with mlabeledtr
+ // At the moment we do not implement mlabeledtr but just hide the label
+ // with display: none. Thus we just test the role for now. See bug 689641.
+ var idxes = [[0]];
+ testTableIndexes("simple_label", idxes);
+ var cellsArray = [[kDataCell]];
+ rowsArray = [ROLE_MATHML_LABELED_ROW];
+ testTableStruct("simple_label", cellsArray, kNoColumnHeader,
+ "", "", kMathTable, rowsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <math>
+ <mtable id="simple">
+ <mtr>
+ <mtd>
+ <mn>1</mn>
+ </mtd>
+ <mtd>
+ <mn>0</mn>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mn>0</mn>
+ </mtd>
+ <mtd>
+ <mn>1</mn>
+ </mtd>
+ </mtr>
+ </mtable>
+
+ <mtable id="complex">
+ <mtr>
+ <mtd columnspan="3">
+ <mtext>1 x 3</mtext>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd rowspan="2" columnspan="2">
+ <mtext>2 x 2</mtext>
+ </mtd>
+ <mtd>
+ <mtext>1 x 1</mtext>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mtext>1 x 1</mtext>
+ </mtd>
+ </mtr>
+ </mtable>
+
+ <mtable id="simple_label">
+ <mlabeledtr>
+ <mtd><mtext>1</mtext></mtd>
+ <mtd><mtext>label</mtext></mtd>
+ </mlabeledtr>
+ </mtable>
+ </math>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_sels_ariagrid.html b/accessible/tests/mochitest/table/test_sels_ariagrid.html
new file mode 100644
index 000000000..e8b082471
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_sels_ariagrid.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=410052
+-->
+<head>
+ <title>nsIAccesible selection methods testing for ARIA grid</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid
+ var cellsArray =
+ [
+ [ true, true, false, true],
+ [ true, false, true, true],
+ [ true, false, false, true],
+ [ true, true, true, true],
+ [ true, true, true, true]
+ ];
+
+ testTableSelection("table", cellsArray);
+ testUnselectTableColumn("table", 3, cellsArray);
+ testUnselectTableRow("table", 3, cellsArray);
+ testSelectTableColumn("table", 0, cellsArray);
+ testSelectTableRow("table", 0, cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // a bit crazy ARIA grid
+ cellsArray =
+ [
+ [ false, false],
+ [ false, false]
+ ];
+
+ testTableSelection("grid2", cellsArray);
+ testSelectTableColumn("grid2", 0, cellsArray);
+ testSelectTableRow("grid2", 0, cellsArray);
+ testUnselectTableColumn("grid2", 0, cellsArray);
+ testUnselectTableRow("grid2", 0, cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid (column and row headers)
+
+ cellsArray =
+ [
+ [ undefined, true, false],
+ [ undefined, true, false]
+ ];
+
+ testTableSelection("grid3", cellsArray);
+ testSelectTableColumn("grid3", 0, cellsArray);
+ testSelectTableRow("grid3", 0, cellsArray);
+ testUnselectTableColumn("grid3", 0, cellsArray);
+ testUnselectTableRow("grid3", 0, cellsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="implement nsIAccessibleTable selection methods for ARIA grids"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Bug 410052</a>
+ <a target="_blank"
+ title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Bug 513848</a>
+ <a target="_blank"
+ title="ARIA columnheader/rowheader shouldn't be selectable by default"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=888247">Bug 888247</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="grid" id="table">
+ <div role="row">
+ <span role="gridcell" aria-selected="true">cell1</span>
+ <span role="gridcell" aria-selected="true">cell2</span>
+ <span role="gridcell">cell3</span>
+ <span role="gridcell" aria-selected="true">cell4</span>
+ </div>
+ <div role="row">
+ <span role="gridcell" aria-selected="true">cell5</span>
+ <span role="gridcell">cell6</span>
+ <span role="gridcell" aria-selected="true">cell7</span>
+ <span role="gridcell" aria-selected="true">cell8</span>
+ </div>
+ <div role="row">
+ <span role="gridcell" aria-selected="true">cell9</span>
+ <span role="gridcell">cell10</span>
+ <span role="gridcell">cell11</span>
+ <span role="gridcell" aria-selected="true">cell12</span>
+ </div>
+ <div role="row" aria-selected="true">
+ <span role="gridcell">cell13</span>
+ <span role="gridcell">cell14</span>
+ <span role="gridcell">cell15</span>
+ <span role="gridcell">cell16</span>
+ </div>
+ <div role="row">
+ <span role="gridcell" aria-selected="true">cell17</span>
+ <span role="gridcell" aria-selected="true">cell18</span>
+ <span role="gridcell" aria-selected="true">cell19</span>
+ <span role="gridcell" aria-selected="true">cell20</span>
+ </div>
+ </div>
+
+ <div role="grid" id="grid2">
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="columnheader" aria-selected="false">header1</td>
+ <td role="columnheader" aria-selected="false">header2</td>
+ </tr>
+ </table>
+ </div>
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="gridcell">cell1</td>
+ <td role="gridcell" tabindex="-1">cell2</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <div role="grid" id="grid3">
+ <div role="row">
+ <div role="columnheader" id="colheader_default">col header1</div>
+ <div role="columnheader" id="colheader_selected" aria-selected="true">col header2</div>
+ <div role="columnheader" id="colheader_notselected" aria-selected="false">col header3</div>
+ </div>
+ <div role="row">
+ <div role="rowheader" id="rowheader_default">row header1</div>
+ <div role="rowheader" id="rowheader_selected" aria-selected="true">row header2</div>
+ <div role="rowheader" id="rowheader_notselected" aria-selected="false">row header3</div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_sels_listbox.xul b/accessible/tests/mochitest/table/test_sels_listbox.xul
new file mode 100644
index 000000000..83697e8d0
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_sels_listbox.xul
@@ -0,0 +1,247 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="nsIAccessibleTable selection methods on xul:listbox test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ var id = "listbox3";
+ var acc = getAccessible(id, [nsIAccessibleTable]);
+
+ var rowCount = acc.rows;
+ var colsCount = acc.columns;
+
+ // columns selection
+ testColumnSelection(id, acc, colsCount, 0, null);
+ acc.selectColumn(0);
+ testColumnSelection(id, acc, colsCount, 0, null);
+
+ // rows selection
+ testRowSelection(id, acc, rowCount, 0, null);
+ acc.selectRow(0);
+ testRowSelection(id, acc, rowCount, 1, [0]);
+ acc.selectRow(1);
+ testRowSelection(id, acc, rowCount, 1, [1]);
+ acc.unselectRow(1);
+ testRowSelection(id, acc, rowCount, 0, null);
+
+ // cells selection
+ testCellSelection(id, acc, rowCount, colsCount, 0, null);
+ acc.selectRow(2);
+ testCellSelection(id, acc, rowCount, colsCount, 3, [6, 7, 8]);
+ acc.unselectRow(2);
+ testCellSelection(id, acc, rowCount, colsCount, 0, null);
+
+ SimpleTest.finish();
+ }
+
+ /**
+ * Helper function to test isColumnSelected(), selectedColumnCount and
+ * getSelectedColumn() methods.
+ */
+ function testColumnSelection(aId, aAcc, aCount, aSelCount, aSelIndexesArray)
+ {
+ // isColumnSelected
+ for (var col = 0; col < aCount; col++) {
+ if (aSelIndexesArray && aSelIndexesArray.indexOf(col) != -1) {
+ is(aAcc.isColumnSelected(col), true,
+ aId + ": column " + col + " should be selected");
+ } else {
+ is(aAcc.isColumnSelected(col), false,
+ aId + ": column " + col + " shouldn't be selected");
+ }
+ }
+
+ // selectedColumnCount
+ is(aAcc.selectedColumnCount, aSelCount,
+ aId + ": wrong number of selected columns");
+
+ // getSelectedColumns
+ var selColsCount = {}, selCols = {};
+ aAcc.getSelectedColumnIndices(selColsCount, selCols);
+
+ is(selColsCount.value, aSelCount,
+ aId + ": wrong number of selected columns");
+
+ if (!aSelIndexesArray) {
+ is(selCols.value, undefined,
+ aId + ": no columns should be selected");
+ } else {
+ for (var i = 0; i < selCols.length; i++) {
+ is(selCols[i], aSelIndexesArray[i],
+ aId + ": wrong selected column index " + i);
+ }
+ }
+ }
+
+ /**
+ * Helper function to test isRowSelected(), selectedRowCount() and
+ * getSelectedRow() methods.
+ */
+ function testRowSelection(aId, aAcc, aCount, aSelCount, aSelIndexesArray)
+ {
+ // isRowSelected
+ for (var row = 0; row < aCount; row++) {
+ if (aSelIndexesArray && aSelIndexesArray.indexOf(row) != -1) {
+ is(aAcc.isRowSelected(row), true,
+ aId + ": row " + row + " should be selected");
+ } else {
+ is(aAcc.isRowSelected(row), false,
+ aId + ": row " + row + " shouldn't be selected");
+ }
+ }
+
+ // selectedRowCount
+ is(aAcc.selectedRowCount, aSelCount,
+ aId + ": wrong number of selected rows");
+
+ // getSelectedRows
+ var selColsCount = {}, selCols = {};
+ aAcc.getSelectedRowIndices(selColsCount, selCols);
+
+ is(selColsCount.value, aSelCount,
+ aId + ": wrong number of selected rows");
+
+ if (!aSelIndexesArray) {
+ is(selCols.value, undefined,
+ aId + ": no row should be selected");
+ } else {
+ for (var i = 0; i < selCols.length; i++) {
+ is(selCols[i], aSelIndexesArray[i],
+ aId + ": wrong selected row index " + i);
+ }
+ }
+ }
+
+ /**
+ * Helper function to test isCellSelected(), selectedCellCount() and
+ * getSelectedCells() methods.
+ */
+ function testCellSelection(aId, aAcc, aRowCount, aColCount,
+ aSelCount, aSelIndexesArray)
+ {
+ // isCellSelected
+ for (var row = 0; row < aRowCount; row++) {
+ for (var col = 0; col < aColCount; col++) {
+ var index = aAcc.getIndexAt(row, col);
+ if (aSelIndexesArray && aSelIndexesArray.indexOf(index) != -1) {
+ is(aAcc.isCellSelected(row, col), true,
+ aId + ": cell (" + row + ", " + col + ") should be selected");
+ } else {
+ is(aAcc.isCellSelected(row, col), false,
+ aId + ": cell (" + row + ", " + col + ") shouldn't be selected");
+ }
+ }
+ }
+
+ // selectedCellCount
+ is(aAcc.selectedCellCount, aSelCount,
+ aId + ": wrong number of selected cells");
+
+ // getSelectedCells
+ var selColsCount = {}, selCols = {};
+ aAcc.getSelectedCellIndices(selColsCount, selCols);
+
+ is(selColsCount.value, aSelCount,
+ aId + ": wrong number of selected cells");
+
+ if (!aSelIndexesArray) {
+ is(selCols.value, undefined,
+ aId + ": no cells should be selected");
+ } else {
+ for (var i = 0; i < selCols.length; i++) {
+ is(selCols[i], aSelIndexesArray[i],
+ aId + ": wrong selected cell index " + i);
+ }
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=418371"
+ title="implement the rest of methods of nsIAccessibleTable on xul:listbox">
+ Mozilla Bug 418371
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <label control="listbox2" value="multicolumn listbox: "/>
+ <listbox id="listbox2">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ </listbox>
+
+ <label control="listbox3" value="multicolumn listbox with header"/>
+ <listbox id="listbox3">
+ <listhead>
+ <listheader label="header1"/>
+ <listheader label="header2"/>
+ <listheader label="header3"/>
+ </listhead>
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell label="cell0"/>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell3"/>
+ <listcell label="cell4"/>
+ <listcell label="cell5"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell6"/>
+ <listcell label="cell7"/>
+ <listcell label="cell8"/>
+ </listitem>
+ </listbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_sels_table.html b/accessible/tests/mochitest/table/test_sels_table.html
new file mode 100644
index 000000000..c0b37384e
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_sels_table.html
@@ -0,0 +1,180 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>nsIAccesible selection methods testing for HTML table</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="text/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // table
+
+ var cellsArray =
+ [
+ [false, false, false, kColSpanned, false, false, false, false],
+ [false, false, false, false, false, false, false, kRowSpanned],
+ [false, false, kColSpanned, false, false, false, false, kRowSpanned],
+ [false, kRowSpanned, kSpanned, false, false, kRowSpanned, false, kRowSpanned]
+ ];
+
+ testTableSelection("table", cellsArray);
+
+ var rowCount = 4;
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++)
+ testSelectTableRow("table", rowIdx, cellsArray);
+
+ for (var rowIdx = 0; rowIdx < rowCount; rowIdx++) {
+ testSelectTableRow("table", rowIdx, cellsArray);
+ testUnselectTableRow("table", rowIdx, cellsArray);
+ }
+
+ var columsCount = 8;
+ for (var colIdx = 0; colIdx < columsCount; colIdx++)
+ testSelectTableColumn("table", colIdx, cellsArray);
+
+ for (var colIdx = 0; colIdx < columsCount; colIdx++) {
+ testSelectTableColumn("table", colIdx, cellsArray);
+ testUnselectTableColumn("table", colIdx, cellsArray);
+ }
+
+ var accTable = getAccessible("table", [nsIAccessibleTable]);
+ ok(!accTable.isProbablyForLayout(), "table is not for layout");
+
+ //////////////////////////////////////////////////////////////////////////
+ // table instane
+
+ cellsArray =
+ [
+ [false, false, false, -1, -1],
+ [false, false, false, -1, -1],
+ [false, false, kColSpanned, kColSpanned, -1],
+ [kRowSpanned, false, false, -1, -1],
+ [kRowSpanned, false, kRowSpanned, false, false]
+ ];
+
+ testTableSelection("tableinsane", cellsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+ </head>
+ <body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052"
+ title="Fix our nsHTMLAccessibleTable class so GetIndexAt and GetRowAtIndex and GetColumnAtIndex behave consistently">
+ Mozilla Bug 410052
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=501635"
+ title="nsHTMLTableAccessible::GetSelectedCells contains index duplicates for spanned rows or columns">
+ Mozilla Bug 501635
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=417929"
+ title="nsIAccessiblTable selectRows does not unselect previously selected rows">
+ Mozilla Bug 417929
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=501659"
+ title="HTML table's isRowSelected/isColumnSelected shouldn't fail if row or column has cell holes">
+ Mozilla Bug 501659
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Test Table -->
+ <br><br><b> Testing Table:</b><br><br>
+ <center>
+ <table cellpadding="2" cellspacing="2" border="1" width="50%" id="table">
+ <tbody>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="1" colspan="2"><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="4" colspan="1"><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td rowspan="2" colspan="2">c1</td>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="2" colspan="1"><br></td>
+ <td><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table border="1" id="tableinsane">
+ <thead>
+ <tr>
+ <th>col1</th>
+ <th>col2</th>
+ <th>col3</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>2</td>
+ <td>3</td>
+ </tr>
+ <tr>
+ <td rowspan="3">4</td>
+ <td colspan="4">5</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td rowspan="2">7</td>
+ </tr>
+ <tr>
+ <td>8</td>
+ <td>9</td>
+ <td>10</td>
+ </tr>
+ </tbody>
+ </table>
+
+ </center>
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_sels_tree.xul b/accessible/tests/mochitest/table/test_sels_tree.xul
new file mode 100644
index 000000000..ab2d1fc25
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_sels_tree.xul
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible Table selection tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../table.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ var cellsArray =
+ [
+ [false, false],
+ [false, false],
+ [false, false]
+ ];
+
+ testTableSelection("tree", cellsArray);
+ testSelectTableRow("tree", 0, cellsArray);
+ testUnselectTableRow("tree", 0, cellsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTableTreeView(3));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="scol" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_struct_ariagrid.html b/accessible/tests/mochitest/table/test_struct_ariagrid.html
new file mode 100644
index 000000000..9f771ef3b
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_struct_ariagrid.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Table accessible tree and table interface tests for ARIA grid</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // Pure ARIA grid
+ var cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell],
+ [kRowHeaderCell, kDataCell, kDataCell],
+ [kRowHeaderCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("table", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML table based ARIA grid
+ cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell],
+ [kDataCell, kDataCell, kDataCell],
+ [kDataCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("grid", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid with HTML table elements
+ cellsArray = [
+ [kColHeaderCell, kColHeaderCell],
+ [kDataCell, kDataCell]
+ ];
+
+ testTableStruct("grid2", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA grid of wrong markup
+ cellsArray = [ ];
+ testTableStruct("grid3", cellsArray);
+
+ cellsArray = [ [] ];
+ testTableStruct("grid4", cellsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="ARIA grid based on HTML table"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=491683">Mozilla Bug 491683</a>
+ <a target="_blank"
+ title="implement IAccessibleTable2"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">Mozilla Bug 512424</a>
+ <a target="_blank"
+ title="nsHTMLTableCellAccessible is used in dojo's crazy ARIA grid"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=513848">Mozilla Bug 513848</a>
+ <a target="_blank"
+ title="Crash [@ AccIterator::GetNext()]"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=675861">Mozilla Bug 675861</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Not usual markup to avoid text accessible between cell accessibles -->
+ <div id="table" role="grid">
+ <div role="row"><span
+ id="table_ch_1" role="columnheader">col_1</span><span
+ id="table_ch_2" role="columnheader">col_2</span><span
+ id="table_ch_3" role="columnheader">col_3</span></div>
+ <div role="row"><span
+ id="table_rh_1" role="rowheader">row_1</span><span
+ id="table_dc_1" role="gridcell">cell1</span><span
+ id="table_dc_2" role="gridcell">cell2</span></div>
+ <div role="row"><span
+ id="table_rh_2" role="rowheader">row_2</span><span
+ id="table_dc_3" role="gridcell">cell3</span><span
+ id="table_dc_4" role="gridcell">cell4</span></div>
+ </div>
+
+ <table role="grid" id="grid" border="1" cellpadding="10" cellspacing="0">
+ <thead>
+ <tr role="row">
+ <th role="columnheader">subject</td>
+ <th role="columnheader">sender</th>
+ <th role="columnheader">date</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr role="row">
+ <td role="gridcell" tabindex="0">about everything</td>
+ <td role="gridcell">president</td>
+ <td role="gridcell">today</td>
+ </tr>
+ <tr role="row">
+ <td role="gridcell">new bugs</td>
+ <td role="gridcell">mozilla team</td>
+ <td role="gridcell">today</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <!-- ARIA grid containing presentational HTML:table with HTML:td used as ARIA
+ grid cells (focusable and not focusable cells) -->
+ <div role="grid" id="grid2">
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="columnheader">header1</td>
+ <td role="columnheader">header2</td>
+ </tr>
+ </table>
+ </div>
+ <div role="row">
+ <table role="presentation">
+ <tr>
+ <td role="gridcell">cell1</td>
+ <td role="gridcell" tabindex="-1">cell2</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <!-- Wrong markup ARIA grid -->
+ <div role="grid" id="grid3"></div>
+ <div role="grid" id="grid4"><div role="row"></div></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_struct_ariatreegrid.html b/accessible/tests/mochitest/table/test_struct_ariatreegrid.html
new file mode 100644
index 000000000..7c97adb9b
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_struct_ariatreegrid.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Table accessible tree and table interface tests for ARIA tree grid</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // HTML based ARIA tree grid
+
+ var cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell],
+ [kDataCell, kDataCell, kDataCell],
+ [kDataCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("treegrid", cellsArray, kNoColumnHeader, "", "",
+ kTreeTable);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ title="ARIA treegrid role on HTML:table makes thead/tbody accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=491683">Mozilla Bug 516133</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table role="treegrid" id="treegrid"
+ border="1" cellpadding="10" cellspacing="0">
+ <thead>
+ <tr role="row">
+ <th role="columnheader">subject</td>
+ <th role="columnheader">sender</th>
+ <th role="columnheader">date</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr role="row">
+ <td role="gridcell">about everything</td>
+ <td role="gridcell">president</td>
+ <td role="gridcell">today</td>
+ </tr>
+ <tr role="row">
+ <td role="gridcell">new bugs</td>
+ <td role="gridcell">mozilla team</td>
+ <td role="gridcell">today</td>
+ </tr>
+ </tbody>
+ </table>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_struct_listbox.xul b/accessible/tests/mochitest/table/test_struct_listbox.xul
new file mode 100644
index 000000000..102269358
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_struct_listbox.xul
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Table accessible tree and table interface tests for XUL listboxes">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // Multicolumn listbox.
+
+ var cellsArray = [
+ [kDataCell, kDataCell],
+ [kDataCell, kDataCell]
+ ];
+
+ testTableStruct("listbox1", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Multicolumn listbox with header.
+
+ var cellsArray = [
+ [kDataCell, kDataCell, kDataCell],
+ [kDataCell, kDataCell, kDataCell],
+ [kDataCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("listbox2", cellsArray, kListboxColumnHeader);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <label control="listbox1" value="multicolumn listbox: "/>
+ <listbox id="listbox1">
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ </listbox>
+
+ <label control="listbox2" value="multicolumn listbox with header"/>
+ <listbox id="listbox2">
+ <listhead>
+ <listheader label="header1"/>
+ <listheader label="header2"/>
+ <listheader label="header3"/>
+ </listhead>
+ <listcols>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem>
+ <listcell label="cell0"/>
+ <listcell label="cell1"/>
+ <listcell label="cell2"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell3"/>
+ <listcell label="cell4"/>
+ <listcell label="cell5"/>
+ </listitem>
+ <listitem>
+ <listcell label="cell6"/>
+ <listcell label="cell7"/>
+ <listcell label="cell8"/>
+ </listitem>
+ </listbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_struct_table.html b/accessible/tests/mochitest/table/test_struct_table.html
new file mode 100644
index 000000000..98b954935
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_struct_table.html
@@ -0,0 +1,203 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Table accessible tree and table interface tests for HTML tables</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../table.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // column headers from thead and tfoot
+
+ cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColSpanned],
+ [kRowSpanned, kColHeaderCell, kColHeaderCell],
+ [kDataCell, kDataCell, kDataCell],
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell]
+ ];
+
+ testTableStruct("table1", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // row and column headers from thead and @scope
+
+ var cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell],
+ [kRowHeaderCell, kDataCell, kDataCell],
+ [kRowHeaderCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("table2", cellsArray);
+
+ //////////////////////////////////////////////////////////////////////////
+ // caption and @summary
+
+ cellsArray = [
+ [kColHeaderCell, kColHeaderCell, kColHeaderCell, kColHeaderCell],
+ [kRowHeaderCell, kDataCell, kDataCell, kDataCell],
+ [kRowHeaderCell, kDataCell, kDataCell, kDataCell]
+ ];
+
+ testTableStruct("table3", cellsArray, kNoColumnHeader,
+ "Test Table",
+ "this is a test table for nsIAccessibleTable");
+
+ //////////////////////////////////////////////////////////////////////////
+ // row and column spans
+
+ cellsArray = [
+ [kDataCell, kDataCell, kDataCell, kColSpanned, kDataCell, kDataCell, kDataCell, kDataCell],
+ [kDataCell, kDataCell, kDataCell, kDataCell, kDataCell, kDataCell, kDataCell, kRowSpanned],
+ [kDataCell, kDataCell, kColSpanned, kDataCell, kDataCell, kDataCell, kDataCell, kRowSpanned],
+ [kDataCell, kRowSpanned, kSpanned, kDataCell, kDataCell, kRowSpanned, kDataCell, kRowSpanned]
+ ];
+
+ testTableStruct("table4", cellsArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix our nsHTMLAccessibleTable class so GetIndexAt and GetRowAtIndex and GetColumnAtIndex behave consistently"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Mozilla Bug 410052</a>
+ <a target="_blank"
+ title="GetCellDataAt callers that expect an error if no cell is found are wrong"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=417912">Mozilla Bug 417912</a>
+ <a target="_blank"
+ title="create accessibles for HTML tr"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=493695">Mozilla Bug 493695</a>
+ <a target="_blank"
+ title="implement IAccessibleTable2"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">Mozilla Bug 512424</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table id="table1">
+ <thead>
+ <tr>
+ <th rowspan="2">col1</th><th colspan="2">col2</th>
+ </tr>
+ <tr>
+ <th>col2sub1</th><th>col2sub2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>cell1</td><td>cell2</td><td>cell3</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th>col1</th><th>col2</th><th>col3</th>
+ </tr>
+ </tfoot>
+ </table>
+
+ <table id="table2">
+ <thead>
+ <tr>
+ <th id="table1_0">col1</th>
+ <th id="table1_1">col2</th>
+ <td id="table1_2" scope="col">col3</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="table1_3">row1</th>
+ <td id="table1_4">cell1</td>
+ <td id="table1_5">cell2</td>
+ </tr>
+ <tr>
+ <td id="table1_6" scope="row">row2</td>
+ <td id="table1_7">cell3</td>
+ <td id="table1_8">cell4</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="table3" border="1"
+ summary="this is a test table for nsIAccessibleTable">
+ <caption>Test Table</caption>
+ <thead>
+ <tr>
+ <th></th>
+ <th>columnHeader_1</th>
+ <th id ="col2a">columnHeader_2</th>
+ <th>columnHeader_3</th>
+ </tr>
+ </thead>
+ <tr>
+ <th id="row2a">rowHeader_1</th>
+ <td id="row2b">row1_column1</td>
+ <td id ="col2b">row1_column2</td>
+ <td id="row2c">row1_column3</td>
+ </tr>
+ <tr>
+ <th>rowHeader_2</th>
+ <td>row2_column1</td>
+ <td id ="col2c">row2_column2</td>
+ <td>row2_column3</td>
+ </tr>
+ </table>
+
+ <table id="table4" cellpadding="2" cellspacing="2" border="1" width="50%">
+ <tbody>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="1" colspan="2"><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="4" colspan="1"><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td rowspan="2" colspan="2">c1</td>
+ <td><br></td>
+ <td><br></td>
+ <td rowspan="2" colspan="1"><br></td>
+ <td><br></td>
+ </tr>
+ <tr>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ <td><br></td>
+ </tr>
+ </tbody>
+ </table>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_struct_tree.xul b/accessible/tests/mochitest/table/test_struct_tree.xul
new file mode 100644
index 000000000..2037bf714
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_struct_tree.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Table accessible tree and table interface tests for XUL trees">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../table.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ var cellsArray = [
+ [kDataCell, kDataCell],
+ [kDataCell, kDataCell],
+ [kDataCell, kDataCell]
+ ];
+
+ testTableStruct("table", cellsArray, kTreeColumnHeader);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "table", new nsTableTreeView(3));
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424"
+ title="implement IAccessibleTable2">
+ Mozilla Bug 512424
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="table" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" label="column"/>
+ <treecol id="scol" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/table/test_table_1.html b/accessible/tests/mochitest/table/test_table_1.html
new file mode 100644
index 000000000..ab3937770
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_table_1.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+
+ <script type="application/javascript">
+
+function doTest()
+{
+ var accTable = getAccessible("table", [nsIAccessibleTable]);
+
+ var s = window.getSelection();
+ if (s.rangeCount > 0)
+ s.removeAllRanges();
+
+ var cell = getNode("col2b");
+ var range = document.createRange();
+ range.selectNode(cell);
+ s.addRange(range);
+
+ is(accTable.selectedCellCount, 1, "only one cell selected");
+ cell = getNode("col2a");
+ range = document.createRange();
+ range.selectNode(cell);
+ s.addRange(range);
+ cell = getNode("col2c");
+ range = document.createRange();
+ range.selectNode(cell);
+ s.addRange(range);
+ is(accTable.selectedColumnCount, 1, "only one column selected");
+
+ cell = getNode("row2a");
+ range = document.createRange();
+ range.selectNode(cell);
+ s.addRange(range);
+ cell = getNode("row2b");
+ range = document.createRange();
+ range.selectNode(cell);
+ s.addRange(range);
+ range = document.createRange();
+ cell = getNode("row2c");
+ range.selectNode(cell);
+ s.addRange(range);
+
+ is(accTable.selectedRowCount, 1, "no cells selected");
+
+ var columnDescription = accTable.getColumnDescription(1);
+ var rowDescription = accTable.getRowDescription(1);
+
+ SimpleTest.finish();
+}
+SimpleTest.waitForExplicitFinish();
+addA11yLoadEvent(doTest);
+ </script>
+ </head>
+ <body >
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=410052">Mozilla Bug 410052</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=760878"
+ title="decomtaminate Get Row / Column Description() on accessible tables">
+ Mozilla Bug 760878
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Test Table -->
+ <br><br><b> Testing Table:</b><br><br>
+ <center>
+ <table id="table" border="1"
+ summary="this is a test table for nsIAccessibleTable" >
+ <caption>Test Table</caption>
+ <thead>
+ <tr>
+ <th></th>
+ <th>columnHeader_1</th>
+ <th id ="col2a">columnHeader_2</th>
+ <th>columnHeader_3</th>
+ </tr>
+ </thead>
+ <tr>
+ <th id="row2a">rowHeader_1</th>
+ <td id="row2b">row1_column1</td>
+ <td id ="col2b">row1_column2</td>
+ <td id="row2c">row1_column3</td>
+ </tr>
+ <tr>
+ <th>rowHeader_2</th>
+ <td>row2_column1</td>
+ <td id ="col2c">row2_column2</td>
+ <td>row2_column3</td>
+ </tr>
+ </table>
+ </center>
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/table/test_table_2.html b/accessible/tests/mochitest/table/test_table_2.html
new file mode 100644
index 000000000..38477c2fa
--- /dev/null
+++ b/accessible/tests/mochitest/table/test_table_2.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="text/javascript">
+
+function doTest()
+{
+ // Test table with role=alert.
+ var tableInterfaceExposed = true;
+ var accTable3 = getAccessible("table3", [nsIAccessibleTable], null, DONOTFAIL_IF_NO_INTERFACE);
+ if (!accTable3)
+ tableInterfaceExposed = false;
+ ok(tableInterfaceExposed, "table interface is not exposed");
+
+ if (tableInterfaceExposed) {
+ testRole(accTable3, ROLE_ALERT);
+
+ is(accTable3.getCellAt(0,0).firstChild.name, "cell0", "wrong cell");
+ is(accTable3.getCellAt(0,1).firstChild.name, "cell1", "wrong cell");
+ }
+
+ // Test table with role=log and aria property in tr. We create accessible for
+ // tr in this case.
+ tableInterfaceExposed = true;
+ var accTable4 = getAccessible("table4", [nsIAccessibleTable], null, DONOTFAIL_IF_NO_INTERFACE);
+ if (!accTable4)
+ tableInterfaceExposed = false;
+ ok(tableInterfaceExposed, "table interface is not exposed");
+
+ if (tableInterfaceExposed) {
+ accNotCreated = (!isAccessible("tr"));
+ ok(!accNotCreated, "missed tr accessible");
+
+ testRole(accTable4, ROLE_TABLE);
+
+ is(accTable4.getCellAt(0,0).firstChild.name, "cell0", "wrong cell");
+ is(accTable4.getCellAt(0,1).firstChild.name, "cell1", "wrong cell");
+ is(accTable4.getCellAt(1,0).firstChild.name, "cell2", "wrong cell");
+ is(accTable4.getCellAt(1,1).firstChild.name, "cell3", "wrong cell");
+ }
+
+ SimpleTest.finish();
+}
+SimpleTest.waitForExplicitFinish();
+addA11yLoadEvent(doTest);
+ </script>
+ </head>
+
+ <body >
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=419811">Mozilla Bug 419811</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Test Table -->
+ <br><br><b> Testing Table:</b><br><br>
+ <center>
+ <table id="table3" border="1" role="alert">
+ <tr>
+ <td>cell0</td>
+ <td>cell1</td>
+ </tr>
+ </table>
+
+ <table id="table4" border="1" role="log">
+ <tr aria-live="polite" id="tr">
+ <td>cell0</td>
+ <td>cell1</td>
+ </tr>
+ <tr>
+ <td>cell2</td>
+ <td>cell3</td>
+ </tr>
+ </table>
+
+ </center>
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/test_OuterDocAccessible.html b/accessible/tests/mochitest/test_OuterDocAccessible.html
new file mode 100644
index 000000000..c2efa18fe
--- /dev/null
+++ b/accessible/tests/mochitest/test_OuterDocAccessible.html
@@ -0,0 +1,89 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=441519
+-->
+<head>
+ <title>nsOuterDocAccessible chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="states.js"></script>
+ <script type="application/javascript"
+ src="role.js"></script>
+
+ <script type="application/javascript">
+ // needed error return value
+ const ns_error_invalid_arg = Components.results.NS_ERROR_INVALID_ARG;
+
+ function doTest()
+ {
+ // Get accessible for body tag.
+ var docAcc = getAccessible(document);
+
+ if (docAcc) {
+ var outerDocAcc = getAccessible(docAcc.parent);
+
+ if (outerDocAcc) {
+ testRole(outerDocAcc, ROLE_INTERNAL_FRAME);
+
+ // check if it is focusable.
+ testStates(outerDocAcc, STATE_FOCUSABLE, 0);
+
+ // see bug 428954: No name wanted for internal frame
+ is(outerDocAcc.name, null, "Wrong name for internal frame!");
+
+ // see bug 440770, no actions wanted on outer doc
+ is(outerDocAcc.actionCount, 0,
+ "Wrong number of actions for internal frame!");
+ var actionTempStr; // not really used, just needs to receive a value
+ try {
+ actionTempStr = outerDocAcc.getActionName(0);
+ do_throw("No exception thrown for actionName!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for actionName call!");
+ }
+
+ try {
+ actionTempStr = outerDocAcc.getActionDescription(0);
+ do_throw("No exception thrown for actionDescription!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for actionDescription call!");
+ }
+
+ try {
+ outerDocAcc.doAction(0);
+ do_throw("No exception thrown for doAction!");
+ } catch(e) {
+ ok(e.result, ns_error_invalid_arg,
+ "Wrong return value for doAction call!");
+ }
+ }
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441519"
+ title="nsOuterDocAccessible chrome tests">
+ Mozilla Bug 441519
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/test_aria_token_attrs.html b/accessible/tests/mochitest/test_aria_token_attrs.html
new file mode 100644
index 000000000..882099026
--- /dev/null
+++ b/accessible/tests/mochitest/test_aria_token_attrs.html
@@ -0,0 +1,329 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=452388
+-->
+<head>
+ <title>An NMTOKEN based ARIA property is undefined if the ARIA attribute is not present, or is set to "" or "undefined"</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="role.js"></script>
+ <script type="application/javascript"
+ src="states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // test aria-pressed state mapping to roles PUSHBUTTON vs TOGGLEBUTTON
+ testRole("button_pressed_true", ROLE_TOGGLE_BUTTON);
+ testRole("button_pressed_false", ROLE_TOGGLE_BUTTON);
+ testRole("button_pressed_empty", ROLE_PUSHBUTTON);
+ testRole("button_pressed_undefined", ROLE_PUSHBUTTON);
+ testRole("button_pressed_absent", ROLE_PUSHBUTTON);
+
+ // test button aria-pressed states
+ testStates("button_pressed_true", STATE_PRESSED, 0, STATE_CHECKABLE);
+ testStates("button_pressed_false", 0, 0, STATE_CHECKABLE | STATE_PRESSED);
+ testStates("button_pressed_empty", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
+ testStates("button_pressed_undefined", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
+ testStates("button_pressed_absent", 0, 0, STATE_PRESSED | STATE_CHECKABLE);
+
+ // test (checkbox) checkable and checked states
+ testStates("checkbox_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("checkbox_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("checkbox_checked_empty", STATE_CHECKABLE , 0, STATE_CHECKED);
+ testStates("checkbox_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("checkbox_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
+
+ // test native checkbox checked state and aria-checked state (if conflict, native wins)
+ testStates("native_checkbox_nativechecked_ariatrue", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("native_checkbox_nativechecked_ariafalse", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("native_checkbox_nativechecked_ariaempty", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("native_checkbox_nativechecked_ariaundefined", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("native_checkbox_nativechecked_ariaabsent", (STATE_CHECKABLE | STATE_CHECKED));
+
+ testStates("native_checkbox_nativeunchecked_ariatrue", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("native_checkbox_nativeunchecked_ariafalse", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("native_checkbox_nativeunchecked_ariaempty", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("native_checkbox_nativeunchecked_ariaundefined", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("native_checkbox_nativeunchecked_ariaabsent", STATE_CHECKABLE, 0, STATE_CHECKED);
+
+ // test (checkbox) readonly states
+ testStates("checkbox_readonly_true", STATE_READONLY);
+ testStates("checkbox_readonly_false", 0, 0, STATE_READONLY);
+ testStates("checkbox_readonly_empty", 0, 0, STATE_READONLY);
+ testStates("checkbox_readonly_undefined", 0, 0, STATE_READONLY);
+ testStates("checkbox_readonly_absent", 0, 0, STATE_READONLY);
+
+ // test (checkbox) required states
+ testStates("checkbox_required_true", STATE_REQUIRED);
+ testStates("checkbox_required_false", 0, 0, STATE_REQUIRED);
+ testStates("checkbox_required_empty", 0, 0, STATE_REQUIRED);
+ testStates("checkbox_required_undefined", 0, 0, STATE_REQUIRED);
+ testStates("checkbox_required_absent", 0, 0, STATE_REQUIRED);
+
+ // test (checkbox) invalid states
+ testStates("checkbox_invalid_true", STATE_INVALID);
+ testStates("checkbox_invalid_false", 0, 0, STATE_INVALID);
+ testStates("checkbox_invalid_empty", 0, 0, STATE_INVALID);
+ testStates("checkbox_invalid_undefined", 0, 0, STATE_INVALID);
+ testStates("checkbox_invalid_absent", 0, 0, STATE_INVALID);
+
+ // test (checkbox) disabled states
+ testStates("checkbox_disabled_true", STATE_UNAVAILABLE);
+ testStates("checkbox_disabled_false", 0, 0, STATE_UNAVAILABLE);
+ testStates("checkbox_disabled_empty", 0, 0, STATE_UNAVAILABLE);
+ testStates("checkbox_disabled_undefined", 0, 0, STATE_UNAVAILABLE);
+ testStates("checkbox_disabled_absent", 0, 0, STATE_UNAVAILABLE);
+
+ // test (listbox) multiselectable states
+ testStates("listbox_multiselectable_true", STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+ testStates("listbox_multiselectable_false", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+ testStates("listbox_multiselectable_empty", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+ testStates("listbox_multiselectable_undefined", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+ testStates("listbox_multiselectable_absent", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE);
+
+ // test (option) checkable and checked states
+ testStates("option_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("option_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("option_checked_empty", 0 , 0, STATE_CHECKABLE | STATE_CHECKED);
+ testStates("option_checked_undefined", 0, 0, STATE_CHECKABLE | STATE_CHECKED);
+ testStates("option_checked_absent", 0, 0, STATE_CHECKABLE | STATE_CHECKED);
+
+ // test (menuitem) checkable and checked states
+ testStates("menuitem_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("menuitem_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("menuitem_checked_empty", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("menuitem_checked_undefined", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("menuitem_checked_absent", 0, 0, (STATE_CHECKABLE | STATE_CHECKED));
+
+ // test (menuitemradio) checkable and checked states
+ testStates("menuitemradio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("menuitemradio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("menuitemradio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("menuitemradio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("menuitemradio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
+
+ // test (radio) checkable and checked states
+ testStates("radio_checked_true", (STATE_CHECKABLE | STATE_CHECKED));
+ testStates("radio_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("radio_checked_empty", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("radio_checked_undefined", STATE_CHECKABLE, 0, STATE_CHECKED);
+ testStates("radio_checked_absent", STATE_CHECKABLE, 0, STATE_CHECKED);
+
+ // test (textbox) multiline states
+ testStates("textbox_multiline_true", 0, EXT_STATE_MULTI_LINE);
+ testStates("textbox_multiline_false", 0, EXT_STATE_SINGLE_LINE);
+ testStates("textbox_multiline_empty", 0, EXT_STATE_SINGLE_LINE);
+ testStates("textbox_multiline_undefined", 0, EXT_STATE_SINGLE_LINE);
+ testStates("textbox_multiline_absent", 0, EXT_STATE_SINGLE_LINE);
+
+ // test (textbox) readonly states
+ testStates("textbox_readonly_true", STATE_READONLY);
+ testStates("textbox_readonly_false", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("textbox_readonly_empty", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("textbox_readonly_undefined", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+ testStates("textbox_readonly_absent", 0, EXT_STATE_EDITABLE, STATE_READONLY);
+
+ // test native textbox readonly state and aria-readonly state (if conflict, native wins)
+ testStates("native_textbox_nativereadonly_ariatrue", STATE_READONLY);
+ testStates("native_textbox_nativereadonly_ariafalse", STATE_READONLY);
+ testStates("native_textbox_nativereadonly_ariaempty", STATE_READONLY);
+ testStates("native_textbox_nativereadonly_ariaundefined", STATE_READONLY);
+ testStates("native_textbox_nativereadonly_ariaabsent", STATE_READONLY);
+
+ testStates("native_textbox_nativeeditable_ariatrue", 0, 0, STATE_READONLY);
+ testStates("native_textbox_nativeeditable_ariafalse", 0, 0, STATE_READONLY);
+ testStates("native_textbox_nativeeditable_ariaempty", 0, 0, STATE_READONLY);
+ testStates("native_textbox_nativeeditable_ariaundefined", 0, 0, STATE_READONLY);
+ testStates("native_textbox_nativeeditable_ariaabsent", 0, 0, STATE_READONLY);
+
+ // test (treeitem) selectable and selected states
+ testStates("treeitem_selected_true", (STATE_SELECTABLE | STATE_SELECTED));
+ testStates("treeitem_selected_false", STATE_SELECTABLE, 0, STATE_SELECTED);
+ testStates("treeitem_selected_empty", STATE_SELECTABLE, 0, STATE_SELECTED);
+ testStates("treeitem_selected_undefined", STATE_SELECTABLE, 0, STATE_SELECTED);
+ testStates("treeitem_selected_absent", STATE_SELECTABLE, 0, STATE_SELECTED);
+
+ // test (treeitem) haspopup states
+ testStates("treeitem_haspopup_true", STATE_HASPOPUP);
+ testStates("treeitem_haspopup_false", 0, 0, STATE_HASPOPUP);
+ testStates("treeitem_haspopup_empty", 0, 0, STATE_HASPOPUP);
+ testStates("treeitem_haspopup_undefined", 0, 0, STATE_HASPOPUP);
+ testStates("treeitem_haspopup_absent", 0, 0, STATE_HASPOPUP);
+
+ // test (treeitem) expandable and expanded/collapsed states
+ testStates("treeitem_expanded_true", STATE_EXPANDED, EXT_STATE_EXPANDABLE);
+ testStates("treeitem_expanded_false", STATE_COLLAPSED, EXT_STATE_EXPANDABLE);
+ testStates("treeitem_expanded_empty", 0, 0, STATE_EXPANDED | STATE_COLLAPSED, EXT_STATE_EXPANDABLE);
+ testStates("treeitem_expanded_undefined", 0, 0, STATE_EXPANDED | STATE_COLLAPSED, EXT_STATE_EXPANDABLE);
+ testStates("treeitem_expanded_absent", 0, 0, STATE_EXPANDED | STATE_COLLAPSED, EXT_STATE_EXPANDABLE);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=452388">
+ Mozilla Bug 452388
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=499653"
+ title="Unify ARIA state attributes mapping rules">
+ Mozilla Bug 499653
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=989958"
+ title="Pressed state is not exposed on a button element with aria-pressed attribute"
+ Mozilla Bug 989958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="button_pressed_true" role="button" aria-pressed="true">This button has aria-pressed="true" and should get ROLE_TOGGLE_BUTTON. It should also get STATE_PRESSED.</div>
+ <div id="button_pressed_false" role="button" aria-pressed="false">This button has aria-pressed="false" and should get ROLE_TOGGLE_BUTTON.</div>
+ <div id="button_pressed_empty" role="button" aria-pressed="">This button has aria-pressed="" and should <emph>not</emph> get ROLE_BUTTON.</div>
+ <div id="button_pressed_undefined" role="button" aria-pressed="undefined">This button has aria-pressed="undefined" and should <emph>not</emph> get ROLE_TOGGLE_BUTTON.</div>
+ <div id="button_pressed_absent" role="button">This button has <emph>no</emph> aria-pressed attribute and should <emph>not</emph> get ROLE_TOGGLE_BUTTON.</div>
+
+ <div id="checkbox_checked_true" role="checkbox" aria-checked="true">This checkbox has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+ <div id="checkbox_checked_false" role="checkbox" aria-checked="false">This checkbox has aria-checked="false" and should get STATE_CHECKABLE.</div>
+ <div id="checkbox_checked_empty" role="checkbox" aria-checked="">This checkbox has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="checkbox_checked_undefined" role="checkbox" aria-checked="undefined">This checkbox has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="checkbox_checked_absent" role="checkbox">This checkbox has <emph>no</emph> aria-checked attribute and should get STATE_CHECKABLE.</div>
+
+ <form action="">
+ <input id="native_checkbox_nativechecked_ariatrue" type="checkbox" checked="checked" aria-checked="true"/>
+ <input id="native_checkbox_nativechecked_ariafalse" type="checkbox" checked="checked" aria-checked="false"/>
+ <input id="native_checkbox_nativechecked_ariaempty" type="checkbox" checked="checked" aria-checked=""/>
+ <input id="native_checkbox_nativechecked_ariaundefined" type="checkbox" checked="checked" aria-checked="undefined"/>
+ <input id="native_checkbox_nativechecked_ariaabsent" type="checkbox" checked="checked"/>
+
+ <input id="native_checkbox_nativeunchecked_ariatrue" type="checkbox" aria-checked="true"/>
+ <input id="native_checkbox_nativeunchecked_ariafalse" type="checkbox" aria-checked="false"/>
+ <input id="native_checkbox_nativeunchecked_ariaempty" type="checkbox" aria-checked=""/>
+ <input id="native_checkbox_nativeunchecked_ariaundefined" type="checkbox" aria-checked="undefined"/>
+ <input id="native_checkbox_nativeunchecked_ariaabsent" type="checkbox"/>
+ </form>
+
+ <div id="checkbox_readonly_true" role="checkbox" aria-readonly="true">This checkbox has aria-readonly="true" and should get STATE_READONLY.</div>
+ <div id="checkbox_readonly_false" role="checkbox" aria-readonly="false">This checkbox has aria-readonly="false" and should <emph>not</emph> get STATE_READONLY.</div>
+ <div id="checkbox_readonly_empty" role="checkbox" aria-readonly="">This checkbox has aria-readonly="" and should <emph>not</emph> get STATE_READONLY.</div>
+ <div id="checkbox_readonly_undefined" role="checkbox" aria-readonly="undefined">This checkbox has aria-readonly="undefined" and should <emph>not</emph> get STATE_READONLY.</div>
+ <div id="checkbox_readonly_absent" role="checkbox">This checkbox has <emph>no</emph> aria-readonly attribute and should <emph>not</emph> get STATE_READONLY.</div>
+
+ <div id="checkbox_required_true" role="checkbox" aria-required="true">This checkbox has aria-required="true" and should get STATE_REQUIRED.</div>
+ <div id="checkbox_required_false" role="checkbox" aria-required="false">This checkbox has aria-required="false" and should <emph>not</emph> get STATE_REQUIRED.</div>
+ <div id="checkbox_required_empty" role="checkbox" aria-required="">This checkbox has aria-required="" and should <emph>not</emph> get STATE_REQUIRED.</div>
+ <div id="checkbox_required_undefined" role="checkbox" aria-required="undefined">This checkbox has aria-required="undefined" and should <emph>not</emph> get STATE_REQUIRED.</div>
+ <div id="checkbox_required_absent" role="checkbox">This checkbox has <emph>no</emph> aria-required attribute and should <emph>not</emph> get STATE_REQUIRED.</div>
+
+ <div id="checkbox_invalid_true" role="checkbox" aria-invalid="true">This checkbox has aria-invalid="true" and should get STATE_INVALID.</div>
+ <div id="checkbox_invalid_false" role="checkbox" aria-invalid="false">This checkbox has aria-invalid="false" and should <emph>not</emph> get STATE_INVALID.</div>
+ <div id="checkbox_invalid_empty" role="checkbox" aria-invalid="">This checkbox has aria-invalid="" and should <emph>not</emph> get STATE_INVALID.</div>
+ <div id="checkbox_invalid_undefined" role="checkbox" aria-invalid="undefined">This checkbox has aria-invalid="undefined" and should <emph>not</emph> get STATE_INVALID.</div>
+ <div id="checkbox_invalid_absent" role="checkbox">This checkbox has <emph>no</emph> aria-invalid attribute and should <emph>not</emph> get STATE_INVALID.</div>
+
+ <div id="checkbox_disabled_true" role="checkbox" aria-disabled="true" tabindex="0">This checkbox has aria-disabled="true" and should get STATE_DISABLED.</div>
+ <div id="checkbox_disabled_false" role="checkbox" aria-disabled="false">This checkbox has aria-disabled="false" and should <emph>not</emph> get STATE_DISABLED.</div>
+ <div id="checkbox_disabled_empty" role="checkbox" aria-disabled="">This checkbox has aria-disabled="" and should <emph>not</emph> get STATE_DISABLED.</div>
+ <div id="checkbox_disabled_undefined" role="checkbox" aria-disabled="undefined">This checkbox has aria-disabled="undefined" and should <emph>not</emph> get STATE_DISABLED.</div>
+ <div id="checkbox_disabled_absent" role="checkbox">This checkbox has <emph>no</emph> aria-disabled attribute and should <emph>not</emph> get STATE_DISABLED.</div>
+
+ <div id="listbox_multiselectable_true" role="listbox" aria-multiselectable="true">
+ <div id="option_checked_true" role="option" aria-checked="true">item</div>
+ </div>
+ <div id="listbox_multiselectable_false" role="listbox" aria-multiselectable="false">
+ <div id="option_checked_false" role="option" aria-checked="false">item</div>
+ </div>
+ <div id="listbox_multiselectable_empty" role="listbox" aria-multiselectable="">
+ <div id="option_checked_empty" role="option" aria-checked="">item</div>
+ </div>
+ <div id="listbox_multiselectable_undefined" role="listbox" aria-multiselectable="undefined">
+ <div id="option_checked_undefined" role="option" aria-checked="undefined">item</div>
+ </div>
+ <div id="listbox_multiselectable_absent" role="listbox">
+ <div id="option_checked_absent" role="option">item</div>
+ </div>
+
+ <div role="menu">
+ <div id="menuitem_checked_true" role="menuitem" aria-checked="true">This menuitem has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+ <div id="menuitem_checked_false" role="menuitem" aria-checked="false">This menuitem has aria-checked="false" and should get STATE_CHECKABLE.</div>
+ <div id="menuitem_checked_empty" role="menuitem" aria-checked="">This menuitem has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="menuitem_checked_undefined" role="menuitem" aria-checked="undefined">This menuitem has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="menuitem_checked_absent" role="menuitem">This menuitem has <emph>no</emph> aria-checked attribute and should <emph>not</emph> get STATE_CHECKABLE.</div>
+
+ <div id="menuitemradio_checked_true" role="menuitemradio" aria-checked="true">This menuitem has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_checked.</div>
+ <div id="menuitemradio_checked_false" role="menuitemradio" aria-checked="false">This menuitem has aria-checked="false" and should get STATE_CHECKABLE.</div>
+ <div id="menuitemradio_checked_empty" role="menuitemradio" aria-checked="">This menuitem has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="menuitemradio_checked_undefined" role="menuitemradio" aria-checked="undefined">This menuitem has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="menuitemradio_checked_absent" role="menuitemradio">This menuitem has <emph>no</emph> aria-checked attribute but should get STATE_CHECKABLE.</div>
+ </div>
+
+ <div id="radio_checked_true" role="radio" aria-checked="true">This menuitem has aria-checked="true" and should get STATE_CHECKABLE. It should also get STATE_CHECKED.</div>
+ <div id="radio_checked_false" role="radio" aria-checked="false">This menuitem has aria-checked="false" and should get STATE_CHECKABLE.</div>
+ <div id="radio_checked_empty" role="radio" aria-checked="">This menuitem has aria-checked="" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="radio_checked_undefined" role="radio" aria-checked="undefined">This menuitem has aria-checked="undefined" and should <emph>not</emph> get STATE_CHECKABLE.</div>
+ <div id="radio_checked_absent" role="radio">This menuitem has <emph>no</emph> aria-checked attribute but should get STATE_CHECKABLE.</div>
+
+ <div id="textbox_readonly_true" role="textbox" aria-readonly="true"></div>
+ <div id="textbox_readonly_false" role="textbox" aria-readonly="false"></div>
+ <div id="textbox_readonly_empty" role="textbox" aria-readonly=""></div>
+ <div id="textbox_readonly_undefined" role="textbox" aria-readonly="undefined"></div>
+ <div id="textbox_readonly_absent" role="textbox"></div>
+
+ <div id="textbox_multiline_true" role="textbox" aria-multiline="true"></div>
+ <div id="textbox_multiline_false" role="textbox" aria-multiline="false"></div>
+ <div id="textbox_multiline_empty" role="textbox" aria-multiline=""></div>
+ <div id="textbox_multiline_undefined" role="textbox" aria-multiline="undefined"></div>
+ <div id="textbox_multiline_absent" role="textbox"></div>
+
+ <form action="">
+ <input id="native_textbox_nativereadonly_ariatrue" readonly="readonly" aria-readonly="true"/>
+ <input id="native_textbox_nativereadonly_ariafalse" readonly="readonly" aria-readonly="false"/>
+ <input id="native_textbox_nativereadonly_ariaempty" readonly="readonly" aria-readonly=""/>
+ <input id="native_textbox_nativereadonly_ariaundefined" readonly="readonly" aria-readonly="undefined"/>
+ <input id="native_textbox_nativereadonly_ariaabsent" readonly="readonly"/>
+
+ <input id="native_textbox_nativeeditable_ariatrue" aria-readonly="true"/>
+ <input id="native_textbox_nativeeditable_ariafalse" aria-readonly="false"/>
+ <input id="native_textbox_nativeeditable_ariaempty" aria-readonly=""/>
+ <input id="native_textbox_nativeeditable_ariaundefined" aria-readonly="undefined"/>
+ <input id="native_textbox_nativeeditable_ariaabsent"/>
+ </form>
+
+ <div role="tree">
+ <div id="treeitem_selected_true" role="treeitem" aria-selected="true">This treeitem has aria-selected="true" and should get STATE_SELECTABLE. It should also get STATE_SELECTED.</div>
+ <div id="treeitem_selected_false" role="treeitem" aria-selected="false">This treeitem has aria-selected="false" and should get STATE_SELECTABLE.</div>
+ <div id="treeitem_selected_empty" role="treeitem" aria-selected="">This treeitem has aria-selected="" and should <emph>not</emph> get STATE_SELECTABLE.</div>
+ <div id="treeitem_selected_undefined" role="treeitem" aria-selected="undefined">This treeitem has aria-selected="undefined" and should <emph>not</emph> get STATE_SELECTABLE.</div>
+ <div id="treeitem_selected_absent" role="treeitem">This treeitem has <emph>no</emph> aria-selected attribute and should <emph>not</emph> get STATE_SELECTABLE.</div>
+
+ <div id="treeitem_haspopup_true" role="treeitem" aria-haspopup="true">This treeitem has aria-haspopup="true" and should get STATE_HASPOPUP.</div>
+ <div id="treeitem_haspopup_false" role="treeitem" aria-haspopup="false">This treeitem has aria-haspopup="false" and should get STATE_HASPOPUP.</div>
+ <div id="treeitem_haspopup_empty" role="treeitem" aria-haspopup="">This treeitem has aria-haspopup="" and should <emph>not</emph> get STATE_HASPOPUP.</div>
+ <div id="treeitem_haspopup_undefined" role="treeitem" aria-haspopup="undefined">This treeitem has aria-haspopup="undefined" and should <emph>not</emph> get STATE_HASPOPUP.</div>
+ <div id="treeitem_haspopup_absent" role="treeitem">This treeitem has <emph>no</emph> aria-haspopup attribute and should <emph>not</emph> get STATE_HASPOPUP.</div>
+
+ <div id="treeitem_expanded_true" role="treeitem" aria-expanded="true">This treeitem has aria-expanded="true" and should get STATE_EXPANDABLE. It should also get STATE_EXPANDED.</div>
+ <div id="treeitem_expanded_false" role="treeitem" aria-expanded="false">This treeitem has aria-expanded="false" and should get STATE_EXPANDABLE. It should also get STATE_COLLAPSED.</div>
+ <div id="treeitem_expanded_empty" role="treeitem" aria-expanded="">This treeitem has aria-expanded="" and should <emph>not</emph> get STATE_EXPANDABLE.</div>
+ <div id="treeitem_expanded_undefined" role="treeitem" aria-expanded="undefined">This treeitem has aria-expanded="undefined" and should <emph>not</emph> get STATE_EXPANDABLE.</div>
+ <div id="treeitem_expanded_absent" role="treeitem">This treeitem has <emph>no</emph> aria-expanded attribute and should <emph>not</emph> get STATE_EXPANDABLE.</div>
+ </div>
+
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/test_bug420863.html b/accessible/tests/mochitest/test_bug420863.html
new file mode 100644
index 000000000..63ab4d37b
--- /dev/null
+++ b/accessible/tests/mochitest/test_bug420863.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=420863
+-->
+<head>
+ <title>Table indexes chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="events.js"></script>
+ <script type="application/javascript"
+ src="actions.js"></script>
+
+ <script type="application/javascript">
+ var gClickHandler = null;
+
+ function doTest()
+ {
+ // Actions should be exposed on any accessible having related DOM node
+ // with registered 'click' event handler.
+
+ //////////////////////////////////////////////////////////////////////////
+ // generic td
+ var td1Acc = getAccessible("td1");
+ if (!td1Acc) {
+ SimpleTest.finish();
+ return;
+ }
+
+ is(td1Acc.actionCount, 0,
+ "Simple table cell shouldn't have any actions");
+
+ //////////////////////////////////////////////////////////////////////////
+ // one td with 'onclick' attribute and one with registered click handler
+ var td3Node = getNode("td3");
+
+ // register 'click' event handler
+ gClickHandler = {
+ handleEvent: function handleEvent(aEvent)
+ {
+ }
+ };
+ td3Node.addEventListener("click", gClickHandler, false);
+
+ // check actions
+ var actionsArray = [
+ {
+ ID: "td2", // "onclick" attribute
+ actionName: "click",
+ actionIndex: 0,
+ events: CLICK_EVENTS
+ },
+ {
+ ID: td3Node,
+ actionName: "click",
+ actionIndex: 0,
+ events: CLICK_EVENTS,
+ checkOnClickEvent: function check(aEvent)
+ {
+ // unregister click event handler
+ this.ID.removeEventListener("click", gClickHandler, false);
+
+ // check actions
+ is(getAccessible(this.ID).actionCount, 0,
+ "td3 shouldn't have actions");
+ }
+ }
+ ];
+
+ testActions(actionsArray); // will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=420863"
+ title="If an HTML element has an onClick attribute, expose its click action on the element rather than its child text leaf node."
+ target="_blank">Mozilla Bug 420863</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table>
+ <tr>
+ <td id="td1">Can't click this cell</td>
+ <td onclick="gTdClickAttr = true;"
+ id="td2">Cell with 'onclick' attribute</td>
+ <td id="td3">Cell with registered 'click' event handler</td>
+ </tr>
+ </table>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/test_descr.html b/accessible/tests/mochitest/test_descr.html
new file mode 100644
index 000000000..b9dc031d5
--- /dev/null
+++ b/accessible/tests/mochitest/test_descr.html
@@ -0,0 +1,121 @@
+<html>
+
+<head>
+ <title>nsIAccessible::description tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="name.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Description from aria-describedby attribute
+ testDescr("img1", "aria description");
+
+ // No description from @title attribute because it is used to generate
+ // name.
+ testDescr("img2", "");
+
+ // Description from @title attribute, name is generated from @alt
+ // attribute.
+ testDescr("img3", "description");
+
+ // No description from aria-describedby since it is the same as the
+ // @alt attribute which is used as the name
+ testDescr("img4", "");
+
+ // No description from @title attribute since it is the same as the
+ // @alt attribute which is used as the name
+ testDescr("img5", "");
+
+ // Description from content of h2.
+ testDescr("p", "heading");
+
+ // From table summary (caption is used as a name)
+ testDescr("table1", "summary");
+
+ // Empty (summary is used as a name)
+ testDescr("table2", "");
+
+ // From title (summary is used as a name)
+ testDescr("table3", "title");
+
+ // No description from <desc> element since it is the same as the
+ // <title> element.
+ testDescr("svg", "");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=489944"
+ title="@title attribute no longer exposed on accDescription">
+ Mozilla Bug 489944
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=666212"
+ title="summary attribute content mapped to accessible name in MSAA">
+ Mozilla Bug 666212
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi/id=1031188"
+ title="Ensure that accDescription never duplicates AccessibleName">
+ Mozilla Bug 1031188
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="description">aria description</p>
+ <img id="img1" aria-describedby="description" />
+ <img id="img2" title="title" />
+ <img id="img3" alt="name" title="description" />
+ <img id="img4" alt="aria description" aria-describedby="description">
+ <img id="img5" alt="image" title="image">
+
+ <h2 id="heading">heading</h2>
+ <p id="p" aria-describedby="heading" role="button">click me</p>
+
+ <table id="table1" summary="summary">
+ <caption>caption</caption>
+ <tr><td>cell</td></tr>
+ </table>
+
+ <table id="table2" summary="summary">
+ <tr><td>cell</td></tr>
+ </table>
+
+ <table id="table3" summary="summary" title="title">
+ <tr><td>cell</td></tr>
+ </table>
+
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1"
+ viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice"
+ id="svg"
+ style="width:100px; height:100px;">
+ <title>SVG Image</title>
+ <desc>SVG Image</desc>
+ <linearGradient id="gradient">
+ <stop class="begin" offset="0%"/>
+ <stop class="end" offset="100%"/>
+ </linearGradient>
+ <rect x="0" y="0" width="100" height="100" style="fill:url(#gradient)" />
+ <circle cx="50" cy="50" r="30" style="fill:url(#gradient)" />
+ </svg>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/test_nsIAccessibleDocument.html b/accessible/tests/mochitest/test_nsIAccessibleDocument.html
new file mode 100644
index 000000000..9e0dc7489
--- /dev/null
+++ b/accessible/tests/mochitest/test_nsIAccessibleDocument.html
@@ -0,0 +1,96 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=441737
+-->
+<head>
+ <title>nsIAccessibleDocument chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="role.js"></script>
+ <script type="application/javascript"
+ src="states.js"></script>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var docAcc = getAccessible(document, [nsIAccessibleDocument]);
+ if (docAcc) {
+ // nsIAccessible
+ is(docAcc.name, "nsIAccessibleDocument chrome tests",
+ "Name for document accessible not correct!");
+
+ testRole(docAcc, ROLE_DOCUMENT);
+
+ // check if it is focusable, read-only.
+ testStates(docAcc, (STATE_READONLY | STATE_FOCUSABLE));
+
+ // No actions wanted on doc
+ is(docAcc.actionCount, 0, "Wrong number of actions for document!");
+
+ // attributes should contain tag:body
+ attributes = docAcc.attributes;
+ is(attributes.getStringProperty("tag"), "body",
+ "Wrong attribute on document!");
+
+ // Document URL.
+ var rootDir = getRootDirectory(window.location.href);
+ is(docAcc.URL, rootDir + "test_nsIAccessibleDocument.html",
+ "Wrong URL for document!");
+
+ // Document title and mime type.
+ is(docAcc.title, "nsIAccessibleDocument chrome tests",
+ "Wrong title for document!");
+ is(docAcc.mimeType, "text/html",
+ "Wrong mime type for document!");
+
+ // DocAccessible::getDocType currently returns NS_ERROR_FAILURE.
+ // See bug 442005. After fixing, please remove this comment and
+ // uncomment the below two lines to enable the test.
+// is(docAcc.docType, "HTML",
+// "Wrong type of document!");
+
+ // Test for correct nsIDOMDocument retrieval.
+ var domDoc = null;
+ try {
+ domDoc = docAcc.DOMDocument.QueryInterface(nsIDOMDocument);
+ } catch(e) {}
+ ok(domDoc, "no nsIDOMDocument for this doc accessible!");
+ is(domDoc, document, "Document nodes do not match!");
+
+ // Test for correct nsIDOMWindow retrieval.
+ var domWindow = null;
+ try {
+ domWindow = docAcc.window.QueryInterface(nsIDOMWindow);
+ } catch(e) {}
+ ok(domWindow, "no nsIDOMWindow for this doc accessible!");
+ is(domWindow, window, "Window nodes do not match!");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=441737"
+ title="nsAccessibleDocument chrome tests">
+ Mozilla Bug 441737
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/test_nsIAccessibleImage.html b/accessible/tests/mochitest/test_nsIAccessibleImage.html
new file mode 100644
index 000000000..39e7e41e1
--- /dev/null
+++ b/accessible/tests/mochitest/test_nsIAccessibleImage.html
@@ -0,0 +1,202 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429659
+-->
+<head>
+ <title>nsIAccessibleImage chrome tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="common.js"></script>
+ <script type="application/javascript"
+ src="role.js"></script>
+ <script type="application/javascript"
+ src="attributes.js"></script>
+ <script type="application/javascript"
+ src="layout.js"></script>
+
+ <script type="application/javascript">
+ function testCoordinates(aID, aAcc, aWidth, aHeight)
+ {
+ var screenX = {}, screenY = {}, windowX = {}, windowY = {}, parentX = {},
+ parentY = {};
+
+ // get screen coordinates.
+ aAcc.getImagePosition(
+ nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE,
+ screenX, screenY);
+ // get window coordinates.
+ aAcc.getImagePosition(
+ nsIAccessibleCoordinateType.COORDTYPE_WINDOW_RELATIVE,
+ windowX, windowY);
+ // get parent related coordinates.
+ aAcc.getImagePosition(
+ nsIAccessibleCoordinateType.COORDTYPE_PARENT_RELATIVE,
+ parentX, parentY);
+ // XXX For linked images, a negative parentY value is returned, and the
+ // screenY coordinate is the link's screenY coordinate minus 1.
+ // Until this is fixed, set parentY to -1 if it's negative.
+ if (parentY.value < 0)
+ parentY.value = -1;
+
+ // See if asking image for child at image's screen coordinates gives
+ // correct accessible. getChildAtPoint operates on screen coordinates.
+ var tempAcc = null;
+ try {
+ tempAcc = aAcc.getChildAtPoint(screenX.value, screenY.value);
+ } catch(e) {}
+ is(tempAcc, aAcc,
+ "Wrong accessible returned for position of " + aID + "!");
+
+ // get image's parent.
+ var imageParentAcc = null;
+ try {
+ imageParentAcc = aAcc.parent;
+ } catch(e) {}
+ ok(imageParentAcc, "no parent accessible for " + aID + "!");
+
+ if (imageParentAcc) {
+ // See if parent's screen coordinates plus image's parent relative
+ // coordinates equal to image's screen coordinates.
+ var parentAccX = {}, parentAccY = {}, parentAccWidth = {},
+ parentAccHeight = {};
+ imageParentAcc.getBounds(parentAccX, parentAccY, parentAccWidth,
+ parentAccHeight);
+ is(parentAccX.value + parentX.value, screenX.value,
+ "Wrong screen x coordinate for " + aID + "!");
+// XXX see bug 456344 is(parentAccY.value + parentY.value, screenY.value,
+// "Wrong screen y coordinate for " + aID + "!");
+ }
+
+ var [expected_w, expected_h] = CSSToDevicePixels(window, aWidth, aHeight);
+ var width = {}, height = {};
+ aAcc.getImageSize(width, height);
+ is(width.value, expected_w, "Wrong width for " + aID + "!");
+ is(height.value, expected_h, "wrong height for " + aID + "!");
+ }
+
+ function testThis(aID, aSRC, aWidth, aHeight,
+ aActionCount, aActionNames)
+ {
+ var acc = getAccessible(aID, [nsIAccessibleImage]);
+ if (!acc)
+ return;
+
+ // Test role
+ testRole(aID, ROLE_GRAPHIC);
+
+ // test coordinates and size
+ testCoordinates(aID, acc, aWidth, aHeight);
+
+ // bug 429659: Make sure the SRC attribute is set for any image
+ var attributes = {"src": aSRC};
+ testAttrs(acc, attributes, true);
+
+ var actionCount = aActionCount || 0;
+ is(acc.actionCount, actionCount,
+ "Wrong number of actions for " + aID + "!");
+ if (actionCount) {
+ for (index = 0; index < aActionNames.length; index++) {
+ is(acc.getActionName(index), aActionNames[index],
+ "Wrong action name for " + aID + ", index " + index +"!");
+ }
+ }
+ }
+
+ function doTest()
+ {
+ // Test non-linked image
+ testThis("nonLinkedImage", "moz.png", 89, 38);
+
+ // Test linked image
+ var actionNamesArray = new Array("jump");
+ testThis("linkedImage", "moz.png", 89, 38, 1,
+ actionNamesArray);
+
+ // Image with long desc
+ var actionNamesArray = new Array("showlongdesc");
+ testThis("longdesc", "moz.png", 89, 38, 1,
+ actionNamesArray);
+
+ // Image with invalid url in long desc
+ testThis("invalidLongdesc", "moz.png", 89, 38, 0);
+
+ // Image with click and long desc
+ actionNamesArray = null;
+ actionNamesArray = new Array("click", "showlongdesc");
+ testThis("clickAndLongdesc", "moz.png",
+ 89, 38, 2, actionNamesArray);
+
+ // Image with click
+ actionNamesArray = null;
+ actionNamesArray = new Array("click");
+ testThis("click", "moz.png",
+ 89, 38, 1, actionNamesArray);
+
+ // Image with long desc
+ actionNamesArray = null;
+ actionNamesArray = new Array("showlongdesc");
+ testThis("longdesc2", "moz.png",
+ 89, 38, 1, actionNamesArray);
+
+ // Image described by HTML:a@href with whitespaces
+ actionNamesArray = null;
+ actionNamesArray = new Array("showlongdesc");
+ testThis("longdesc3", "moz.png",
+ 89, 38, 1, actionNamesArray);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=429659">Mozilla Bug 429659</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=652635"
+ title="fall back missing @longdesc to aria-describedby pointing to a href">
+ Mozilla Bug 652635
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <br>Simple image:<br>
+ <img id="nonLinkedImage" src="moz.png"/>
+ <br>Linked image:<br>
+ <a href="http://www.mozilla.org"><img id="linkedImage" src="moz.png"></a>
+ <br>Image with longdesc:<br>
+ <img id="longdesc" src="moz.png" longdesc="longdesc_src.html"
+ alt="Image of Mozilla logo"/>
+ <br>Image with invalid url in longdesc:<br>
+ <img id="invalidLongdesc" src="moz.png" longdesc="longdesc src.html"
+ alt="Image of Mozilla logo"/>
+ <br>Image with click and longdesc:<br>
+ <img id="clickAndLongdesc" src="moz.png" longdesc="longdesc_src.html"
+ alt="Another image of Mozilla logo" onclick="alert('Clicked!');"/>
+
+ <br>image described by a link to be treated as longdesc<br>
+ <img id="longdesc2" src="moz.png" aria-describedby="describing_link"
+ alt="Second Image of Mozilla logo"/>
+ <a id="describing_link" href="longdesc_src.html">link to description of image</a>
+
+ <br>Image described by a link to be treated as longdesc with whitespaces<br>
+ <img id="longdesc3" src="moz.png" aria-describedby="describing_link2"
+ alt="Second Image of Mozilla logo"/>
+ <a id="describing_link2" href="longdesc src.html">link to description of image</a>
+
+ <br>Image with click:<br>
+ <img id="click" src="moz.png"
+ alt="A third image of Mozilla logo" onclick="alert('Clicked, too!');"/>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text.js b/accessible/tests/mochitest/text.js
new file mode 100644
index 000000000..a91ea9acc
--- /dev/null
+++ b/accessible/tests/mochitest/text.js
@@ -0,0 +1,634 @@
+////////////////////////////////////////////////////////////////////////////////
+// Public
+
+const BOUNDARY_CHAR = nsIAccessibleText.BOUNDARY_CHAR;
+const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START;
+const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END;
+const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START;
+const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END;
+
+const kTextEndOffset = nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT;
+const kCaretOffset = nsIAccessibleText.TEXT_OFFSET_CARET;
+
+const EndPoint_Start = nsIAccessibleTextRange.EndPoint_Start;
+const EndPoint_End = nsIAccessibleTextRange.EndPoint_End;
+
+const kTodo = 1; // a test is expected to fail
+const kOk = 2; // a test doesn't fail
+
+/**
+ * Test characterCount for the given array of accessibles.
+ *
+ * @param aCount [in] the expected character count
+ * @param aIDs [in] array of accessible identifiers to test
+ * @param aTodoFlag [in, optional] either kOk or kTodo
+ */
+function testCharacterCount(aIDs, aCount, aTodoFlag)
+{
+ var ids = (aIDs instanceof Array) ? aIDs : [ aIDs ];
+ var isFunc = (aTodoFlag == kTodo) ? todo_is : is;
+ for (var i = 0; i < ids.length; i++) {
+ var textacc = getAccessible(ids[i], [nsIAccessibleText]);
+ isFunc(textacc.characterCount, aCount,
+ "Wrong character count for " + prettyName(ids[i]));
+ }
+}
+
+/**
+ * Test text between two given offsets.
+ *
+ * @param aIDs [in] an array of accessible IDs to test
+ * @param aStartOffset [in] the start offset within the text to test
+ * @param aEndOffset [in] the end offset up to which the text is tested
+ * @param aText [in] the expected result from the test
+ * @param aTodoFlag [in, optional] either kOk or kTodo
+ */
+function testText(aIDs, aStartOffset, aEndOffset, aText, aTodoFlag)
+{
+ var ids = (aIDs instanceof Array) ? aIDs : [ aIDs ];
+ var isFunc = (aTodoFlag == kTodo) ? todo_is : is;
+ for (var i = 0; i < ids.length; i++) {
+ var acc = getAccessible(ids[i], nsIAccessibleText);
+ try {
+ isFunc(acc.getText(aStartOffset, aEndOffset), aText,
+ "getText: wrong text between start and end offsets '" +
+ aStartOffset + "', '" + aEndOffset + " for '" +
+ prettyName(ids[i]) + "'");
+ } catch (e) {
+ ok(false,
+ "getText fails between start and end offsets '" + aStartOffset +
+ "', '" + aEndOffset + " for '" + prettyName(ids[i]) + "'");
+ }
+ }
+}
+
+/**
+ * Test password text between two given offsets
+ *
+ * @param aIDs [in] an array of accessible IDs to test
+ * @param aStartOffset [in] the start offset within the text to test
+ * @param aEndOffset [in] the end offset up to which the text is tested
+ * @param aText [in] the expected result from the test
+ *
+ * @note All this function does is test that getText doe snot expose the
+ * password text itself, but something else.
+ */
+function testPasswordText(aIDs, aStartOffset, aEndOffset, aText)
+{
+ for (var i = 0; i < aIDs.length; i++)
+ {
+ var acc = getAccessible(aIDs[i], nsIAccessibleText);
+ try {
+ isnot(acc.getText(aStartOffset, aEndOffset), aText,
+ "getText: plain text between start and end offsets '" + aStartOffset +
+ "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'");
+ } catch (e) {
+ ok(false,
+ "getText fails between start and end offsets '" + aStartOffset +
+ "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'");
+ }
+ }
+}
+
+/**
+ * Test getTextAtOffset for BOUNDARY_CHAR over different elements.
+ *
+ * @param aIDs [in] the accessible identifier or array of accessible
+ * identifiers
+ * @param aOffset [in] the offset to get a character at it
+ * @param aChar [in] the expected character
+ * @param aStartOffset [in] expected start offset of the character
+ * @param aEndOffset [in] expected end offset of the character
+ */
+function testCharAtOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
+{
+ var IDs = (aIDs instanceof Array) ? aIDs : [ aIDs ];
+ for (var i = 0; i < IDs.length; i++) {
+ var acc = getAccessible(IDs[i], nsIAccessibleText);
+ testTextHelper(IDs[i], aOffset, BOUNDARY_CHAR,
+ aChar, aStartOffset, aEndOffset,
+ kOk, kOk, kOk,
+ acc.getTextAtOffset, "getTextAtOffset ");
+ }
+}
+
+/**
+ * Test getTextAtOffset function over different elements.
+ *
+ * @param aIDs [in] ID or array of IDs
+ * @param aBoundaryType [in] boundary type for text to be retrieved
+ * @param aTestList [in] array of sets:
+ * offset1 and offset2 defining the offset range
+ * the text in the range
+ * start offset of the text in the range
+ * end offset of the text in the range
+ *
+ * or
+ *
+ * @param aOffset [in] the offset to get the text at
+ * @param aBoundaryType [in] Boundary type for text to be retrieved
+ * @param aText [in] expected return text for getTextAtOffset
+ * @param aStartOffset [in] expected return start offset for getTextAtOffset
+ * @param aEndOffset [in] expected return end offset for getTextAtOffset
+ * @param ... [in] list of ids or list of tuples made of:
+ * element identifier
+ * kTodo or kOk for returned text
+ * kTodo or kOk for returned start offset
+ * kTodo or kOk for returned offset result
+ */
+function testTextAtOffset()
+{
+ testTextSuperHelper("getTextAtOffset", arguments);
+}
+
+/**
+ * Test getTextAfterOffset for BOUNDARY_CHAR over different elements.
+ *
+ * @param aIDs [in] the accessible identifier or array of accessible
+ * identifiers
+ * @param aOffset [in] the offset to get a character after it
+ * @param aChar [in] the expected character
+ * @param aStartOffset [in] expected start offset of the character
+ * @param aEndOffset [in] expected end offset of the character
+ */
+function testCharAfterOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
+{
+ var IDs = (aIDs instanceof Array) ? aIDs : [ aIDs ];
+ for (var i = 0; i < IDs.length; i++) {
+ var acc = getAccessible(IDs[i], nsIAccessibleText);
+ testTextHelper(IDs[i], aOffset, BOUNDARY_CHAR,
+ aChar, aStartOffset, aEndOffset,
+ kOk, kOk, kOk,
+ acc.getTextAfterOffset, "getTextAfterOffset ");
+ }
+}
+
+/**
+ * Test getTextAfterOffset function over different elements
+ *
+ * @param aIDs [in] ID or array of IDs
+ * @param aBoundaryType [in] boundary type for text to be retrieved
+ * @param aTestList [in] array of sets:
+ * offset1 and offset2 defining the offset range
+ * the text in the range
+ * start offset of the text in the range
+ * end offset of the text in the range
+ *
+ * or
+ *
+ * @param aOffset [in] the offset to get the text after
+ * @param aBoundaryType [in] Boundary type for text to be retrieved
+ * @param aText [in] expected return text for getTextAfterOffset
+ * @param aStartOffset [in] expected return start offset for getTextAfterOffset
+ * @param aEndOffset [in] expected return end offset for getTextAfterOffset
+ * @param ... [in] list of ids or list of tuples made of:
+ * element identifier
+ * kTodo or kOk for returned text
+ * kTodo or kOk for returned start offset
+ * kTodo or kOk for returned offset result
+ */
+function testTextAfterOffset(aOffset, aBoundaryType,
+ aText, aStartOffset, aEndOffset)
+{
+ testTextSuperHelper("getTextAfterOffset", arguments);
+}
+
+/**
+ * Test getTextBeforeOffset for BOUNDARY_CHAR over different elements.
+ *
+ * @param aIDs [in] the accessible identifier or array of accessible
+ * identifiers
+ * @param aOffset [in] the offset to get a character before it
+ * @param aChar [in] the expected character
+ * @param aStartOffset [in] expected start offset of the character
+ * @param aEndOffset [in] expected end offset of the character
+ */
+function testCharBeforeOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
+{
+ var IDs = (aIDs instanceof Array) ? aIDs : [ aIDs ];
+ for (var i = 0; i < IDs.length; i++) {
+ var acc = getAccessible(IDs[i], nsIAccessibleText);
+ testTextHelper(IDs[i], aOffset, BOUNDARY_CHAR,
+ aChar, aStartOffset, aEndOffset,
+ kOk, kOk, kOk,
+ acc.getTextBeforeOffset, "getTextBeforeOffset ");
+ }
+}
+
+/**
+ * Test getTextBeforeOffset function over different elements
+ *
+ * @param aIDs [in] ID or array of IDs
+ * @param aBoundaryType [in] boundary type for text to be retrieved
+ * @param aTestList [in] array of sets:
+ * offset1 and offset2 defining the offset range
+ * the text in the range
+ * start offset of the text in the range
+ * end offset of the text in the range
+ *
+ * or
+ *
+ * @param aOffset [in] the offset to get the text before
+ * @param aBoundaryType [in] Boundary type for text to be retrieved
+ * @param aText [in] expected return text for getTextBeforeOffset
+ * @param aStartOffset [in] expected return start offset for getTextBeforeOffset
+ * @param aEndOffset [in] expected return end offset for getTextBeforeOffset
+ * @param ... [in] list of ids or list of tuples made of:
+ * element identifier
+ * kTodo or kOk for returned text
+ * kTodo or kOk for returned start offset
+ * kTodo or kOk for returned offset result
+ */
+function testTextBeforeOffset(aOffset, aBoundaryType,
+ aText, aStartOffset, aEndOffset)
+{
+ testTextSuperHelper("getTextBeforeOffset", arguments);
+}
+
+/**
+ * Test word count for an element.
+ *
+ * @param aElement [in] element identifier
+ * @param aCount [in] Expected word count
+ * @param aToDoFlag [in] kTodo or kOk for returned text
+ */
+function testWordCount(aElement, aCount, aToDoFlag)
+{
+ var isFunc = (aToDoFlag == kTodo) ? todo_is : is;
+ var acc = getAccessible(aElement, nsIAccessibleText);
+ var startOffsetObj = {}, endOffsetObj = {};
+ var length = acc.characterCount;
+ var offset = 0;
+ var wordCount = 0;
+ while (true) {
+ var text = acc.getTextAtOffset(offset, BOUNDARY_WORD_START,
+ startOffsetObj, endOffsetObj);
+ if (offset >= length)
+ break;
+
+ wordCount++;
+ offset = endOffsetObj.value;
+ }
+ isFunc(wordCount, aCount,
+ "wrong words count for '" + acc.getText(0, -1) + "': " + wordCount +
+ " in " + prettyName(aElement));
+}
+
+/**
+ * Test word at a position for an element.
+ *
+ * @param aElement [in] element identifier
+ * @param aWordIndex [in] index of the word to test
+ * @param aText [in] expected text for that word
+ * @param aToDoFlag [in] kTodo or kOk for returned text
+ */
+function testWordAt(aElement, aWordIndex, aText, aToDoFlag)
+{
+ var isFunc = (aToDoFlag == kTodo) ? todo_is : is;
+ var acc = getAccessible(aElement, nsIAccessibleText);
+
+ var textLength = acc.characterCount;
+ var wordIdx = aWordIndex;
+ var startOffsetObj = { value: 0 }, endOffsetObj = { value: 0 };
+ for (offset = 0; offset < textLength; offset = endOffsetObj.value) {
+ acc.getTextAtOffset(offset, BOUNDARY_WORD_START,
+ startOffsetObj, endOffsetObj);
+
+ wordIdx--;
+ if (wordIdx < 0)
+ break;
+ }
+
+ if (wordIdx >= 0) {
+ ok(false,
+ "the given word index '" + aWordIndex + "' exceeds words amount in " +
+ prettyName(aElement));
+
+ return;
+ }
+
+ var startWordOffset = startOffsetObj.value;
+ var endWordOffset = endOffsetObj.value;
+
+ // Calculate the end word offset.
+ acc.getTextAtOffset(endOffsetObj.value, BOUNDARY_WORD_END,
+ startOffsetObj, endOffsetObj);
+ if (startOffsetObj.value != textLength)
+ endWordOffset = startOffsetObj.value
+
+ if (endWordOffset <= startWordOffset) {
+ todo(false,
+ "wrong start and end offset for word at index '" + aWordIndex + "': " +
+ " of text '" + acc.getText(0, -1) + "' in " + prettyName(aElement));
+
+ return;
+ }
+
+ text = acc.getText(startWordOffset, endWordOffset);
+ isFunc(text, aText, "wrong text for word at index '" + aWordIndex + "': " +
+ " of text '" + acc.getText(0, -1) + "' in " + prettyName(aElement));
+}
+
+/**
+ * Test words in a element.
+ *
+ * @param aElement [in] element identifier
+ * @param aWords [in] array of expected words
+ * @param aToDoFlag [in, optional] kTodo or kOk for returned text
+ */
+function testWords(aElement, aWords, aToDoFlag)
+{
+ if (aToDoFlag == null)
+ aToDoFlag = kOk;
+
+ testWordCount(aElement, aWords.length, aToDoFlag);
+
+ for (var i = 0; i < aWords.length; i++) {
+ testWordAt(aElement, i, aWords[i], aToDoFlag);
+ }
+}
+
+/**
+ * Remove all selections.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ */
+function cleanTextSelections(aID)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+
+ while (acc.selectionCount > 0)
+ acc.removeSelection(0);
+}
+
+/**
+ * Test addSelection method.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ * @param aStartOffset [in] start offset for the new selection
+ * @param aEndOffset [in] end offset for the new selection
+ * @param aSelectionsCount [in] expected number of selections after addSelection
+ */
+function testTextAddSelection(aID, aStartOffset, aEndOffset, aSelectionsCount)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ var text = acc.getText(0, -1);
+
+ acc.addSelection(aStartOffset, aEndOffset);
+
+ ok(acc.selectionCount, aSelectionsCount,
+ text + ": failed to add selection from offset '" + aStartOffset +
+ "' to offset '" + aEndOffset + "': selectionCount after");
+}
+
+/**
+ * Test removeSelection method.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ * @param aSelectionIndex [in] index of the selection to be removed
+ * @param aSelectionsCount [in] expected number of selections after
+ * removeSelection
+ */
+function testTextRemoveSelection(aID, aSelectionIndex, aSelectionsCount)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ var text = acc.getText(0, -1);
+
+ acc.removeSelection(aSelectionIndex);
+
+ ok(acc.selectionCount, aSelectionsCount,
+ text + ": failed to remove selection at index '" +
+ aSelectionIndex + "': selectionCount after");
+}
+
+/**
+ * Test setSelectionBounds method.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ * @param aStartOffset [in] new start offset for the selection
+ * @param aEndOffset [in] new end offset for the selection
+ * @param aSelectionIndex [in] index of the selection to set
+ * @param aSelectionsCount [in] expected number of selections after
+ * setSelectionBounds
+ */
+function testTextSetSelection(aID, aStartOffset, aEndOffset,
+ aSelectionIndex, aSelectionsCount)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ var text = acc.getText(0, -1);
+
+ acc.setSelectionBounds(aSelectionIndex, aStartOffset, aEndOffset);
+
+ is(acc.selectionCount, aSelectionsCount,
+ text + ": failed to set selection at index '" +
+ aSelectionIndex + "': selectionCount after");
+}
+
+/**
+ * Test selectionCount method.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ * @param aCount [in] expected selection count
+ */
+function testTextSelectionCount(aID, aCount)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ var text = acc.getText(0, -1);
+
+ is(acc.selectionCount, aCount, text + ": wrong selectionCount: ");
+}
+
+/**
+ * Test getSelectionBounds method.
+ *
+ * @param aID [in] Id, DOM node, or acc obj
+ * @param aStartOffset [in] expected start offset for the selection
+ * @param aEndOffset [in] expected end offset for the selection
+ * @param aSelectionIndex [in] index of the selection to get
+ */
+function testTextGetSelection(aID, aStartOffset, aEndOffset, aSelectionIndex)
+{
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ var text = acc.getText(0, -1);
+
+ var startObj = {}, endObj = {};
+ acc.getSelectionBounds(aSelectionIndex, startObj, endObj);
+
+ is(startObj.value, aStartOffset, text + ": wrong start offset for index '" +
+ aSelectionIndex + "'");
+ is(endObj.value, aEndOffset, text + ": wrong end offset for index '" +
+ aSelectionIndex + "'");
+}
+
+function testTextRange(aRange, aRangeDescr, aStartContainer, aStartOffset,
+ aEndContainer, aEndOffset, aText,
+ aCommonContainer, aChildren)
+{
+ isObject(aRange.startContainer, getAccessible(aStartContainer),
+ "Wrong start container of " + aRangeDescr);
+ is(aRange.startOffset, aStartOffset,
+ "Wrong start offset of " + aRangeDescr);
+ isObject(aRange.endContainer, getAccessible(aEndContainer),
+ "Wrong end container of " + aRangeDescr);
+ is(aRange.endOffset, aEndOffset,
+ "Wrong end offset of " + aRangeDescr);
+
+ if (aText === undefined) {
+ return;
+ }
+
+ is(aRange.text, aText, "Wrong text of " + aRangeDescr);
+
+ var children = aRange.embeddedChildren;
+ is(children ? children.length : 0, aChildren ? aChildren.length : 0,
+ "Wrong embedded children count of " + aRangeDescr);
+
+ isObject(aRange.container, getAccessible(aCommonContainer),
+ "Wrong container of " + aRangeDescr);
+
+ if (aChildren) {
+ for (var i = 0; i < aChildren.length; i++) {
+ var expectedChild = getAccessible(aChildren[i]);
+ var actualChild = children.queryElementAt(i, nsIAccessible);
+ isObject(actualChild, expectedChild,
+ "Wrong child at index '" + i + "' of " + aRangeDescr);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private
+
+function testTextSuperHelper(aFuncName, aArgs)
+{
+ // List of tests.
+ if (aArgs[2] instanceof Array) {
+ var ids = (aArgs[0] instanceof Array) ? aArgs[0] : [ aArgs[0] ];
+ var boundaryType = aArgs[1];
+ var list = aArgs[2];
+ for (var i = 0; i < list.length; i++) {
+ var offset1 = list[i][0], offset2 = list[i][1];
+ var text = list[i][2], startOffset = list[i][3], endOffset = list[i][4];
+ var failureList = list[i][5];
+ for (var offset = offset1; offset <= offset2; offset++) {
+ for (var idIdx = 0; idIdx < ids.length; idIdx++) {
+ var id = ids[idIdx];
+
+ var flagOk1 = kOk, flagOk2 = kOk, flagOk3 = kOk;
+ if (failureList) {
+ for (var fIdx = 0; fIdx < failureList.length; fIdx++) {
+ if (offset == failureList[fIdx][0] && id == failureList[fIdx][1]) {
+ flagOk1 = failureList[fIdx][2];
+ flagOk2 = failureList[fIdx][3];
+ flagOk3 = failureList[fIdx][4];
+ break;
+ }
+ }
+ }
+
+ var acc = getAccessible(id, nsIAccessibleText);
+ testTextHelper(id, offset, boundaryType,
+ text, startOffset, endOffset,
+ flagOk1, flagOk2, flagOk3,
+ acc[aFuncName], aFuncName + " ");
+ }
+ }
+ }
+ return;
+ }
+
+ // Test at single offset. List of IDs.
+ var offset = aArgs[0];
+ var boundaryType = aArgs[1];
+ var text = aArgs[2];
+ var startOffset = aArgs[3];
+ var endOffset = aArgs[4];
+ if (aArgs[5] instanceof Array) {
+ var ids = aArgs[5];
+ for (var i = 0; i < ids.length; i++) {
+ var acc = getAccessible(ids[i], nsIAccessibleText);
+ testTextHelper(ids[i], offset, boundaryType,
+ text, startOffset, endOffset,
+ kOk, kOk, kOk,
+ acc[aFuncName], aFuncName + " ");
+ }
+
+ return;
+ }
+
+ // Each ID is tested separately.
+ for (var i = 5; i < aArgs.length; i = i + 4) {
+ var ID = aArgs[i];
+ var acc = getAccessible(ID, nsIAccessibleText);
+ var toDoFlag1 = aArgs[i + 1];
+ var toDoFlag2 = aArgs[i + 2];
+ var toDoFlag3 = aArgs[i + 3];
+
+ testTextHelper(ID, offset, boundaryType,
+ text, startOffset, endOffset,
+ toDoFlag1, toDoFlag2, toDoFlag3,
+ acc[aFuncName], aFuncName + " ");
+ }
+}
+
+function testTextHelper(aID, aOffset, aBoundaryType,
+ aText, aStartOffset, aEndOffset,
+ aToDoFlag1, aToDoFlag2, aToDoFlag3,
+ aTextFunc, aTextFuncName)
+{
+ var exceptionFlag = aToDoFlag1 == undefined ||
+ aToDoFlag2 == undefined ||
+ aToDoFlag3 == undefined;
+
+ var startMsg = aTextFuncName + "(" + boundaryToString(aBoundaryType) + "): ";
+ var endMsg = ", id: " + prettyName(aID) + ";";
+
+ try {
+ var startOffsetObj = {}, endOffsetObj = {};
+ var text = aTextFunc(aOffset, aBoundaryType,
+ startOffsetObj, endOffsetObj);
+
+ if (exceptionFlag) {
+ ok(false, startMsg + "no expected failure at offset " + aOffset + endMsg);
+ return;
+ }
+
+ var isFunc1 = (aToDoFlag1 == kTodo) ? todo : ok;
+ var isFunc2 = (aToDoFlag2 == kTodo) ? todo : ok;
+ var isFunc3 = (aToDoFlag3 == kTodo) ? todo : ok;
+
+ isFunc1(text == aText,
+ startMsg + "wrong text " +
+ "(got '" + text + "', expected: '" + aText + "')" +
+ ", offset: " + aOffset + endMsg);
+ isFunc2(startOffsetObj.value == aStartOffset,
+ startMsg + "wrong start offset" +
+ "(got '" + startOffsetObj.value + "', expected: '" + aStartOffset + "')" +
+ ", offset: " + aOffset + endMsg);
+ isFunc3(endOffsetObj.value == aEndOffset,
+ startMsg + "wrong end offset" +
+ "(got '" + endOffsetObj.value + "', expected: '" + aEndOffset + "')" +
+ ", offset: " + aOffset + endMsg);
+
+ } catch (e) {
+ var okFunc = exceptionFlag ? todo : ok;
+ okFunc(false, startMsg + "failed at offset " + aOffset + endMsg +
+ ", exception: " + e);
+ }
+}
+
+function boundaryToString(aBoundaryType)
+{
+ switch (aBoundaryType) {
+ case BOUNDARY_CHAR:
+ return "char";
+ case BOUNDARY_WORD_START:
+ return "word start";
+ case BOUNDARY_WORD_END:
+ return "word end";
+ case BOUNDARY_LINE_START:
+ return "line start";
+ case BOUNDARY_LINE_END:
+ return "line end";
+ }
+}
diff --git a/accessible/tests/mochitest/text/a11y.ini b/accessible/tests/mochitest/text/a11y.ini
new file mode 100644
index 000000000..96283a736
--- /dev/null
+++ b/accessible/tests/mochitest/text/a11y.ini
@@ -0,0 +1,16 @@
+[DEFAULT]
+support-files = doc.html
+ !/accessible/tests/mochitest/*.js
+
+[test_atcaretoffset.html]
+[test_charboundary.html]
+[test_doc.html]
+[test_dynamic.html]
+[test_general.xul]
+[test_gettext.html]
+[test_hypertext.html]
+[test_lineboundary.html]
+[test_passwords.html]
+[test_selection.html]
+[test_wordboundary.html]
+[test_words.html]
diff --git a/accessible/tests/mochitest/text/doc.html b/accessible/tests/mochitest/text/doc.html
new file mode 100644
index 000000000..d57406c22
--- /dev/null
+++ b/accessible/tests/mochitest/text/doc.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="application/javascript">
+ document.documentElement.appendChild(document.createTextNode("outbody"));
+ </script>
+</head>
+<body>inbody</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_atcaretoffset.html b/accessible/tests/mochitest/text/test_atcaretoffset.html
new file mode 100644
index 000000000..330298a62
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_atcaretoffset.html
@@ -0,0 +1,455 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test: nsIAccessibleText getText* functions at caret offset</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function traverseTextByLines(aQueue, aID, aLines)
+ {
+ var wholeText = "";
+ for (var i = 0; i < aLines.length ; i++)
+ wholeText += aLines[i][0] + aLines[i][1];
+
+ var baseInvokerFunc = synthClick;
+ var charIter = new charIterator(wholeText, aLines);
+ //charIter.debugOffset = 10; // enable to run tests at given offset only
+
+ while (charIter.next()) {
+ aQueue.push(new tmpl_moveTo(aID, baseInvokerFunc, wholeText, charIter));
+ baseInvokerFunc = synthRightKey;
+ }
+ }
+
+ /**
+ * Used to get test list for each traversed character.
+ */
+ function charIterator(aWholeText, aLines)
+ {
+ this.next = function charIterator_next()
+ {
+ // Don't increment offset if we are at end of the wrapped line
+ // (offset is shared between end of this line and start of next line).
+ if (this.mAtWrappedLineEnd) {
+ this.mAtWrappedLineEnd = false;
+ this.mLine = this.mLine.nextLine;
+ return true;
+ }
+
+ this.mOffset++;
+ if (this.mOffset > aWholeText.length)
+ return false;
+
+ var nextLine = this.mLine.nextLine;
+ if (!nextLine.isFakeLine() && this.mOffset == nextLine.start) {
+ if (nextLine.start == this.mLine.end)
+ this.mAtWrappedLineEnd = true;
+ else
+ this.mLine = nextLine;
+ }
+
+ return true;
+ }
+
+ Object.defineProperty(this, "offset", { get: function()
+ { return this.mOffset; }
+ });
+
+ Object.defineProperty(this, "offsetDescr", { get: function()
+ {
+ return this.mOffset + " offset (" + this.mLine.number + " line, " +
+ (this.mOffset - this.mLine.start) + " offset on the line)";
+ }
+ });
+
+ Object.defineProperty(this, "tests", { get: function()
+ {
+ // Line boundary tests.
+ var cLine = this.mLine;
+ var pLine = cLine.prevLine;
+ var ppLine = pLine.prevLine;
+ var nLine = cLine.nextLine;
+ var nnLine = nLine.nextLine;
+
+ var lineTests = [
+ [ testTextBeforeOffset, BOUNDARY_LINE_START, pLine.start, cLine.start],
+ [ testTextBeforeOffset, BOUNDARY_LINE_END, ppLine.end, pLine.end],
+ [ testTextAtOffset, BOUNDARY_LINE_START, cLine.start, nLine.start],
+ [ testTextAtOffset, BOUNDARY_LINE_END, pLine.end, cLine.end],
+ [ testTextAfterOffset, BOUNDARY_LINE_START, nLine.start, nnLine.start],
+ [ testTextAfterOffset, BOUNDARY_LINE_END, cLine.end, nLine.end]
+ ];
+
+ // Word boundary tests.
+ var cWord = this.mLine.firstWord;
+ var nWord = cWord.nextWord, pWord = cWord.prevWord;
+
+ // The current word is a farthest word starting at or after the offset.
+ if (this.mOffset >= nWord.start) {
+ while (this.mOffset >= nWord.start && !this.mLine.isLastWord(cWord)) {
+ cWord = nWord;
+ nWord = nWord.nextWord;
+ }
+ pWord = cWord.prevWord;
+
+ } else if (this.mOffset < cWord.start) {
+ while (this.mOffset < cWord.start) {
+ cWord = pWord;
+ pWord = pWord.prevWord;
+ }
+ nWord = cWord.nextWord;
+ }
+
+ var nnWord = nWord.nextWord, ppWord = pWord.prevWord;
+
+ var isAfterWordEnd =
+ this.mOffset > cWord.end || cWord.line != this.mLine;
+ var isAtOrAfterWordEnd = (this.mOffset >= cWord.end);
+ var useNextWordForAtWordEnd =
+ isAtOrAfterWordEnd && this.mOffset != aWholeText.length;
+
+ var wordTests = [
+ [ testTextBeforeOffset, BOUNDARY_WORD_START,
+ pWord.start, cWord.start ],
+ [ testTextBeforeOffset, BOUNDARY_WORD_END,
+ (isAfterWordEnd ? pWord : ppWord).end,
+ (isAfterWordEnd ? cWord : pWord).end ],
+ [ testTextAtOffset, BOUNDARY_WORD_START,
+ cWord.start, nWord.start ],
+ [ testTextAtOffset, BOUNDARY_WORD_END,
+ (useNextWordForAtWordEnd ? cWord : pWord).end,
+ (useNextWordForAtWordEnd ? nWord : cWord).end ],
+ [ testTextAfterOffset, BOUNDARY_WORD_START,
+ nWord.start, nnWord.start ],
+ [ testTextAfterOffset, BOUNDARY_WORD_END,
+ (isAfterWordEnd ? nWord : cWord).end,
+ (isAfterWordEnd ? nnWord : nWord).end ]
+ ];
+
+ // Character boundary tests.
+ var prevOffset = this.offset > 1 ? this.offset - 1 : 0;
+ var nextOffset = this.offset >= aWholeText.length ?
+ this.offset : this.offset + 1;
+ var nextAfterNextOffset = nextOffset >= aWholeText.length ?
+ nextOffset : nextOffset + 1;
+
+ var charTests = [
+ [ testTextBeforeOffset, BOUNDARY_CHAR,
+ prevOffset, this.offset ],
+ [ testTextAtOffset, BOUNDARY_CHAR,
+ this.offset,
+ this.mAtWrappedLineEnd ? this.offset : nextOffset ],
+ [ testTextAfterOffset, BOUNDARY_CHAR,
+ this.mAtWrappedLineEnd ? this.offset : nextOffset,
+ this.mAtWrappedLineEnd ? nextOffset : nextAfterNextOffset ]
+ ];
+
+ return lineTests.concat(wordTests.concat(charTests));
+ }
+ });
+
+ Object.defineProperty(this, "failures", { get: function()
+ {
+ if (this.mOffset == this.mLine.start)
+ return this.mLine.lineStartFailures;
+ if (this.mOffset == this.mLine.end)
+ return this.mLine.lineEndFailures;
+ return [];
+ }
+ });
+
+ this.mOffset = -1;
+ this.mLine = new line(aWholeText, aLines, 0);
+ this.mAtWrappedLineEnd = false;
+ this.mWord = this.mLine.firstWord;
+ }
+
+ /**
+ * A line object. Allows to navigate by lines and by words.
+ */
+ function line(aWholeText, aLines, aIndex)
+ {
+ Object.defineProperty(this, "prevLine", { get: function()
+ {
+ return new line(aWholeText, aLines, aIndex - 1);
+ }
+ });
+ Object.defineProperty(this, "nextLine", { get: function()
+ {
+ return new line(aWholeText, aLines, aIndex + 1);
+ }
+ });
+
+ Object.defineProperty(this, "start", { get: function()
+ {
+ if (aIndex < 0)
+ return 0;
+
+ if (aIndex >= aLines.length)
+ return aWholeText.length;
+
+ return aLines[aIndex][2];
+ }
+ });
+ Object.defineProperty(this, "end", { get: function()
+ {
+ if (aIndex < 0)
+ return 0;
+
+ if (aIndex >= aLines.length)
+ return aWholeText.length;
+
+ return aLines[aIndex][3];
+ }
+ });
+
+ Object.defineProperty(this, "number", { get: function()
+ { return aIndex; }
+ });
+ Object.defineProperty(this, "wholeText", { get: function()
+ { return aWholeText; }
+ });
+ this.isFakeLine = function line_isFakeLine()
+ {
+ return aIndex < 0 || aIndex >= aLines.length;
+ }
+
+ Object.defineProperty(this, "lastWord", { get: function()
+ {
+ if (aIndex < 0)
+ return new word(this, [], -1);
+ if (aIndex >= aLines.length)
+ return new word(this, [], 0);
+
+ var words = aLines[aIndex][4].words;
+ return new word(this, words, words.length - 2);
+ }
+ });
+ Object.defineProperty(this, "firstWord", { get: function()
+ {
+ if (aIndex < 0)
+ return new word(this, [], -1);
+ if (aIndex >= aLines.length)
+ return new word(this, [], 0);
+
+ var words = aLines[aIndex][4].words;
+ return new word(this, words, 0);
+ }
+ });
+
+ this.isLastWord = function line_isLastWord(aWord)
+ {
+ var lastWord = this.lastWord;
+ return lastWord.start == aWord.start && lastWord.end == aWord.end;
+ }
+
+ Object.defineProperty(this, "lineStartFailures", { get: function()
+ {
+ if (aIndex < 0 || aIndex >= aLines.length)
+ return [];
+
+ return aLines[aIndex][4].lsf || [];
+ }
+ });
+ Object.defineProperty(this, "lineEndFailures", { get: function()
+ {
+ if (aIndex < 0 || aIndex >= aLines.length)
+ return [];
+
+ return aLines[aIndex][4].lef || [];
+ }
+ });
+ }
+
+ /**
+ * A word object. Allows to navigate by words.
+ */
+ function word(aLine, aWords, aIndex)
+ {
+ Object.defineProperty(this, "prevWord", { get: function()
+ {
+ if (aIndex >= 2)
+ return new word(aLine, aWords, aIndex - 2);
+
+ var prevLineLastWord = aLine.prevLine.lastWord;
+ if (this.start == prevLineLastWord.start && !this.isFakeStartWord())
+ return prevLineLastWord.prevWord;
+ return prevLineLastWord;
+ }
+ });
+ Object.defineProperty(this, "nextWord", { get: function()
+ {
+ if (aIndex + 2 < aWords.length)
+ return new word(aLine, aWords, aIndex + 2);
+
+ var nextLineFirstWord = aLine.nextLine.firstWord;
+ if (this.end == nextLineFirstWord.end && !this.isFakeEndWord())
+ return nextLineFirstWord.nextWord;
+ return nextLineFirstWord;
+ }
+ });
+
+ Object.defineProperty(this, "line", { get: function() { return aLine; } });
+
+ Object.defineProperty(this, "start", { get: function()
+ {
+ if (this.isFakeStartWord())
+ return 0;
+
+ if (this.isFakeEndWord())
+ return aLine.end;
+ return aWords[aIndex];
+ }
+ });
+ Object.defineProperty(this, "end", { get: function()
+ {
+ if (this.isFakeStartWord())
+ return 0;
+
+ return this.isFakeEndWord() ? aLine.end : aWords[aIndex + 1];
+ }
+ });
+
+ this.toString = function word_toString()
+ {
+ var start = this.start, end = this.end;
+ return "'" + aLine.wholeText.substring(start, end) +
+ "' at [" + start + ", " + end + "]";
+ }
+
+ this.isFakeStartWord = function() { return aIndex < 0; }
+ this.isFakeEndWord = function() { return aIndex >= aWords.length; }
+ }
+
+ /**
+ * A template invoker to move through the text.
+ */
+ function tmpl_moveTo(aID, aInvokerFunc, aWholeText, aCharIter)
+ {
+ this.offset = aCharIter.offset;
+
+ var checker = new caretMoveChecker(this.offset, aID);
+ this.__proto__ = new (aInvokerFunc)(aID, checker);
+
+ this.finalCheck = function genericMoveTo_finalCheck()
+ {
+ if (this.noTests())
+ return;
+
+ for (var i = 0; i < this.tests.length; i++) {
+ var func = this.tests[i][0];
+ var boundary = this.tests[i][1];
+ var startOffset = this.tests[i][2];
+ var endOffset = this.tests[i][3];
+ var text = aWholeText.substring(startOffset, endOffset);
+
+ var isOk1 = kOk, isOk2 = kOk, isOk3 = kOk;
+ for (var fIdx = 0; fIdx < this.failures.length; fIdx++) {
+ var failure = this.failures[fIdx];
+ if (func.name.indexOf(failure[0]) != -1 && boundary == failure[1]) {
+ isOk1 = failure[2];
+ isOk2 = failure[3];
+ isOk3 = failure[4];
+ }
+ }
+
+ func.call(null, kCaretOffset, boundary, text, startOffset, endOffset,
+ aID, isOk1, isOk2, isOk3);
+ }
+ }
+
+ this.getID = function genericMoveTo_getID()
+ {
+ return "move to " + this.offsetDescr;
+ }
+
+ this.noTests = function tmpl_moveTo_noTests()
+ {
+ return ("debugOffset" in aCharIter) &&
+ (aCharIter.debugOffset != this.offset);
+ }
+
+ this.offsetDescr = aCharIter.offsetDescr;
+ this.tests = this.noTests() ? null : aCharIter.tests;
+ this.failures = aCharIter.failures;
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ // __a__w__o__r__d__\n
+ // 0 1 2 3 4 5
+ // __t__w__o__ (soft line break)
+ // 6 7 8 9
+ // __w__o__r__d__s
+ // 10 11 12 13 14 15
+
+ traverseTextByLines(gQueue, "textarea",
+ [ [ "aword", "\n", 0, 5, { words: [ 0, 5 ] } ],
+ [ "two ", "", 6, 10, { words: [ 6, 9 ] } ],
+ [ "words", "", 10, 15, { words: [ 10, 15 ] } ]
+ ] );
+
+ var line4 = [ // "riend "
+ [ "TextBeforeOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ],
+ [ "TextAfterOffset", BOUNDARY_WORD_END, kTodo, kTodo, kTodo ]
+ ];
+ traverseTextByLines(gQueue, "ta_wrapped",
+ [ [ "hi ", "", 0, 3, { words: [ 0, 2 ] } ],
+ [ "hello ", "", 3, 9, { words: [ 3, 8 ] } ],
+ [ "my ", "", 9, 12, { words: [ 9, 11 ] } ],
+ [ "longf", "", 12, 17, { words: [ 12, 17 ] } ],
+ [ "riend ", "", 17, 23, { words: [ 17, 22 ], lsf: line4 } ],
+ [ "t sq ", "", 23, 28, { words: [ 23, 24, 25, 27 ] } ],
+ [ "t", "", 28, 29, { words: [ 28, 29 ] } ]
+ ] );
+
+ gQueue.invoke(); // will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="nsIAccessibleText getText related functions tests at caret offset"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=852021">
+ Bug 852021
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+
+ <textarea id="textarea" cols="5">aword
+two words</textarea>
+
+ <textarea id="ta_wrapped" cols="5">hi hello my longfriend t sq t</textarea>
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_charboundary.html b/accessible/tests/mochitest/text/test_charboundary.html
new file mode 100644
index 000000000..2fddcb5be
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_charboundary.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Char boundary text tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ var IDs = [ "i1", "d1", "e1", "t1" ];
+
+ testCharBeforeOffset(IDs, 0, "", 0, 0);
+ testCharBeforeOffset(IDs, 1, "h", 0, 1);
+ testCharBeforeOffset(IDs, 14, "n", 13, 14);
+ testCharBeforeOffset(IDs, 15, "d", 14, 15);
+
+ testCharAtOffset(IDs, 0, "h", 0, 1);
+ testCharAtOffset(IDs, 1, "e", 1, 2);
+ testCharAtOffset(IDs, 14, "d", 14, 15);
+ testCharAtOffset(IDs, 15, "", 15, 15);
+
+ testCharAfterOffset(IDs, 0, "e", 1, 2);
+ testCharAfterOffset(IDs, 1, "l", 2, 3);
+ testCharAfterOffset(IDs, 14, "", 15, 15);
+ testCharAfterOffset(IDs, 15, "", 15, 15);
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
+
+ IDs = [ "i2", "d2", "e2", "t2" ];
+
+ testCharBeforeOffset(IDs, 0, "", 0, 0);
+ testCharBeforeOffset(IDs, 1, "B", 0, 1);
+ testCharBeforeOffset(IDs, 6, " ", 5, 6);
+ testCharBeforeOffset(IDs, 10, " ", 9, 10);
+ testCharBeforeOffset(IDs, 11, " ", 10, 11);
+ testCharBeforeOffset(IDs, 17, " ", 16, 17);
+ testCharBeforeOffset(IDs, 19, " ", 18, 19);
+
+ testCharAtOffset(IDs, 0, "B", 0, 1);
+ testCharAtOffset(IDs, 1, "r", 1, 2);
+ testCharAtOffset(IDs, 5, " ", 5, 6);
+ testCharAtOffset(IDs, 9, " ", 9, 10);
+ testCharAtOffset(IDs, 10, " ", 10, 11);
+ testCharAtOffset(IDs, 17, " ", 17, 18);
+
+ testCharAfterOffset(IDs, 0, "r", 1, 2);
+ testCharAfterOffset(IDs, 1, "a", 2, 3);
+ testCharAfterOffset(IDs, 4, " ", 5, 6);
+ testCharAfterOffset(IDs, 5, "S", 6, 7);
+ testCharAfterOffset(IDs, 8, " ", 9, 10);
+ testCharAfterOffset(IDs, 9, " ", 10, 11);
+ testCharAfterOffset(IDs, 10, "R", 11, 12);
+ testCharAfterOffset(IDs, 15, " ", 16, 17);
+ testCharAfterOffset(IDs, 16, " ", 17, 18);
+ testCharAfterOffset(IDs, 17, " ", 18, 19);
+ testCharAfterOffset(IDs, 18, "r", 19, 20);
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __o__n__e__w__o__r__d__\n
+ // 0 1 2 3 4 5 6 7
+ // __\n
+ // 8
+ // __t__w__o__ __w__o__r__d__s__\n
+ // 9 10 11 12 13 14 15 16 17 18
+
+ IDs = ["d3", "dbr3", "e3", "ebr3", "t3"];
+
+ testCharBeforeOffset(IDs, 8, "\n", 7, 8);
+ testCharBeforeOffset(IDs, 9, "\n", 8, 9);
+ testCharBeforeOffset(IDs, 10, "t", 9, 10);
+
+ testCharAtOffset(IDs, 7, "\n", 7, 8);
+ testCharAtOffset(IDs, 8, "\n", 8, 9);
+ testCharAtOffset(IDs, 9, "t", 9, 10);
+
+ testCharAfterOffset(IDs, 6, "\n", 7, 8);
+ testCharAfterOffset(IDs, 7, "\n", 8, 9);
+ testCharAfterOffset(IDs, 8, "t", 9, 10);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="i1" value="hello my friend"/>
+ <div id="d1">hello my friend</div>
+ <div id="e1" contenteditable="true">hello my friend</div>
+ <textarea id="t1" contenteditable="true">hello my friend</textarea>
+
+ <input id="i2" value="Brave Sir Robin ran"/>
+ <pre>
+ <div id="d2">Brave Sir Robin ran</div>
+ <div id="e2" contenteditable="true">Brave Sir Robin ran</div>
+ </pre>
+ <textarea id="t2" cols="300">Brave Sir Robin ran</textarea>
+
+ <pre>
+ <div id="d3">oneword
+
+two words
+</div>
+ <div id="dbr3">oneword<br/><br/>two words<br/></div>
+ <div id="e3" contenteditable="true">oneword
+
+two words
+</div>
+ <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/></div>
+ <textarea id="t3" cols="300">oneword
+
+two words</textarea>
+ </pre>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_doc.html b/accessible/tests/mochitest/text/test_doc.html
new file mode 100644
index 000000000..9c6788275
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_doc.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleText getText related function tests for document accessible</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ var iframeDoc = [ getNode("iframe").contentDocument ];
+ testCharacterCount(iframeDoc, 15);
+ testText(iframeDoc, 0, 15, "outbody inbody ");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Elements appended outside the body aren't accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe id="iframe" src="doc.html"></iframe>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_dynamic.html b/accessible/tests/mochitest/text/test_dynamic.html
new file mode 100644
index 000000000..0e5d4394a
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_dynamic.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleText getText related function tests for tree mutations</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function insertBefore(aId, aEl, aTextBefore, aTextAfter, aStartIdx, aEndIdx)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aId)
+ ];
+
+ this.invoke = function insertBefore_invoke()
+ {
+ testText(aId, 0, -1, aTextBefore);
+ getNode(aId).insertBefore(aEl, getNode(aId).firstChild);
+ }
+
+ this.finalCheck = function insertBefore_finalCheck()
+ {
+ testText(aId, aStartIdx, aEndIdx, aTextAfter);
+ }
+
+ this.getID = function insertTextBefore_getID() {
+ return "insert " + prettyName(aEl) + " before";
+ }
+ }
+
+ function insertTextBefore(aId, aTextBefore, aText)
+ {
+ var el = document.createTextNode(aText);
+ this.__proto__ = new insertBefore(aId, el, aTextBefore,
+ aText + aTextBefore, 0, -1)
+ }
+
+ function insertImgBefore(aId, aTextBefore)
+ {
+ var el = document.createElement("img");
+ el.setAttribute("src", "../moz.png");
+ el.setAttribute("alt", "mozilla");
+
+ this.__proto__ = new insertBefore(aId, el, aTextBefore,
+ kEmbedChar + aTextBefore, 0, -1)
+ }
+
+ function insertTextBefore2(aId)
+ {
+ var el = document.createTextNode("hehe");
+ this.__proto__ = new insertBefore(aId, el, "ho", "ho", 4, -1)
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new insertTextBefore("c1", "ho", "ha"));
+ gQueue.push(new insertImgBefore("c1", "haho"));
+ gQueue.push(new insertImgBefore("c2", kEmbedChar));
+ gQueue.push(new insertTextBefore2("c3"));
+ gQueue.invoke(); // will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1">ho</div>
+ <div id="c2"><img src="../moz.png" alt="mozilla"></div>
+ <div id="c3">ho</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_general.xul b/accessible/tests/mochitest/text/test_general.xul
new file mode 100644
index 000000000..0bd720e28
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_general.xul
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tests: XUL label text interface">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Testing
+
+ var gQueue = null;
+ function doTests()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // XUL label
+
+ var ids = ["label1", "label2"];
+
+ testCharacterCount(ids, 5);
+
+ testText(ids, 0, -1, "Hello");
+ testText(ids, 0, 1, "H");
+
+ testCharAfterOffset(ids, 0, "e", 1, 2);
+ testCharBeforeOffset(ids, 1, "H", 0, 1);
+ testCharAtOffset(ids, 1, "e", 1, 2);
+
+ //////////////////////////////////////////////////////////////////////////
+ // XUL textbox
+
+ testTextAtOffset([ getNode("tbox1").inputField ], BOUNDARY_LINE_START,
+ [ [ 0, 4, "test", 0, 4 ] ]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=396166"
+ title="xul:label@value accessible should implement nsIAccessibleText">
+ Bug 396166
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=899433"
+ title="Accessibility returns empty line for last line in certain cases">
+ Bug 899433
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+ <label id="label1" value="Hello"/>
+ <label id="label2">Hello</label>
+
+ <textbox id="tbox1" value="test" multiline="true"/>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/text/test_gettext.html b/accessible/tests/mochitest/text/test_gettext.html
new file mode 100644
index 000000000..303edc58a
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_gettext.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Get text between offsets tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ var IDs = [ "i1", "d1", "e1", "t1" ];
+
+ testCharacterCount(IDs, 15);
+
+ testText(IDs, 0, 1, "h");
+ testText(IDs, 1, 3, "el");
+ testText(IDs, 14, 15, "d");
+ testText(IDs, 0, 15, "hello my friend");
+ testText(IDs, 0, -1, "hello my friend");
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
+
+ IDs = [ "i2", "dpre2", "epre2", "t2" ];
+
+ testCharacterCount(IDs, 22);
+
+ testText(IDs, 0, 1, "B");
+ testText(IDs, 5, 6, " ");
+ testText(IDs, 9, 11, " ");
+ testText(IDs, 16, 19, " ");
+ testText(IDs, 0, 22, "Brave Sir Robin ran");
+ testText(IDs, 0, -1, "Brave Sir Robin ran");
+
+ testCharacterCount(["d2", "e2"], 19);
+ testText(["d2", "e2"], 0, 19, "Brave Sir Robin ran");
+
+ //////////////////////////////////////////////////////////////////////////
+ //
+ // __o__n__e__w__o__r__d__\n
+ // 0 1 2 3 4 5 6 7
+ // __\n
+ // 8
+ // __t__w__o__ __w__o__r__d__s__\n
+ // 9 10 11 12 13 14 15 16 17 18
+
+ var IDs = ["d3", "dbr3", "e3", "ebr3", "t3"];
+
+ testCharacterCount(IDs, 19);
+
+ testText(IDs, 0, 19, "oneword\n\ntwo words\n");
+ testText(IDs, 0, -1, "oneword\n\ntwo words\n");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="i1" value="hello my friend"/>
+ <div id="d1">hello my friend</div>
+ <div id="e1" contenteditable="true">hello my friend</div>
+ <textarea id="t1">hello my friend</textarea>
+
+ <input id="i2" value="Brave Sir Robin ran"/>
+ <pre><div id="dpre2">Brave Sir Robin ran</div></pre>
+ <pre><div id="epre2" contenteditable="true">Brave Sir Robin ran</div></pre>
+ <textarea id="t2" cols="300">Brave Sir Robin ran</textarea>
+ <div id="d2">Brave Sir Robin ran</div>
+ <div id="e2" contenteditable="true">Brave Sir Robin ran</div>
+
+ <pre>
+ <div id="d3">oneword
+
+two words
+</div>
+ <div id="dbr3">oneword<br/><br/>two words<br/><br/></div>
+ <div id="e3" contenteditable="true">oneword
+
+two words
+</div>
+ <div id="ebr3" contenteditable="true">oneword<br/><br/>two words<br/><br/></div>
+ <textarea id="t3" cols="300">oneword
+
+two words
+</textarea>
+ </pre>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_hypertext.html b/accessible/tests/mochitest/text/test_hypertext.html
new file mode 100644
index 000000000..2d71e11a9
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_hypertext.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleText getText related function tests for rich text</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ #listitemnone {
+ list-style-type: none;
+ }
+ h6.gencontent:before {
+ content: "aga"
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // null getText
+ //////////////////////////////////////////////////////////////////////////
+
+ var emptyTextAcc = getAccessible("nulltext", [nsIAccessibleText]);
+ is(emptyTextAcc.getText(0, -1), "", "getText() END_OF_TEXT with null string");
+ is(emptyTextAcc.getText(0, 0), "", "getText() Len==0 with null string");
+
+ //////////////////////////////////////////////////////////////////////////
+ // hypertext
+ //////////////////////////////////////////////////////////////////////////
+
+ // ! - embedded object char
+ // __h__e__l__l__o__ __!__ __s__e__e__ __!__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+
+ var IDs = [ "hypertext", "hypertext2" ];
+
+ ////////////////////////////////////////////////////////////////////////
+ // characterCount
+
+ testCharacterCount(IDs, 13);
+
+ ////////////////////////////////////////////////////////////////////////
+ // getText
+
+ testText(IDs, 0, 1, "h");
+ testText(IDs, 5, 7, " " + kEmbedChar);
+ testText(IDs, 10, 13, "e " + kEmbedChar);
+ testText(IDs, 0, 13, "hello " + kEmbedChar + " see " + kEmbedChar);
+
+ ////////////////////////////////////////////////////////////////////////
+ // getTextAtOffset line boundary
+
+ testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
+ "hypertext3", kOk, kOk, kOk);
+
+ // XXX: see bug 634202.
+ testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
+ "hypertext4", kTodo, kOk, kTodo);
+
+ //////////////////////////////////////////////////////////////////////////
+ // list
+ //////////////////////////////////////////////////////////////////////////
+
+ IDs = [ "list" ];
+ testCharacterCount(IDs, 2);
+ testText(IDs, 0, 2, kEmbedChar + kEmbedChar);
+
+ IDs = [ "listitem" ];
+ testCharacterCount(IDs, 6);
+ testText(IDs, 0, 6, "1. foo");
+
+ IDs = [ "listitemnone" ];
+ testCharacterCount(IDs, 3);
+ testText(IDs, 0, 3, "bar");
+
+ testText(["testbr"], 0, 3, "foo");
+
+ testTextAtOffset(2, nsIAccessibleText.BOUNDARY_CHAR, "o", 2, 3, "testbr",
+ kOk, kOk, kOk);
+ testTextAtOffset(2, nsIAccessibleText.BOUNDARY_WORD_START, "foo\n", 0, 4,
+ "testbr", kTodo, kOk, kTodo);
+ testTextBeforeOffset(2, nsIAccessibleText.BOUNDARY_LINE_START, "foo\n",
+ 0, 4, "testbr", kTodo, kOk, kTodo);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix getText"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630001">
+ Bug 630001, part3
+ </a>
+ <a target="_blank"
+ title="getTextAtOffset line boundary may return more than one line"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=638326">
+ Bug 638326
+ </a>
+ <a target="_blank"
+ title="getText(0, -1) fails with empty text"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=749810">
+ Bug 749810
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="nulltext"></div>
+
+ <div id="hypertext">hello <a>friend</a> see <img src="about:blank"></div>
+ <div id="hypertext2">hello <a>friend</a> see <input></div>
+ <ol id="list">
+ <li id="listitem">foo</li>
+ <li id="listitemnone">bar</li>
+ </ol>
+
+ <div id="hypertext3">line
+<!-- haha -->
+<!-- hahaha -->
+<h6>heading</h6>
+ </div>
+
+ <div id="hypertext4">line
+<!-- haha -->
+<!-- hahaha -->
+<h6 role="presentation" class="gencontent">heading</h6>
+ </div>
+
+ <div id="testbr">foo<br/></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_lineboundary.html b/accessible/tests/mochitest/text/test_lineboundary.html
new file mode 100644
index 000000000..8370d25d0
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_lineboundary.html
@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Line boundary getText* functions tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript">
+ function doTest()
+ {
+ testTextAtOffset("line_test_1", BOUNDARY_LINE_START,
+ [[0, 6, "Line 1 ", 0, 7],
+ [7, 7, "", 7, 7],
+ [8, 15, "Line 3 ", 8, 15]]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+
+ var IDs = [ "input", "div", "editable", "textarea",
+ getNode("ta", getNode("ta_cntr").contentDocument) ];
+
+ testTextBeforeOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 15, "", 0, 0 ] ]);
+ testTextBeforeOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 15, "", 0, 0 ] ]);
+
+ testTextAtOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 15, "hello my friend", 0, 15 ] ]);
+ testTextAtOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 15, "hello my friend", 0, 15 ] ]);
+
+ testTextAfterOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 15, "", 15, 15 ] ]);
+ testTextAfterOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 15, "", 15, 15 ] ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // __o__n__e__w__o__r__d__\n
+ // 0 1 2 3 4 5 6 7
+ // __\n
+ // 8
+ // __t__w__o__ __w__o__r__d__s__\n
+ // 9 10 11 12 13 14 15 16 17 18
+
+ IDs = [ "ml_div", "ml_divbr", "ml_editable", "ml_editablebr", "ml_textarea"];
+
+ testTextBeforeOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 7, "", 0, 0 ],
+ [ 8, 8, "oneword\n", 0, 8 ],
+ [ 9, 18, "\n", 8, 9 ],
+ [ 19, 19, "two words\n", 9, 19 ]]);
+
+ testTextBeforeOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 7, "", 0, 0 ],
+ [ 8, 8, "oneword", 0, 7 ],
+ [ 9, 18, "\n", 7, 8 ],
+ [ 19, 19, "\ntwo words", 8, 18 ]]);
+
+ testTextAtOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 7, "oneword\n", 0, 8 ],
+ [ 8, 8, "\n", 8, 9 ],
+ [ 9, 18, "two words\n", 9, 19 ],
+ [ 19, 19, "", 19, 19 ]]);
+ testTextAtOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 7, "oneword", 0, 7 ],
+ [ 8, 8, "\n", 7, 8 ],
+ [ 9, 18, "\ntwo words", 8, 18 ],
+ [ 19, 19, "\n", 18, 19 ]]);
+
+ testTextAfterOffset(IDs, BOUNDARY_LINE_START,
+ [ [ 0, 7, "\n", 8, 9 ],
+ [ 8, 8, "two words\n", 9, 19 ],
+ [ 9, 19, "", 19, 19 ]]);
+ testTextAfterOffset(IDs, BOUNDARY_LINE_END,
+ [ [ 0, 7, "\n", 7, 8 ],
+ [ 8, 8, "\ntwo words", 8, 18 ],
+ [ 9, 18, "\n", 18, 19 ],
+ [ 19, 19, "", 19, 19 ]]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // a * b (* is embedded char for link)
+ testTextBeforeOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START,
+ [ [ 0, 5, "", 0, 0 ] ]);
+
+ testTextBeforeOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END,
+ [ [ 0, 5, "", 0, 0 ] ]);
+
+ testTextAtOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START,
+ [ [ 0, 5, "a " + kEmbedChar + " c", 0, 5 ] ]);
+
+ testTextAtOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END,
+ [ [ 0, 5, "a " + kEmbedChar + " c", 0, 5 ] ]);
+
+ testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_START,
+ [ [ 0, 5, "", 5, 5 ] ]);
+
+ testTextAfterOffset([ getAccessible("ht_1").firstChild ], BOUNDARY_LINE_END,
+ [ [ 0, 5, "", 5, 5 ] ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // foo<br> and foo<br><br>
+
+ testTextAtOffset([ getAccessible("ht_2").firstChild.firstChild ],
+ BOUNDARY_LINE_START,
+ [ [ 0, 3, "foo", 0, 3 ] ]);
+ testTextAtOffset([ getAccessible("ht_3").firstChild.firstChild ],
+ BOUNDARY_LINE_START,
+ [ [ 0, 3, "foo\n", 0, 4 ], [ 4, 4, "", 4, 4 ] ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // 'Hello world ' (\n is rendered as space)
+
+ testTextAtOffset([ "ht_4" ], BOUNDARY_LINE_START,
+ [ [ 0, 12, "Hello world ", 0, 12 ] ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // list items
+
+ testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
+ [ [ 0, 6, kDiscBulletText + "Item", 0, 6 ] ]);
+ testTextAtOffset([ "li2" ], BOUNDARY_LINE_START,
+ [ [ 0, 2, kDiscBulletText, 0, 2 ] ]);
+ testTextAtOffset([ "li3" ], BOUNDARY_LINE_START,
+ [ [ 0, 8, kDiscBulletText + "a long ", 0, 9 ],
+ [ 9, 12, "and ", 9, 13 ] ]);
+ testTextAtOffset([ "li4" ], BOUNDARY_LINE_START,
+ [ [ 0, 7, kDiscBulletText + "a " + kEmbedChar + " c", 0, 7 ] ]);
+ testTextAtOffset([ "li5" ], BOUNDARY_LINE_START,
+ [ [ 0, 2, kDiscBulletText + "\n", 0, 3 ],
+ [ 3, 7, "hello", 3, 8 ] ]);
+ testTextAtOffset([ "ul1" ], BOUNDARY_LINE_START,
+ [ [ 0, 0, kEmbedChar, 0, 1 ],
+ [ 1, 1, kEmbedChar, 1, 2 ],
+ [ 2, 2, kEmbedChar, 2, 3 ],
+ [ 3, 3, kEmbedChar, 3, 4 ],
+ [ 4, 5, kEmbedChar, 4, 5 ] ]);
+
+ testTextAtOffset([ "li6" ], BOUNDARY_LINE_START,
+ [ [ 0, 7, "1. Item", 0, 7 ] ]);
+ testTextAtOffset([ "li7" ], BOUNDARY_LINE_START,
+ [ [ 0, 3, "2. ", 0, 3 ] ]);
+ testTextAtOffset([ "li8" ], BOUNDARY_LINE_START,
+ [ [ 0, 9, "3. a long ", 0, 10 ],
+ [ 10, 13, "and ", 10, 14 ] ]);
+ testTextAtOffset([ "li9" ], BOUNDARY_LINE_START,
+ [ [ 0, 8, "4. a " + kEmbedChar + " c", 0, 8 ] ]);
+ testTextAtOffset([ "li10" ], BOUNDARY_LINE_START,
+ [ [ 0, 3, "5. \n", 0, 4 ],
+ [ 4, 8, "hello", 4, 9 ] ]);
+ testTextAtOffset([ "ol1" ], BOUNDARY_LINE_START,
+ [ [ 0, 0, kEmbedChar, 0, 1 ],
+ [ 1, 1, kEmbedChar, 1, 2 ],
+ [ 2, 2, kEmbedChar, 2, 3 ],
+ [ 3, 3, kEmbedChar, 3, 4 ],
+ [ 4, 5, kEmbedChar, 4, 5 ] ]);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Nested hypertexts
+
+ testTextAtOffset(["ht_5" ], BOUNDARY_LINE_START,
+ [ [ 0, 0, kEmbedChar, 0, 1 ] ]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="getTextAtOffset for word boundaries: beginning of a new life"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+ Bug 853340
+ </a>
+ <a target="_blank"
+ title="getTextBeforeOffset for word boundaries: evolving"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+ Bug 855732
+ </a>
+ <a target="_blank"
+ title=" getTextAfterOffset for line boundary on new rails"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292">
+ Bug 882292
+ </a>
+ <a target="_blank"
+ title="getTextAtOffset broken for last object when closing tag is preceded by newline char"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=947170">
+ Bug 947170
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input" value="hello my friend"/>
+ <div id="div">hello my friend</div>
+ <div id="editable" contenteditable="true">hello my friend</div>
+ <textarea id="textarea">hello my friend</textarea>
+ <iframe id="ta_cntr"
+ src="data:text/html,<html><body><textarea id='ta'>hello my friend</textarea></body></html>"></iframe>
+
+ <pre>
+ <div id="ml_div" style="border-style:outset;">oneword
+
+two words
+</div>
+ <div id="ml_divbr" style="border-style:outset;">oneword<br/><br/>two words<br/><br/></div>
+ <div id="ml_editable" style="border-style:outset;" contenteditable="true">oneword
+
+two words
+</div>
+ <div id="ml_editablebr" contenteditable="true" style="border-style:outset;">oneword<br/><br/>two words<br/><br/></div>
+ <textarea id="ml_textarea" cols="300">oneword
+
+two words
+</textarea>
+ </pre>
+
+ <iframe id="ht_1" src="data:text/html,<html><body>a <a href=''>b</a> c</body></html>"></iframe>
+
+ <iframe id="ht_2" src="data:text/html,<div contentEditable='true'>foo<br/></div>"></iframe>
+ <iframe id="ht_3" src="data:text/html,<div contentEditable='true'>foo<br/><br/></div>"></iframe>
+
+ <p id="ht_4">Hello world
+</p>
+
+ <ul id="ul1">
+ <li id="li1">Item</li>
+ <li id="li2"></li>
+ <li id="li3" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li>
+ <li id="li4">a <a href=''>b</a> c</li>
+ <li id="li5"><br>hello</li>
+ </ul>
+
+ <ol id="ol1">
+ <li id="li6">Item</li>
+ <li id="li7"></li>
+ <li id="li8" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li>
+ <li id="li9">a <a href=''>b</a> c</li>
+ <li id="li10"><br>hello</li>
+ </ol>
+
+ <div id="ht_5">
+ <div>
+ <p>sectiounus</p>
+ <p>seciofarus</p>
+ </div>
+ </div>
+ <div id="line_test_1">
+ Line 1
+ <center><input type="TEXT"><input value="Button" type="SUBMIT"></center>
+ Line 3
+ </div>
+ </body>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_passwords.html b/accessible/tests/mochitest/text/test_passwords.html
new file mode 100644
index 000000000..8a47b5944
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_passwords.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleText getText related function tests for text and password inputs</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // regular text and password inputs
+ //////////////////////////////////////////////////////////////////////////
+
+ ////////////////////////////////////////////////////////////////////////
+ // characterCount and getText for regular text field
+
+ var IDs = [ "username" ];
+ testCharacterCount(IDs, 4);
+ testText(IDs, 0, 4, "test");
+
+ ////////////////////////////////////////////////////////////////////////
+ // characterCount and getText for password field
+
+ IDs = [ "password" ];
+ testCharacterCount(IDs, 4);
+ testPasswordText(IDs, 0, 4, "test");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="mochitest for getText for password fields"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=415943">Mozilla Bug 415943</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <form action="post.php" method="post">
+ <label for="username">User name:</label>
+ <input id="username" value="test"><br />
+ <label for="password">Password:</label>
+ <input type="password" id="password" value="test"/>
+ </form>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_selection.html b/accessible/tests/mochitest/text/test_selection.html
new file mode 100644
index 000000000..b832231cf
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_selection.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test text selection functions</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // Test selection count: clean selection / check count.
+ testTextAddSelection("div0", 0, 2, 1); // |Test selection...
+ cleanTextSelections("div0");
+ testTextSelectionCount("div0", 0);
+
+ // Test addition: adding two equal selections, the second one should
+ // not be added.
+ testTextAddSelection("div1", 7, 9, 1); // Test ad|di|ng two...
+ testTextAddSelection("div1", 7, 9, 1); // Test ad|di|ng two...
+ testTextGetSelection("div1", 7, 9, 0);
+
+ // Test overlapping selections: adding three selections, one adjacent.
+ testTextAddSelection("div2", 0, 3, 1); // |Tes|t adding 3...
+ testTextAddSelection("div2", 7, 9, 2); // |Tes|t ad|di|ng 3...
+ testTextAddSelection("div2", 3, 4, 3); // |Tes||t| ad|di|ng 3...
+ testTextGetSelection("div2", 0, 3, 0);
+ testTextGetSelection("div2", 3, 4, 1);
+ testTextGetSelection("div2", 7, 9, 2);
+
+ // Test selection re-ordering: adding two selections.
+ // NOTE: removeSelections aSelectionIndex is from start of document.
+ testTextAddSelection("div3", 0, 3, 1); // |Tes|t adding 2...
+ testTextAddSelection("div3", 7, 9, 2); // |Tes|t ad|di|ng 2...
+ testTextRemoveSelection("div3", 4, 1); // Test ad|di|ng 2...
+
+ // Test extending existing selection.
+ // NOTE: setSelectionBounds aSelectionIndex is from start of document.
+ testTextAddSelection("div4", 4, 5, 1); // Test| |extending...
+ testTextSetSelection("div4", 4, 9, 6, 1); // Test| exte|nding...
+
+ // Test moving an existing selection.
+ // NOTE: setSelectionBounds aSelectionIndex is from start of document.
+ testTextAddSelection("div5", 1, 3, 1); // T|es|t moving...
+ testTextSetSelection("div5", 5, 9, 6, 1); // Test |movi|ng...
+
+ // Test adding selections to multiple inner elements.
+ testTextAddSelection("div71", 0, 3, 1); // |Tes|t adding...
+ testTextAddSelection("div71", 7, 8, 2); // |Tes|t ad|d|ing...
+ testTextAddSelection("div72", 4, 6, 1); // Test| a|dding...
+ testTextAddSelection("div72", 7, 8, 2); // Test| a|d|d|ing...
+
+ // Test adding selection to parent element.
+ // NOTE: If inner elements are represented as embedded chars
+ // we count their internal selections.
+ testTextAddSelection("div7", 7, 8, 5); // Test ad|d|ing...
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+</script>
+</head>
+
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="div0">Test selection count</div>
+ </br>
+ <div id="div1">Test adding two equal selections </div>
+ <div id="div2">Test adding 3 selections one adjacent </div>
+ <div id="div3">Test adding 2 selections, remove first one </div>
+ <div id="div4">Test extending a selection </div>
+ <div id="div5">Test moving a selection </div>
+ </br>
+ <div id="div7">Test adding selections to parent element
+ <div id="div71">Test adding selections to inner element1 </div>
+ <div id="div72">Test adding selections to inner element2 </div>
+ </div>
+
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/text/test_wordboundary.html b/accessible/tests/mochitest/text/test_wordboundary.html
new file mode 100644
index 000000000..6d3c09e8c
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_wordboundary.html
@@ -0,0 +1,291 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Word boundary text tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // "hello"
+ // __h__e__l__l__o__
+ // 0 1 2 3 4 5
+ var ids = [ "i1", "d1", "e1", "t1" ];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "", 0, 0 ] ]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 0, 0 ] ]);
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "hello", 0, 5 ] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "hello", 0, 5 ] ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "", 5, 5 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 5, 5 ] ]);
+
+ // "hello "
+ // __h__e__l__l__o__ __
+ // 0 1 2 3 4 5 6
+ var ids = [ "i2", "d2", "e2", "t2" ];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 6, "", 0, 0 ] ]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 6, "hello", 0, 5 ]
+ ]);
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 6, "hello ", 0, 6 ] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 4, "hello", 0, 5 ],
+ [ 5, 6, " ", 5, 6 ]
+ ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 6, "", 6, 6 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, " ", 5, 6 ],
+ [ 6, 6, "", 6, 6 ]
+ ]);
+
+ // "hello all"
+ // __h__e__l__l__o__ __a__l__l__
+ // 0 1 2 3 4 5 6 7 8 9
+ ids = [ "i6", "d6", "e6", "t6" ];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 9, "hello ", 0, 6 ]]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 9, "hello", 0, 5 ] ]);
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "hello ", 0, 6 ],
+ [ 6, 9, "all", 6, 9 ] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 4, "hello", 0, 5 ],
+ [ 5, 9, " all", 5, 9 ] ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "all", 6, 9 ],
+ [ 6, 9, "", 9, 9 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, " all", 5, 9 ],
+ [ 6, 9, "", 9, 9 ] ]);
+
+ // "hello my friend"
+ // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ ids = [ "i7", "d7", "e7", "t7" ];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 8, "hello ", 0, 6 ],
+ [ 9, 15, "my ", 6, 9 ] ]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 8, "hello", 0, 5 ],
+ [ 9, 15, " my", 5, 8 ] ]);
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "hello ", 0, 6 ],
+ [ 6, 8, "my ", 6, 9 ],
+ [ 9, 15, "friend", 9, 15] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 4, "hello", 0, 5 ],
+ [ 5, 7, " my", 5, 8 ],
+ [ 8, 15, " friend", 8, 15] ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "my ", 6, 9 ],
+ [ 6, 8, "friend", 9, 15 ],
+ [ 9, 15, "", 15, 15 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, " my", 5, 8 ],
+ [ 6, 8, " friend", 8, 15 ],
+ [ 9, 15, "", 15, 15 ] ]);
+
+ // "Brave Sir Robin ran"
+ // __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n__
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ ids = [ "i8", "d8", "e8", "t8" ];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 10, "Brave ", 0, 6 ],
+ [ 11, 18, "Sir ", 6, 11 ],
+ [ 19, 22, "Robin ", 11, 19 ] ]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, "", 0, 0 ],
+ [ 6, 9, "Brave", 0, 5 ],
+ [ 10, 16, " Sir", 5, 9 ],
+ [ 17, 22, " Robin", 9, 16 ] ]);
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "Brave ", 0, 6 ],
+ [ 6, 10, "Sir ", 6, 11 ],
+ [ 11, 18, "Robin ", 11, 19 ],
+ [ 19, 22, "ran", 19, 22 ] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 4, "Brave", 0, 5 ],
+ [ 5, 8, " Sir", 5, 9 ],
+ [ 9, 15, " Robin", 9, 16 ],
+ [ 16, 22, " ran", 16, 22 ] ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 5, "Sir ", 6, 11 ],
+ [ 6, 10, "Robin ", 11, 19 ],
+ [ 11, 18, "ran", 19, 22 ],
+ [ 19, 22, "", 22, 22 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 5, " Sir", 5, 9 ],
+ [ 6, 9, " Robin", 9, 16 ],
+ [ 10, 16, " ran", 16, 22 ],
+ [ 17, 22, "", 22, 22 ] ]);
+
+ // 'oneword
+ // '
+ // 'two words
+ // '
+ // __o__n__e__w__o__r__d__\n
+ // 0 1 2 3 4 5 6 7
+ // __\n
+ // 8
+ // __t__w__o__ __w__o__r__d__s__\n__
+ // 9 10 11 12 13 14 15 16 17 18 19
+
+ ids = ["ml_div1", "ml_divbr1", "ml_ediv1", "ml_edivbr1", "ml_t1"];
+ testTextBeforeOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 8, "", 0, 0 ],
+ [ 9, 12, "oneword\n\n", 0, 9 ],
+ [ 13, 19, "two ", 9, 13 ] ]);
+ testTextBeforeOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 7, "", 0, 0 ],
+ [ 8, 12, "oneword", 0, 7,
+ [ [ 8, "ml_divbr1", kTodo, kOk, kTodo ],
+ [ 8, "ml_edivbr1", kTodo, kOk, kTodo ],
+ [ 9, "ml_divbr1", kTodo, kOk, kTodo ],
+ [ 9, "ml_edivbr1", kTodo, kOk, kTodo ] ] ],
+ [ 13, 18, "\n\ntwo", 7, 12 ],
+ [ 19, 19, " words", 12, 18,
+ [ [ 19, "ml_divbr1", kTodo, kTodo, kTodo, ],
+ [ 19, "ml_edivbr1", kTodo, kTodo, kTodo, ] ] ]
+ ] );
+
+ testTextAtOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 8, "oneword\n\n", 0, 9,
+ [ [ 7, "ml_divbr1", kTodo, kTodo, kTodo ],
+ [ 7, "ml_edivbr1", kTodo, kTodo, kTodo ],
+ [ 8, "ml_divbr1", kTodo, kTodo, kTodo ],
+ [ 8, "ml_edivbr1", kTodo, kTodo, kTodo ] ] ],
+ [ 9, 12, "two ", 9, 13 ],
+ [ 13, 19, "words\n", 13, 19 ] ]);
+ testTextAtOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 6, "oneword", 0, 7 ],
+ [ 7, 11, "\n\ntwo", 7, 12 ],
+ [ 12, 17, " words", 12, 18 ],
+ [ 18, 19, "\n", 18, 19,
+ [ [ 18, "ml_divbr1", kTodo, kTodo, kOk ],
+ [ 18, "ml_edivbr1", kTodo, kTodo, kOk ],
+ [ 19, "ml_divbr1", kTodo, kTodo, kOk ],
+ [ 19, "ml_edivbr1", kTodo, kTodo, kOk ] ] ] ]);
+
+ testTextAfterOffset(ids, BOUNDARY_WORD_START,
+ [ [ 0, 8, "two ", 9, 13,
+ [ [ 7, "ml_divbr1", kTodo, kTodo, kTodo ],
+ [ 7, "ml_edivbr1", kTodo, kTodo, kTodo ],
+ [ 8, "ml_divbr1", kTodo, kTodo, kTodo ],
+ [ 8, "ml_edivbr1", kTodo, kTodo, kTodo ] ] ],
+ [ 9, 12, "words\n", 13, 19 ],
+ [ 13, 19, "", 19, 19 ] ]);
+ testTextAfterOffset(ids, BOUNDARY_WORD_END,
+ [ [ 0, 7, "\n\ntwo", 7, 12 ],
+ [ 8, 12, " words", 12, 18 ],
+ [ 13, 18, "\n", 18, 19,
+ [ [ 18, "ml_divbr1", kTodo, kTodo, kOk ],
+ [ 18, "ml_edivbr1", kTodo, kTodo, kOk ] ] ],
+ [ 19, 19, "", 19, 19 ] ]);
+
+ // a <a href="#">b</a>
+ // a *
+ testTextBeforeOffset("cntr_1", BOUNDARY_WORD_START,
+ [ [ 0, 1, "", 0, 0 ],
+ [ 2, 3, "a ", 0, 2 ] ]);
+
+ testTextAtOffset("cntr_1", BOUNDARY_WORD_START,
+ [ [ 0, 1, "a ", 0, 2 ],
+ [ 2, 3, kEmbedChar, 2, 3 ] ]);
+ testTextAfterOffset("cntr_1", BOUNDARY_WORD_START,
+ [ [ 0, 1, kEmbedChar, 2, 3 ],
+ [ 2, 3, "", 3, 3 ] ]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="i1" value="hello"/>
+ <div id="d1">hello</div>
+ <div id="e1" contenteditable="true">hello</div>
+ <textarea id="t1">hello</textarea>
+
+ <input id="i2" value="hello "/>
+ <pre><div id="d2">hello </div></pre>
+ <div id="e2" contenteditable="true" style='white-space:pre'>hello </div>
+ <textarea id="t2">hello </textarea>
+
+ <input id="i6" value="hello all"/>
+ <div id="d6">hello all</div>
+ <div id="e6" contenteditable="true">hello all</div>
+ <textarea id="t6">hello all</textarea>
+
+ <input id="i7" value="hello my friend"/>
+ <div id="d7">hello my friend</div>
+ <div id="e7" contenteditable="true">hello my friend</div>
+ <textarea id="t7">hello my friend</textarea>
+
+ <input id="i8" value="Brave Sir Robin ran"/>
+ <pre>
+ <div id="d8">Brave Sir Robin ran</div>
+ <div id="e8" contenteditable="true">Brave Sir Robin ran</div>
+ </pre>
+ <textarea id="t8" cols="300">Brave Sir Robin ran</textarea>
+
+ <pre>
+<div id="ml_div1">oneword
+
+two words
+</div>
+<div id="ml_divbr1">oneword<br/><br/>two words<br/><br/></div>
+<div id="ml_ediv1" contenteditable="true">oneword
+
+two words
+</div>
+<div id="ml_edivbr1" contenteditable="true">oneword<br/><br/>two words<br/><br/></div>
+<textarea id="ml_t1" cols="300">oneword
+
+two words
+</textarea>
+ </pre>
+
+ <div id="cntr_1">a <a href="#">b</a></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/text/test_words.html b/accessible/tests/mochitest/text/test_words.html
new file mode 100644
index 000000000..dff90bfea
--- /dev/null
+++ b/accessible/tests/mochitest/text/test_words.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>nsIAccessibleText getText related function tests for html:input,html:div and html:textarea</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript">
+ if (navigator.platform.startsWith("Mac")) {
+ SimpleTest.expectAssertions(0, 1);
+ } else {
+ SimpleTest.expectAssertions(0, 1);
+ }
+
+ function doTest()
+ {
+ // "one two"
+ testWords("div1", ["one", "two"]);
+
+ // "one two"
+ testWords("div2", ["one", "two"]);
+
+ // "one,two"
+ testWordCount("div3", 2, kOk);
+ testWordAt("div3", 0, "one", kTodo);
+ testWordAt("div3", 1, "two", kOk);
+
+ // "one, two"
+ testWordCount("div4", 2, kOk);
+ testWordAt("div4", 0, "one", kTodo);
+ testWordAt("div4", 1, "two", kOk);
+
+ // "one+two"
+ testWordCount("div5", 2, kOk);
+ testWordAt("div5", 0, "one", kTodo);
+ testWordAt("div5", 1, "two", kOk);
+
+ // "one+two "
+ testWordCount("div6", 2, kOk);
+ testWordAt("div6", 0, "one", kTodo);
+ testWordAt("div6", 1, "two", kOk);
+
+ // "one\ntwo"
+ testWordCount("div7", 2, kOk);
+ testWordAt("div7", 0, "one", kOk);
+ testWordAt("div7", 1, "two", kTodo);
+
+ // "one.two"
+ testWordCount("div8", 2, kOk);
+ testWordAt("div8", 0, "one", kTodo);
+ testWordAt("div8", 1, "two", kOk);
+
+ // "345"
+ testWords("div9", ["345"]);
+
+ // "3a A4"
+ testWords("div10", ["3a", "A4"]);
+
+ // "3.1416"
+ testWords("div11", ["3.1416"], kTodo);
+
+ // "4,261.01"
+ testWords("div12", ["4,261.01"], kTodo);
+
+ // "カタカナ"
+ testWords("div13", ["カタカナ"], kOk);
+
+ // "Peter's car"
+ testWords("div14", ["Peter's", "car"], kTodo);
+
+ // "N.A.T.O."
+ testWords("div15", ["N.A.T.O."], kTodo);
+
+ // "3+4*5=23"
+ testWordCount("div16", 4, kOk);
+ testWordAt("div15", 0, "3", kTodo);
+ testWordAt("div15", 1, "4", kTodo);
+ testWordAt("div15", 2, "5", kTodo);
+ testWordAt("div15", 3, "23", kTodo);
+
+ // "Hello. Friend, are you here?!"
+ testWordCount("div17", 5, kOk);
+ testWordAt("div17", 0, "Hello", kTodo);
+ testWordAt("div17", 1, "Friend", kTodo);
+ testWordAt("div17", 2, "are", kOk);
+ testWordAt("div17", 3, "you", kOk);
+ testWordAt("div17", 4, "here", kTodo);
+
+ testWords("input_1", ["foo", "bar"]);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="nsIAccessibleText test word boundaries"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ <div id="div1">one two</div>
+ <div id="div2">one two</div>
+ <div id="div3">one,two</div>
+ <div id="div4">one, two</div>
+ <div id="div5">one+two</div>
+ <div id="div6">one+two </div>
+ <div id="div7">one<br/>two</div>
+ <div id="div8">one.two</div>
+ <div id="div9">345</div>
+ <div id="div10">3a A4</div>
+ <div id="div11">3.1416</div>
+ <div id="div12">4,261.01</div>
+ <div id="div13">カタカナ</div>
+ <div id="div14">Peter's car</div>
+ <div id="div15">N.A.T.O.</div>
+ <div id="div16">3+4*5=23</div>
+ <div id="div17">Hello. Friend, are you here?!</div>
+ </pre>
+ <input id="input_1" type="text" value="foo bar" placeholder="something or other">
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textattrs/a11y.ini b/accessible/tests/mochitest/textattrs/a11y.ini
new file mode 100644
index 000000000..046bd57e8
--- /dev/null
+++ b/accessible/tests/mochitest/textattrs/a11y.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+
+[test_general.html]
+[test_invalid.html]
diff --git a/accessible/tests/mochitest/textattrs/test_general.html b/accessible/tests/mochitest/textattrs/test_general.html
new file mode 100644
index 000000000..142701b17
--- /dev/null
+++ b/accessible/tests/mochitest/textattrs/test_general.html
@@ -0,0 +1,735 @@
+<html>
+
+<head>
+ <title>Text attributes tests</title>
+ <meta charset="utf-8" />
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ .gencontent:before { content: "*"; }
+ .gencontent:after { content: "*"; }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ var gComputedStyle = null;
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // area1
+ var ID = "area1";
+ var defAttrs = buildDefaultTextAttrs(ID, "10pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ var attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
+
+ attrs = { "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 7, attrs, defAttrs, 7, 11);
+
+ attrs = {};
+ testTextAttrs(ID, 12, attrs, defAttrs, 11, 18);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area2
+ ID = "area2";
+ defAttrs = buildDefaultTextAttrs(ID, "14pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
+
+ attrs = { "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 7, attrs, defAttrs, 7, 12);
+
+ var tempElem = getNode(ID).firstChild.nextSibling.firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"font-style": gComputedStyle.fontStyle,
+ "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 13, attrs, defAttrs, 12, 19);
+
+ attrs = { "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 20, attrs, defAttrs, 19, 23);
+
+ attrs = {};
+ testTextAttrs(ID, 24, attrs, defAttrs, 23, 30);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area3
+ ID = "area3";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ tempElem = getNode(ID).firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
+
+ tempElem = tempElem.firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 6, attrs, defAttrs, 6, 26);
+
+ tempElem = tempElem.parentNode;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 26, attrs, defAttrs, 26, 27);
+
+ tempElem = tempElem.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color,
+ "background-color": gComputedStyle.backgroundColor};
+ testTextAttrs(ID, 27, attrs, defAttrs, 27, 50);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area4
+ ID = "area4";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ tempElem = getNode(ID).firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 16);
+
+ tempElem = tempElem.nextSibling.firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 16, attrs, defAttrs, 16, 33);
+
+ tempElem = tempElem.parentNode;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 34, attrs, defAttrs, 33, 46);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area5: "Green!*!RedNormal"
+ ID = "area5";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ // Green
+ tempElem = getNode(ID).firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
+
+ // br
+ attrs = {};
+ testTextAttrs(ID, 5, attrs, defAttrs, 5, 6);
+
+ // img, embedded accessible, no attributes
+ attrs = {};
+ testTextAttrs(ID, 6, attrs, {}, 6, 7);
+
+ // br
+ attrs = {};
+ testTextAttrs(ID, 7, attrs, defAttrs, 7, 8);
+
+ // Red
+ tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 9, attrs, defAttrs, 8, 11);
+
+ // Normal
+ attrs = {};
+ testTextAttrs(ID, 11, attrs, defAttrs, 11, 18);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area6 (CSS vertical-align property, refer to bug 445938 for details
+ // and sup and sub elements, refer to bug 735645 for details)
+ ID = "area6";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 5);
+
+ attrs = { "text-position": "super", "font-size": "10pt" };
+ testTextAttrs(ID, 5, attrs, defAttrs, 5, 13);
+
+ attrs = {};
+ testTextAttrs(ID, 13, attrs, defAttrs, 13, 27);
+
+ attrs = { "text-position": "super" };
+ testTextAttrs(ID, 27, attrs, defAttrs, 27, 35);
+
+ attrs = {};
+ testTextAttrs(ID, 35, attrs, defAttrs, 35, 39);
+
+ attrs = { "text-position": "sub", "font-size": "10pt" };
+ testTextAttrs(ID, 39, attrs, defAttrs, 39, 50);
+
+ attrs = {};
+ testTextAttrs(ID, 50, attrs, defAttrs, 50, 55);
+
+ attrs = { "text-position": "sub" };
+ testTextAttrs(ID, 55, attrs, defAttrs, 55, 64);
+
+ attrs = {};
+ testTextAttrs(ID, 64, attrs, defAttrs, 64, 69);
+
+ attrs = { "text-position": "super" };
+ testTextAttrs(ID, 69, attrs, defAttrs, 69, 84);
+
+ attrs = {};
+ testTextAttrs(ID, 84, attrs, defAttrs, 84, 89);
+
+ attrs = { "text-position": "sub" };
+ testTextAttrs(ID, 89, attrs, defAttrs, 89, 102);
+
+ attrs = {};
+ testTextAttrs(ID, 102, attrs, defAttrs, 102, 107);
+
+ attrs = { "text-position": "super" };
+ testTextAttrs(ID, 107, attrs, defAttrs, 107, 123);
+
+ attrs = {};
+ testTextAttrs(ID, 123, attrs, defAttrs, 123, 128);
+
+ attrs = { "text-position": "sub" };
+ testTextAttrs(ID, 128, attrs, defAttrs, 128, 142);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area7
+ ID = "area7";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ defAttrs["language"] = "en";
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {"language": "ru"};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
+
+ attrs = {};
+ testTextAttrs(ID, 6, attrs, defAttrs, 6, 7);
+
+ tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "background-color": gComputedStyle.backgroundColor};
+ testTextAttrs(ID, 13, attrs, defAttrs, 7, 20);
+
+ attrs = {};
+ testTextAttrs(ID, 20, attrs, defAttrs, 20, 21);
+
+ attrs = {"language": "de"};
+ testTextAttrs(ID, 21, attrs, defAttrs, 21, 36);
+
+ attrs = {};
+ testTextAttrs(ID, 36, attrs, defAttrs, 36, 44);
+
+ attrs = {};
+ testTextAttrs(ID, 37, attrs, defAttrs, 36, 44);
+
+ tempElem = tempElem.nextSibling.nextSibling.nextSibling.nextSibling.firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 44, attrs, defAttrs, 44, 51);
+
+ tempElem = tempElem.firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"font-weight": kBoldFontWeight,
+ "color": gComputedStyle.color};
+ testTextAttrs(ID, 51, attrs, defAttrs, 51, 55);
+
+ tempElem = tempElem.parentNode;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"color": gComputedStyle.color};
+ testTextAttrs(ID, 55, attrs, defAttrs, 55, 62);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area9, different single style spans in styled paragraph
+ ID = "area9";
+ defAttrs = buildDefaultTextAttrs(ID, "10pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 6);
+
+ attrs = { "font-size": "12pt" };
+ testTextAttrs(ID, 7, attrs, defAttrs, 6, 12);
+
+ attrs = {};
+ testTextAttrs(ID, 13, attrs, defAttrs, 12, 21);
+
+ // Walk to the span with the different background color
+ tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "background-color": gComputedStyle.backgroundColor };
+ testTextAttrs(ID, 22, attrs, defAttrs, 21, 36);
+
+ attrs = {};
+ testTextAttrs(ID, 37, attrs, defAttrs, 36, 44);
+
+ // Walk from the background color span to the one with font-style
+ tempElem = tempElem.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "font-style": gComputedStyle.fontStyle };
+ testTextAttrs(ID, 45, attrs, defAttrs, 44, 61);
+
+ attrs = {};
+ testTextAttrs(ID, 62, attrs, defAttrs, 61, 69);
+
+ // Walk from span with font-style to the one with font-family.
+ tempElem = tempElem.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "font-family": kMonospaceFontFamily };
+ testTextAttrs(ID, 70, attrs, defAttrs, 69, 83);
+
+ attrs = {};
+ testTextAttrs(ID, 84, attrs, defAttrs, 83, 91);
+
+ attrs = {
+ "text-underline-style": "solid",
+ "text-underline-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 92, attrs, defAttrs, 91, 101);
+
+ attrs = {};
+ testTextAttrs(ID, 102, attrs, defAttrs, 101, 109);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 110, attrs, defAttrs, 109, 122);
+
+ attrs = {};
+ testTextAttrs(ID, 123, attrs, defAttrs, 122, 130);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 131, attrs, defAttrs, 130, 143);
+
+ attrs = {};
+ testTextAttrs(ID, 144, attrs, defAttrs, 143, 151);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 152, attrs, defAttrs, 151, 164);
+
+ attrs = {};
+ testTextAttrs(ID, 165, attrs, defAttrs, 164, 172);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area10, different single style spans in non-styled paragraph
+ ID = "area10";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 7);
+
+ attrs = { "font-size": "14pt" };
+ testTextAttrs(ID, 7, attrs, defAttrs, 7, 13);
+
+ attrs = {};
+ testTextAttrs(ID, 13, attrs, defAttrs, 13, 22);
+
+ // Walk to the span with the different background color
+ tempElem = getNode(ID).firstChild.nextSibling.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "background-color": gComputedStyle.backgroundColor };
+ testTextAttrs(ID, 23, attrs, defAttrs, 22, 37);
+
+ attrs = {};
+ testTextAttrs(ID, 38, attrs, defAttrs, 37, 45);
+
+ // Walk from the background color span to the one with font-style
+ tempElem = tempElem.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = {"font-style": gComputedStyle.fontStyle};
+ testTextAttrs(ID, 46, attrs, defAttrs, 45, 62);
+
+ attrs = {};
+ testTextAttrs(ID, 63, attrs, defAttrs, 62, 70);
+
+ // Walk from span with font-style to the one with font-family.
+ tempElem = tempElem.nextSibling.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "font-family": kMonospaceFontFamily };
+ testTextAttrs(ID, 71, attrs, defAttrs, 70, 84);
+
+ attrs = {};
+ testTextAttrs(ID, 85, attrs, defAttrs, 84, 92);
+
+ attrs = {
+ "text-underline-style": "solid",
+ "text-underline-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 93, attrs, defAttrs, 92, 102);
+
+ attrs = {};
+ testTextAttrs(ID, 103, attrs, defAttrs, 102, 110);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": gComputedStyle.color
+ };
+ testTextAttrs(ID, 111, attrs, defAttrs, 110, 123);
+
+ attrs = {};
+ testTextAttrs(ID, 124, attrs, defAttrs, 123, 131);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area11, "font-weight" tests
+ ID = "area11";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt", kBoldFontWeight);
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = { };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 13);
+
+ attrs = { "font-weight": kNormalFontWeight };
+ testTextAttrs(ID, 13, attrs, defAttrs, 13, 20);
+
+ attrs = { };
+ testTextAttrs(ID, 20, attrs, defAttrs, 20, 27);
+
+ attrs = { "font-weight": kNormalFontWeight };
+ testTextAttrs(ID, 27, attrs, defAttrs, 27, 33);
+
+ attrs = { };
+ testTextAttrs(ID, 33, attrs, defAttrs, 33, 51);
+
+ attrs = { "font-weight": kNormalFontWeight };
+ testTextAttrs(ID, 51, attrs, defAttrs, 51, 57);
+
+ attrs = { };
+ testTextAttrs(ID, 57, attrs, defAttrs, 57, 97);
+
+ //////////////////////////////////////////////////////////////////////////
+ // test out of range offset
+ testTextAttrsWrongOffset("area12", -1);
+ testTextAttrsWrongOffset("area12", 500);
+
+ //////////////////////////////////////////////////////////////////////////
+ // test zero offset on empty hypertext accessibles
+ ID = "area13";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ attrs = { };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
+
+ ID = "area14";
+ defAttrs = buildDefaultTextAttrs(ID, kInputFontSize,
+ kNormalFontWeight, kInputFontFamily);
+
+ attrs = { };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 0);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area15, embed char tests, "*plain*plain**bold*bold*"
+ ID = "area15";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+
+ // p
+ testTextAttrs(ID, 0, { }, { }, 0, 1);
+ // plain
+ testTextAttrs(ID, 1, { }, defAttrs, 1, 6);
+ // p
+ testTextAttrs(ID, 6, { }, { }, 6, 7);
+ // plain
+ testTextAttrs(ID, 7, { }, defAttrs, 7, 12);
+ // p and img
+ testTextAttrs(ID, 12, { }, { }, 12, 14);
+ // bold
+ attrs = { "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 14, attrs, defAttrs, 14, 18);
+ // p
+ testTextAttrs(ID, 18, { }, { }, 18, 19);
+ // bold
+ attrs = { "font-weight": kBoldFontWeight };
+ testTextAttrs(ID, 19, attrs, defAttrs, 19, 23);
+ // p
+ testTextAttrs(ID, 23, { }, { }, 23, 24);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area16, "font-family" tests
+ ID = "area16";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = { "font-family": kMonospaceFontFamily };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 4);
+
+ attrs = { };
+ testTextAttrs(ID, 4, attrs, defAttrs, 4, 9);
+
+ attrs = { "font-family": kSerifFontFamily };
+ testTextAttrs(ID, 9, attrs, defAttrs, 9, 13);
+
+ attrs = { };
+ testTextAttrs(ID, 13, attrs, defAttrs, 13, 18);
+
+ attrs = { "font-family": kAbsentFontFamily };
+ testTextAttrs(ID, 18, attrs, defAttrs, 18, 22);
+
+ // bug 1224498 - this fails with 'cursive' fontconfig lookup
+ if (!LINUX) {
+ attrs = { };
+ testTextAttrs(ID, 22, attrs, defAttrs, 22, 27);
+
+ attrs = { "font-family": kCursiveFontFamily };
+ testTextAttrs(ID, 27, attrs, defAttrs, 27, 31);
+
+ attrs = { };
+ testTextAttrs(ID, 31, attrs, defAttrs, 31, 45);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // area17, "text-decoration" tests
+ ID = "area17";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ attrs = {
+ "text-underline-style": "solid",
+ "text-underline-color": "rgb(0, 0, 0)",
+ };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 10);
+
+ attrs = {
+ "text-underline-style": "solid",
+ "text-underline-color": "rgb(0, 0, 255)",
+ };
+ testTextAttrs(ID, 10, attrs, defAttrs, 10, 15);
+
+ attrs = {
+ "text-underline-style": "dotted",
+ "text-underline-color": "rgb(0, 0, 0)",
+ };
+ testTextAttrs(ID, 15, attrs, defAttrs, 15, 22);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": "rgb(0, 0, 0)",
+ };
+ testTextAttrs(ID, 22, attrs, defAttrs, 22, 34);
+
+ attrs = {
+ "text-line-through-style": "solid",
+ "text-line-through-color": "rgb(0, 0, 255)",
+ };
+ testTextAttrs(ID, 34, attrs, defAttrs, 34, 39);
+
+ attrs = {
+ "text-line-through-style": "wavy",
+ "text-line-through-color": "rgb(0, 0, 0)",
+ };
+ testTextAttrs(ID, 39, attrs, defAttrs, 39, 44);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area18, "auto-generation text" tests
+ ID = "area18";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ var attrs = {
+ "auto-generated": "true"
+ };
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 3);
+ testTextAttrs(ID, 3, { }, defAttrs, 3, 7);
+ testTextAttrs(ID, 7, attrs, defAttrs, 7, 8);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area19, "HTML5 mark tag" test
+ // text enclosed in mark tag will have a different background color
+ ID = "area19";
+ defAttrs = buildDefaultTextAttrs(ID, "12pt");
+
+ attrs = {};
+ testTextAttrs(ID, 0, attrs, defAttrs, 0, 10);
+
+ tempElem = getNode(ID).firstChild.nextSibling;
+ gComputedStyle = document.defaultView.getComputedStyle(tempElem, "");
+ attrs = { "background-color": gComputedStyle.backgroundColor };
+ testTextAttrs(ID, 11, attrs, defAttrs, 10, 17);
+
+ attrs = {};
+ testTextAttrs(ID, 18, attrs, defAttrs, 17, 28);
+
+ //////////////////////////////////////////////////////////////////////////
+ // area20, "aOffset as -1 (Mozilla Bug 789621)" test
+
+ ID = "area20";
+ defAttrs = buildDefaultTextAttrs(ID, "15pt");
+ testDefaultTextAttrs(ID, defAttrs);
+
+ testTextAttrs(ID, -1, {}, defAttrs, 0, 11);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body style="font-size: 12pt">
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=345759"
+ title="Implement text attributes">
+ Mozilla Bug 345759
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=473569"
+ title="Restrict text-position to allowed values">
+ Mozilla Bug 473569
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=473576"
+ title="font-family text attribute should expose actual font used">
+ Mozilla Bug 473576
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=523304"
+ title="expose text-underline-color and text-line-through-color text attributes">
+ Mozilla Bug 523304
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=735645"
+ title="expose sub and sup elements in text attributes">
+ Mozilla Bug 735645
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=445516"
+ title="Support auto-generated text attribute on bullet lists">
+ Mozilla Bug 445516
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=789621"
+ title="getTextAttributes doesn't work with magic offsets">
+ Mozilla Bug 789621
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="area1" style="font-size: smaller">Normal <b>Bold</b> Normal</p>
+ <p id="area2" style="font-size: 120%">Normal <b>Bold <i>Italic </i>Bold</b> Normal</p>
+ <p id="area3" style="background-color: blue;">
+ <span style="color: green; background-color: rgb(0, 0, 255)">
+ Green
+ <span style="color: red">but children are red</span>
+ </span><span style="color: green; background-color: rgb(255, 255, 0);">
+ Another green section.
+ </span>
+ </p>
+ <p id="area4">
+ <span style="color: green">
+ Green
+ </span><span style="color: green">
+ Green too
+ <span style="color: red">with red children</span>
+ Green again
+ </span>
+ </p>
+ <!-- Green!*!RedNormal-->
+ <p id="area5">
+ <span style="color: green">Green</span>
+ <img src="../moz.png" alt="image"/>
+ <span style="color: red">Red</span>Normal
+ </p>
+ <p id="area6">
+ This <sup>sentence</sup> has the word
+ <span style="vertical-align:super;">sentence</span> in
+ <sub>superscript</sub> and
+ <span style="vertical-align:sub;">subscript</span> and
+ <span style="vertical-align:20%;">superscript 20%</span> and
+ <span style="vertical-align:-20%;">subscript 20%</span> and
+ <span style="vertical-align:20px;">superscript 20px</span> and
+ <span style="vertical-align:-20px;">subscript 20px</span>
+ </p>
+
+ <p lang="en" id="area7">
+ <span lang="ru">Привет</span>
+ <span style="background-color: blue">Blue BG color</span>
+ <span lang="de">Ich bin/Du bist</span>
+ <span lang="en">
+ Normal
+ <span style="color: magenta">Magenta<b>Bold</b>Magenta</span>
+ </span>
+ </p>
+
+ <p id="area9" style="font-size: smaller">Small
+ <span style="font-size: 120%">bigger</span> smaller
+ <span style="background-color: blue;">background blue</span> normal
+ <span style="font-style: italic;">Different styling</span> normal
+ <span style="font-family: monospace;">Different font</span> normal
+ <span style="text-decoration: underline;">underlined</span> normal
+ <span style="text-decoration: line-through;">strikethrough</span> normal
+ <s>strikethrough</s> normal
+ <strike>strikethrough</strike> normal
+ </p>
+
+ <p id="area10">Normal
+ <span style="font-size: 120%">bigger</span> smaller
+ <span style="background-color: blue;">background blue</span> normal
+ <span style="font-style: italic;">Different styling</span> normal
+ <span style="font-family: monospace;">Different font</span> normal
+ <span style="text-decoration: underline;">underlined</span> normal
+ <span style="text-decoration: line-through;">strikethrough</span> normal
+ </p>
+
+ <p id="area11" style="font-weight: bolder;">
+ <span style="font-weight: bolder;">bolder</span>bolder
+ <span style="font-weight: lighter;">lighter</span>bolder
+ <span style="font-weight: normal;">normal</span>bolder
+ <b>bold</b>bolder
+ <span style="font-weight: 400;">normal</span>bolder
+ <span style="font-weight: 700;">bold</span>bolder
+ <span style="font-weight: bold;">bold</span>bolder
+ <span style="font-weight: 900;">bold</span>bolder
+ </p>
+
+ <p id="area12">hello</p>
+ <p id="area13"></p>
+ <input id="area14">
+
+ <!-- *plain*plain**bold*bold*-->
+ <div id="area15"><p>embed</p>plain<p>embed</p>plain<p>embed</p><img src="../moz.png" alt="image"/><b>bold</b><p>embed</p><b>bold</b><p>embed</p></div>
+
+ <p id="area16" style="font-family: sans-serif;">
+ <span style="font-family: monospace;">text</span>text
+ <span style="font-family: serif;">text</span>text
+ <span style="font-family: BodoniThatDoesntExist;">text</span>text
+ <span style="font-family: Comic Sans MS, cursive;">text</span>text
+ <span style="font-family: sans-serif, fantasy;">text</span>text
+ </p>
+
+ <p id="area17">
+ <span style="text-decoration-line: underline;">underline
+ </span><span style="text-decoration: underline; text-decoration-color: blue;">blue
+ </span><span style="text-decoration: underline; text-decoration-style: dotted;">dotted
+ </span><span style="text-decoration-line: line-through;">linethrough
+ </span><span style="text-decoration: line-through; text-decoration-color: blue;">blue
+ </span><span style="text-decoration: line-through; text-decoration-style: wavy;">wavy
+ </span>
+ </p>
+
+ <ul>
+ <li id="area18" class="gencontent">item</li>
+ </ul>
+
+ <p id="area19">uncolored
+ <mark>colored</mark> uncolored
+ </p>
+
+ <p id="area20" style="font-size: 15pt;">offset test</p>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textattrs/test_invalid.html b/accessible/tests/mochitest/textattrs/test_invalid.html
new file mode 100644
index 000000000..495db0888
--- /dev/null
+++ b/accessible/tests/mochitest/textattrs/test_invalid.html
@@ -0,0 +1,62 @@
+<html>
+
+<head>
+ <title>Invalid text attribute</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+ function doTests()
+ {
+ testDefaultTextAttrs("aria_invalid_empty", {}, true);
+ testDefaultTextAttrs("aria_invalid_true", { "invalid": "true" }, true);
+ testDefaultTextAttrs("aria_invalid_false", { "invalid": "false" }, true);
+ testDefaultTextAttrs("aria_invalid_grammar", { "invalid": "grammar" }, true);
+ testDefaultTextAttrs("aria_invalid_spelling", { "invalid": "spelling" }, true);
+ testDefaultTextAttrs("aria_invalid_erroneous", { "invalid": "true" }, true);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=445510"
+ title="Support ARIA-based text attributes">
+ Mozilla Bug 445510
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="aria_invalid_empty" aria-invalid="">no invalid</div>
+ <div id="aria_invalid_true" aria-invalid="true">invalid:true</div>
+ <div id="aria_invalid_false" aria-invalid="false">invalid:false</div>
+ <div id="aria_invalid_grammar" aria-invalid="grammar">invalid:grammar</div>
+ <div id="aria_invalid_spelling" aria-invalid="spelling">invalid:spelling</div>
+ <div id="aria_invalid_erroneous" aria-invalid="erroneous">invalid:true</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textcaret/a11y.ini b/accessible/tests/mochitest/textcaret/a11y.ini
new file mode 100644
index 000000000..22d09751d
--- /dev/null
+++ b/accessible/tests/mochitest/textcaret/a11y.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_browserui.xul]
+[test_general.html]
diff --git a/accessible/tests/mochitest/textcaret/test_browserui.xul b/accessible/tests/mochitest/textcaret/test_browserui.xul
new file mode 100644
index 000000000..e2be6a464
--- /dev/null
+++ b/accessible/tests/mochitest/textcaret/test_browserui.xul
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessibility Caret Offset Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpToConsole = true; // debug
+ //enableLogging("tree,verbose");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new setCaretOffset(urlbarInput(), -1, urlbarInput()));
+ gQueue.push(new setCaretOffset(urlbarInput(), 0));
+ gQueue.onFinish = function()
+ {
+ closeBrowserWindow();
+ }
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, "about:");
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=723833"
+ title="IAccessibleText::setCaretOffset on location or search bar causes focus to jump">
+ Bug 723833
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/textcaret/test_general.html b/accessible/tests/mochitest/textcaret/test_general.html
new file mode 100644
index 000000000..69f83959f
--- /dev/null
+++ b/accessible/tests/mochitest/textcaret/test_general.html
@@ -0,0 +1,183 @@
+<html>
+
+<head>
+ <title>Text accessible caret testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Turn on/off the caret browsing mode.
+ */
+ function turnCaretBrowsing(aIsOn)
+ {
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ prefs.setBoolPref("accessibility.browsewithcaret", aIsOn);
+ }
+
+ /**
+ * Test caret offset for the given accessible.
+ */
+ function testCaretOffset(aID, aCaretOffset)
+ {
+ var acc = getAccessible(aID, [nsIAccessibleText]);
+ is(acc.caretOffset, aCaretOffset,
+ "Wrong caret offset for " + aID);
+ }
+
+ function testCaretOffsets(aList)
+ {
+ for (var i = 0; i < aList.length; i++)
+ testCaretOffset(aList[0][0], aList[0][1]);
+ }
+
+ function queueTraversalList(aList, aFocusNode)
+ {
+ for (var i = 0 ; i < aList.length; i++) {
+ var node = aList[i].DOMPoint[0];
+ var nodeOffset = aList[i].DOMPoint[1];
+
+ var textAcc = aList[i].point[0];
+ var textOffset = aList[i].point[1];
+ var textList = aList[i].pointList;
+ var invoker =
+ new moveCaretToDOMPoint(textAcc, node, nodeOffset, textOffset,
+ ((i == 0) ? aFocusNode : null),
+ testCaretOffsets.bind(null, textList))
+ gQueue.push(invoker);
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ turnCaretBrowsing(true);
+
+ // test caret offsets
+ testCaretOffset(document, 16);
+ testCaretOffset("textbox", -1);
+ testCaretOffset("textarea", -1);
+ testCaretOffset("p", -1);
+
+ // test caret move events and caret offsets
+ gQueue = new eventQueue();
+
+ gQueue.push(new setCaretOffset("textbox", 1, "textbox"));
+ gQueue.push(new setCaretOffset("link", 1, "link"));
+ gQueue.push(new setCaretOffset("heading", 1, document));
+
+ // a*b*c
+ var p2Doc = getNode("p2_container").contentDocument;
+ var traversalList = [
+ { // before 'a'
+ DOMPoint: [ getNode("p2", p2Doc).firstChild, 0 ],
+ point: [ getNode("p2", p2Doc), 0 ],
+ pointList: [ [ p2Doc, 0 ] ]
+ },
+ { // after 'a' (before anchor)
+ DOMPoint: [ getNode("p2", p2Doc).firstChild, 1 ],
+ point: [ getNode("p2", p2Doc), 1 ],
+ pointList: [ [ p2Doc, 0 ] ]
+ },
+ { // before 'b' (inside anchor)
+ DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 0 ],
+ point: [ getNode("p2_a", p2Doc), 0 ],
+ pointList: [
+ [ getNode("p2", p2Doc), 1 ],
+ [ p2Doc, 0 ]
+ ]
+ },
+ { // after 'b' (inside anchor)
+ DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 1 ],
+ point: [ getNode("p2_a", p2Doc), 1 ],
+ pointList: [
+ [ getNode("p2", p2Doc), 1 ] ,
+ [ p2Doc, 0 ]
+ ]
+ },
+ { // before 'c' (after anchor)
+ DOMPoint: [ getNode("p2", p2Doc).lastChild, 0 ],
+ point: [ getNode("p2", p2Doc), 2 ],
+ pointList: [ [ p2Doc, 0 ] ]
+ },
+ { // after 'c'
+ DOMPoint: [ getNode("p2", p2Doc).lastChild, 1 ],
+ point: [ getNode("p2", p2Doc), 3 ],
+ pointList: [ [ p2Doc, 0 ] ]
+ }
+ ];
+ queueTraversalList(traversalList, getNode("p2", p2Doc));
+
+ gQueue.onFinish = function()
+ {
+ turnCaretBrowsing(false);
+ }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=448744"
+ title="caretOffset should return -1 if the system caret is not currently with in that particular object">
+ Bug 448744
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115"
+ title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs">
+ Bug 524115
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=546068"
+ title="Position is not being updated when atk_text_set_caret_offset is used">
+ Bug 546068
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=672717"
+ title="Broken caret when moving into/out of embedded objects with right arrow">
+ Bug 672717
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=725581"
+ title="caretOffset for textarea should be -1 when textarea doesn't have a focus">
+ Bug 725581
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox" value="hello"/>
+ <textarea id="textarea">text<br>text</textarea>
+ <p id="p" contentEditable="true"><span>text</span><br/>text</p>
+ <a id="link" href="about:">about mozilla</a>
+ <h5 id="heading">heading</h5>
+ <iframe id="p2_container"
+ src="data:text/html,<p id='p2' contentEditable='true'>a<a id='p2_a' href='mozilla.org'>b</a>c</p>"></iframe>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textrange/a11y.ini b/accessible/tests/mochitest/textrange/a11y.ini
new file mode 100644
index 000000000..4e610c61f
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/a11y.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+
+[test_general.html]
+[test_selection.html]
diff --git a/accessible/tests/mochitest/textrange/test_general.html b/accessible/tests/mochitest/textrange/test_general.html
new file mode 100644
index 000000000..21a758e17
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/test_general.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Text Range tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ // enclosingRange
+ var input = getAccessible("input", [ nsIAccessibleText ]);
+ testTextRange(input.enclosingRange, "enclosing range for 'input'",
+ input, 0, input, 5, "hello", input);
+
+ var ta = getAccessible("textarea", [ nsIAccessibleText ]);
+ testTextRange(ta.enclosingRange, "enclosing range for 'textarea'",
+ ta, 0, ta, 5, "hello", textarea);
+
+ var iframeDocNode = getNode("iframe").contentDocument;
+ var iframeDoc = getAccessible(iframeDocNode, [ nsIAccessibleText ]);
+ testTextRange(iframeDoc.enclosingRange, "enclosing range for iframe doc",
+ iframeDoc, 0, iframeDoc, 1, "hello",
+ iframeDoc, [ getNode("p", iframeDocNode) ]);
+
+ // getRangeByChild
+ var docacc = getAccessible(document, [ nsIAccessibleText ]);
+ var p1 = getAccessible("p1");
+ var p1Range = docacc.getRangeByChild(p1);
+ testTextRange(p1Range, "range by 'p1' child",
+ p1, 0, "p1", 11, "text text",
+ p1, ["p1_img"]);
+
+ testTextRange(docacc.getRangeByChild(getAccessible("p1_img")),
+ "range by 'p1_img' child",
+ "p1", 5, "p1", 5, "",
+ "p1", ["p1_img"]);
+
+ var p2 = getAccessible("p2");
+ var p2Range = docacc.getRangeByChild(p2);
+ testTextRange(p2Range, "range by 'p2' child",
+ p2, 0, "p2", 11, "text link text",
+ p2, ["p2_a"]);
+
+ testTextRange(docacc.getRangeByChild(getAccessible("p2_a")),
+ "range by 'p2_a' child",
+ "p2_a", 0, "p2_a", 5, "link",
+ "p2_a", ["p2_img"]);
+
+ // getRangeAtPoint
+ getNode("p2_a").scrollIntoView(true);
+ var [x, y] = getPos("p2_a");
+ testTextRange(docacc.getRangeAtPoint(x + 1, y + 1),
+ "range at 'p2_a' top-left edge",
+ "p2_a", 0, "p2_a", 0, "",
+ "p2_a");
+
+ // TextRange::compare
+ ok(input.enclosingRange.compare(input.enclosingRange),
+ "input enclosing ranges should be equal");
+
+ ok(!input.enclosingRange.compare(ta.enclosingRange),
+ "input and textarea enclosing ranges can't be equal");
+
+ // TextRange::compareEndPoints
+ var res = p1Range.compareEndPoints(EndPoint_End, p2Range, EndPoint_Start);
+ is(res, -1, "p1 range must be lesser with p2 range");
+
+ res = p2Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_End);
+ is(res, 1, "p2 range must be greater with p1 range");
+
+ res = p1Range.compareEndPoints(EndPoint_Start, p1Range, EndPoint_Start);
+ is(res, 0, "p1 range must be equal with p1 range");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement Text accessible text range methods"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=975065">Bug 975065</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input" value="hello">
+ <textarea id="textarea">hello</textarea>
+ <iframe id="iframe" src="data:text/html,<html><body><p id='p'>hello</p></body></html>"></iframe>
+ <p id="p1">text <img id="p1_img", src="../moz.png"> text</p>
+ <p id="p2">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textrange/test_selection.html b/accessible/tests/mochitest/textrange/test_selection.html
new file mode 100644
index 000000000..a7c79091f
--- /dev/null
+++ b/accessible/tests/mochitest/textrange/test_selection.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Text Range selection tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript"
+ src="../layout.js"></script>
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ var sel = window.getSelection();
+ var p = getNode("p1");
+ var a = getNode("p2_a");
+
+ var range = document.createRange();
+ sel.addRange(range);
+
+ // the accessible is contained by the range
+ range.selectNode(p);
+
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #1", document, 3, document, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1.");
+ testTextRange(a11yrange, "cropped range #1", a, 0, a, 5);
+
+ // the range is contained by the accessible
+ range.selectNode(a);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #2", p, 5, p, 6);
+
+ ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2.");
+ testTextRange(a11yrange, "cropped range #2", p, 5, p, 6);
+
+ // the range starts before the accessible and ends inside it
+ range.setStart(p, 0);
+ range.setEndAfter(a.firstChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #3", p, 0, a, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3.");
+ testTextRange(a11yrange, "cropped range #3", a, 0, a, 4);
+
+ // the range starts inside the accessible and ends after it
+ range.setStart(a.firstChild, 1);
+ range.setEndAfter(p);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #4", a, 1, document, 4);
+
+ ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4.");
+ testTextRange(a11yrange, "cropped range #4", a, 1, a, 5);
+
+ // the range ends before the accessible
+ range.setStart(p.firstChild, 0);
+ range.setEnd(p.firstChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #5", p, 0, p, 4);
+ ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't");
+
+ // the range starts after the accessible
+ range.setStart(p.lastChild, 0);
+ range.setEnd(p.lastChild, 4);
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #6", p, 6, p, 10);
+
+ ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't");
+
+ // crop a range by a table
+ range.selectNode(getNode("c2"));
+ var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
+ var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
+
+ testTextRange(a11yrange, "selection range #7", document, 4, document, 5);
+
+ ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7.");
+ testTextRange(a11yrange, "cropped range #7", "c2", 5, "c2", 6);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Implement IAccessible2_3::selectionRanges"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
+
+ <div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textselection/a11y.ini b/accessible/tests/mochitest/textselection/a11y.ini
new file mode 100644
index 000000000..6581af56d
--- /dev/null
+++ b/accessible/tests/mochitest/textselection/a11y.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_general.html]
+[test_userinput.html]
diff --git a/accessible/tests/mochitest/textselection/test_general.html b/accessible/tests/mochitest/textselection/test_general.html
new file mode 100644
index 000000000..87d95eaf0
--- /dev/null
+++ b/accessible/tests/mochitest/textselection/test_general.html
@@ -0,0 +1,221 @@
+<html>
+
+<head>
+ <title>Text selection testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Invokers
+ */
+ function addSelection(aID, aStartOffset, aEndOffset)
+ {
+ this.hyperTextNode = getNode(aID);
+ this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+ ];
+
+ this.invoke = function addSelection_invoke()
+ {
+ this.hyperText.addSelection(aStartOffset, aEndOffset);
+ }
+
+ this.finalCheck = function addSelection_finalCheck()
+ {
+ is(this.hyperText.selectionCount, 1,
+ "addSelection: Wrong selection count for " + aID);
+ var startOffset = {}, endOffset = {};
+ this.hyperText.getSelectionBounds(0, startOffset, endOffset);
+
+ is(startOffset.value, aStartOffset,
+ "addSelection: Wrong start offset for " + aID);
+ is(endOffset.value, aEndOffset,
+ "addSelection: Wrong end offset for " + aID);
+ }
+
+ this.getID = function addSelection_getID()
+ {
+ return "nsIAccessibleText::addSelection test for " + aID;
+ }
+ }
+
+ function changeSelection(aID, aStartOffset, aEndOffset)
+ {
+ this.hyperTextNode = getNode(aID);
+ this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+ ];
+
+ this.invoke = function changeSelection_invoke()
+ {
+ this.hyperText.setSelectionBounds(0, aStartOffset, aEndOffset);
+ }
+
+ this.finalCheck = function changeSelection_finalCheck()
+ {
+ is(this.hyperText.selectionCount, 1,
+ "setSelectionBounds: Wrong selection count for " + aID);
+ var startOffset = {}, endOffset = {};
+ this.hyperText.getSelectionBounds(0, startOffset, endOffset);
+
+ is(startOffset.value, aStartOffset,
+ "setSelectionBounds: Wrong start offset for " + aID);
+ is(endOffset.value, aEndOffset,
+ "setSelectionBounds: Wrong end offset for " + aID);
+ }
+
+ this.getID = function changeSelection_getID()
+ {
+ return "nsIAccessibleText::setSelectionBounds test for " + aID;
+ }
+ }
+
+ function removeSelection(aID)
+ {
+ this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, document)
+ ];
+
+ this.invoke = function removeSelection_invoke()
+ {
+ this.hyperText.removeSelection(0);
+ }
+
+ this.finalCheck = function removeSelection_finalCheck()
+ {
+ is(this.hyperText.selectionCount, 0,
+ "removeSelection: Wrong selection count for " + aID);
+ }
+
+ this.getID = function removeSelection_getID()
+ {
+ return "nsIAccessibleText::removeSelection test for " + aID;
+ }
+ }
+
+ function changeDOMSelection(aID, aNodeID1, aNodeOffset1,
+ aNodeID2, aNodeOffset2,
+ aTests)
+ {
+ this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+ ];
+
+ this.invoke = function changeDOMSelection_invoke()
+ {
+ var sel = window.getSelection();
+ var range = document.createRange();
+ range.setStart(getNode(aNodeID1), aNodeOffset1);
+ range.setEnd(getNode(aNodeID2), aNodeOffset2);
+ sel.addRange(range);
+ }
+
+ this.finalCheck = function changeDOMSelection_finalCheck()
+ {
+ for (var i = 0; i < aTests.length; i++) {
+ var text = getAccessible(aTests[i][0], nsIAccessibleText);
+ is(text.selectionCount, 1,
+ "setSelectionBounds: Wrong selection count for " + aID);
+ var startOffset = {}, endOffset = {};
+ text.getSelectionBounds(0, startOffset, endOffset);
+
+ is(startOffset.value, aTests[i][1],
+ "setSelectionBounds: Wrong start offset for " + aID);
+ is(endOffset.value, aTests[i][2],
+ "setSelectionBounds: Wrong end offset for " + aID);
+ }
+ }
+
+ this.getID = function changeDOMSelection_getID()
+ {
+ return "DOM selection change for " + aID;
+ }
+ }
+
+ function onfocusEventSeq(aID)
+ {
+ var caretMovedChecker =
+ new invokerChecker(EVENT_TEXT_CARET_MOVED, aID);
+ var selChangedChecker =
+ new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID);
+ selChangedChecker.unexpected = true;
+
+ return [ caretMovedChecker, selChangedChecker ];
+ }
+
+ /**
+ * Do tests
+ */
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new addSelection("paragraph", 1, 3));
+ gQueue.push(new changeSelection("paragraph", 2, 4));
+ gQueue.push(new removeSelection("paragraph"));
+
+ gQueue.push(new synthFocus("textbox", onfocusEventSeq("textbox")));
+ gQueue.push(new changeSelection("textbox", 1, 3));
+
+ gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
+ gQueue.push(new changeSelection("textarea", 1, 3));
+
+ gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0,
+ [["c1", 2, 2]]));
+ gQueue.push(new changeDOMSelection("c2", "c2", 0, "c2_div2", 1,
+ [["c2", 0, 3], ["c2_div2", 0, 2]]));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=688126"
+ title="nsIAccessibleText::setSelectionBounds doesn't fire text selection changed events in some cases">
+ Bug 688126
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=688124"
+ title="no text selection changed event when selection is removed">
+ Bug 688124
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="paragraph">hello</p>
+ <input id="textbox" value="hello"/>
+ <textarea id="textarea">hello</textarea>
+ <div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div>
+ <div id="c2">hi<div id="c2_div2">hi</div></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/textselection/test_userinput.html b/accessible/tests/mochitest/textselection/test_userinput.html
new file mode 100644
index 000000000..1f7127866
--- /dev/null
+++ b/accessible/tests/mochitest/textselection/test_userinput.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>Text selection by user input</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Invokers
+ */
+ function synthTabAndCheckPrevTabbed(aID, aPrevID)
+ {
+ this.__proto__ = new synthTab(aID, new focusChecker(aID));
+
+ this.finalCheck = function changeSelection_finalCheck()
+ {
+ var prevTabbed = getAccessible(aPrevID, [ nsIAccessibleText ]);
+ is(prevTabbed.selectionCount, 0,
+ "Wrong selection count for " + aPrevID);
+
+ var exceptionCaught = false;
+ try {
+ var startOffsetObj = {}, endOffsetObj = {};
+ prevTabbed.getSelectionBounds(0, startOffsetObj, endOffsetObj);
+ } catch (e) {
+ exceptionCaught = true;
+ }
+
+ ok(exceptionCaught, "No selection was expected for " + aPrevID);
+ }
+
+ this.getID = function changeSelection_getID()
+ {
+ return "Hidden selection check for " + aPrevID;
+ }
+ }
+
+ /**
+ * Do tests
+ */
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Tab to 't2' and then tab out it: it must has no selection.
+ gQueue.push(new synthFocus("t1"));
+ gQueue.push(new synthTab("t2", new focusChecker("t2")));
+ gQueue.push(new synthTabAndCheckPrevTabbed("t3", "t2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=440590"
+ title="Text selection information is not updated when HTML and XUL entries lose focus">
+ Bug 440590
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input type="text" id="t1" maxlength="3" size="3" value="1">
+ <input type="text" id="t2" maxlength="3" size="3" value="1">
+ <input type="text" id="t3" maxlength="3" size="3" value="1">
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/a11y.ini b/accessible/tests/mochitest/tree/a11y.ini
new file mode 100644
index 000000000..c43e4552e
--- /dev/null
+++ b/accessible/tests/mochitest/tree/a11y.ini
@@ -0,0 +1,51 @@
+[DEFAULT]
+support-files =
+ dockids.html
+ wnd.xul
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/formimage.png
+ !/accessible/tests/mochitest/letters.gif
+ !/accessible/tests/mochitest/moz.png
+ !/accessible/tests/mochitest/tree/wnd.xul
+ !/dom/media/test/bug461281.ogg
+
+[test_applicationacc.xul]
+skip-if = true # Bug 561508
+[test_aria_globals.html]
+[test_aria_grid.html]
+[test_aria_imgmap.html]
+[test_aria_list.html]
+[test_aria_menu.html]
+[test_aria_owns.html]
+[test_aria_presentation.html]
+[test_aria_table.html]
+[test_brokencontext.html]
+[test_button.xul]
+[test_canvas.html]
+[test_combobox.xul]
+[test_cssflexbox.html]
+[test_cssoverflow.html]
+[test_dochierarchy.html]
+[test_dockids.html]
+[test_filectrl.html]
+[test_formctrl.html]
+skip-if = buildapp == "mulet"
+[test_formctrl.xul]
+[test_gencontent.html]
+[test_groupbox.xul]
+[test_iframe.html]
+[test_img.html]
+[test_invalid_img.xhtml]
+[test_invalidationlist.html]
+[test_list.html]
+[test_map.html]
+[test_media.html]
+skip-if = buildapp == "mulet"
+[test_select.html]
+[test_tabbox.xul]
+[test_tabbrowser.xul]
+[test_table.html]
+[test_tree.xul]
+[test_txtcntr.html]
+[test_txtctrl.html]
+[test_txtctrl.xul]
diff --git a/accessible/tests/mochitest/tree/dockids.html b/accessible/tests/mochitest/tree/dockids.html
new file mode 100644
index 000000000..c59ae3267
--- /dev/null
+++ b/accessible/tests/mochitest/tree/dockids.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+ <head>
+ <link rel="next" href="http://www.mozilla.org">
+ <style>
+ head, link, a { display: block; }
+ link:after { content: "Link to " attr(href); }
+ </style>
+ <script>
+ window.onload = function() {
+ document.documentElement.appendChild(document.createElement("input"));
+
+ var l = document.createElement("link");
+ l.href = "http://www.mozilla.org";
+ l.textContent = "Another ";
+ document.documentElement.appendChild(l);
+
+ l = document.createElement("a");
+ l.href = "http://www.mozilla.org";
+ l.textContent = "Yet another link to mozilla";
+ document.documentElement.appendChild(l);
+ }
+ </script>
+ </head>
+ <body>
+ Hey, I'm a <body> with three links that are not inside me and an input
+ that's not inside me.
+ </body>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_applicationacc.xul b/accessible/tests/mochitest/tree/test_applicationacc.xul
new file mode 100644
index 000000000..5811d00d3
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_applicationacc.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible Application Accessible hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // Note: bug 560239 can be tested if this test runs in standalone mode only.
+
+ var gURL = "../tree/wnd.xul"
+ var gWnd = window.openDialog(gURL, "wnd", "chrome,width=600,height=600");
+
+ function doTest()
+ {
+ // Application accessible should contain two root document accessibles,
+ // one is for browser window, another one is for open dialog window.
+ var accTree = {
+ role: ROLE_APP_ROOT,
+ children: [
+ {
+ role: ROLE_CHROME_WINDOW,
+ name: "Accessibility Chrome Test Harness - Minefield"
+ },
+ {
+ role: ROLE_CHROME_WINDOW,
+ name: "Empty Window"
+ }
+ ]
+ };
+ testAccessibleTree(getApplicationAccessible(), accTree);
+
+ gWnd.close();
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ // We need to open dialog window before accessibility is started.
+ addLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=560239"
+ title="no children of application accessible for windows open before accessibility was started">
+ Mozilla Bug 560239
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/tree/test_aria_globals.html b/accessible/tests/mochitest/tree/test_aria_globals.html
new file mode 100644
index 000000000..771640fcc
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_globals.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test Global ARIA States and Accessible Creation</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var globalIds = [
+ "atomic",
+ "busy",
+ "controls",
+ "describedby",
+ "disabled",
+ "dropeffect",
+ "flowto",
+ "grabbed",
+ "haspopup",
+ "hidden",
+ "invalid",
+ "label",
+ "labelledby",
+ "live",
+ "owns",
+ "relevant"
+ ];
+
+ // Elements having ARIA global state or properties or referred by another
+ // element must be accessible.
+ ok(isAccessible("pawn"),
+ "Must be accessible because referred by another element.");
+
+ for (var idx = 0; idx < globalIds.length; idx++) {
+ ok(isAccessible(globalIds[idx]),
+ "Must be accessible becuase of " + "aria-" + globalIds[idx] +
+ " presence");
+ }
+
+ // Unfocusable elements, having ARIA global state or property with a valid
+ // IDREF value, and an inherited presentation role. A generic accessible
+ // is created (to prevent table cells text jamming).
+ ok(!isAccessible("td_nothing", nsIAccessibleTableCell),
+ "inherited presentation role takes a place");
+
+ for (var idx = 0; idx < globalIds.length; idx++) {
+ ok(isAccessible("td_" + globalIds[idx]),
+ "Inherited presentation role must be ignored becuase of " +
+ "aria-" + globalIds[idx] + " presence");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Update universal ARIA attribute support to latest spec"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=551978">
+ Mozilla Bug 551978
+ </a>
+ <a target="_blank"
+ title="Presentational table related elements referred or having global ARIA attributes must be accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=809751">
+ Mozilla Bug 809751
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- Test that global aria states and properties are enough to cause the
+ creation of accessible objects -->
+ <div id="global_aria_states_and_props" role="group">
+ <span id="pawn"></span>
+ <span id="atomic" aria-atomic="true"></span>
+ <span id="busy" aria-busy="false"></span>
+ <span id="controls" aria-controls="pawn"></span>
+ <span id="describedby" aria-describedby="pawn"></span>
+ <span id="disabled" aria-disabled="true"></span>
+ <span id="dropeffect" aria-dropeffect="move"></span>
+ <span id="flowto" aria-flowto="pawn"></span>
+ <span id="grabbed" aria-grabbed="false"></span>
+ <span id="haspopup" aria-haspopup="false"></span>
+ <span id="hidden" aria-hidden="true"></span>
+ <span id="invalid" aria-invalid="false"></span>
+ <span id="label" aria-label="hi"></span>
+ <span id="labelledby" aria-labelledby="label"></span>
+ <span id="live" aria-live="polite"></span>
+ <span id="owns" aria-owns="pawn"></span>
+ <span id="relevant" aria-relevant="additions"></span>
+ </div>
+
+ <table role="presentation">
+ <tr>
+ <td id="td_nothing"></td>
+ <td id="td_atomic" aria-atomic="true"></td>
+ <td id="td_busy" aria-busy="false"></td>
+ <td id="td_controls" aria-controls="pawn"></td>
+ <td id="td_describedby" aria-describedby="pawn"></td>
+ <td id="td_disabled" aria-disabled="true"></td>
+ <td id="td_dropeffect" aria-dropeffect="move"></td>
+ <td id="td_flowto" aria-flowto="pawn"></td>
+ <td id="td_grabbed" aria-grabbed="false"></td>
+ <td id="td_haspopup" aria-haspopup="false"></td>
+ <td id="td_hidden" aria-hidden="true"></td>
+ <td id="td_invalid" aria-invalid="false"></td>
+ <td id="td_label" aria-label="hi"></td>
+ <td id="td_labelledby" aria-labelledby="label"></td>
+ <td id="td_live" aria-live="polite"></td>
+ <td id="td_owns" aria-owns="pawn"></td>
+ <td id="td_relevant" aria-relevant="additions"></td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_grid.html b/accessible/tests/mochitest/tree/test_aria_grid.html
new file mode 100644
index 000000000..b7fa91cee
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_grid.html
@@ -0,0 +1,279 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML table tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // grid having rowgroups
+
+ var accTree =
+ { TABLE: [
+ { GROUPING: [
+ { ROW: [
+ { GRID_CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] },
+ ] };
+
+ testAccessibleTree("grid", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // crazy grids (mad mix of ARIA and HTML tables)
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ { // div@role="row"
+ role: ROLE_ROW,
+ tagName: "DIV",
+ children: [
+ { // caption text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "caption",
+ children: [ ]
+ },
+ { // th generic accessible
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ { // th text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "header1",
+ children: [ ]
+ }
+ ]
+ },
+ { // td@role="columnheader"
+ role: ROLE_COLUMNHEADER,
+ name: "header2",
+ children: [ { TEXT_LEAF: [ ] } ]
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree("crazy_grid1", accTree);
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ { // tr@role="row"
+ role: ROLE_ROW,
+ tagName: "TR",
+ children: [
+ { // td generic accessible
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ { // td text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "cell1",
+ children: [ ]
+ }
+ ]
+ },
+ { // td@role="gridcell"
+ role: ROLE_GRID_CELL,
+ name: "cell2",
+ children: [ { TEXT_LEAF: [ ] } ]
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree("crazy_grid2", accTree);
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ { // div@role="row"
+ role: ROLE_ROW,
+ children: [
+ { // div@role="gridcell"
+ role: ROLE_GRID_CELL,
+ children: [
+ { // td generic accessible
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ { // text leaf from presentational table
+ role: ROLE_TEXT_LEAF,
+ name: "cell3",
+ children: [ ]
+ }
+ ]
+ },
+ ]
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree("crazy_grid3", accTree);
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ { // div@role="row"
+ role: ROLE_ROW,
+ children: [
+ { // div@role="gridcell"
+ role: ROLE_GRID_CELL,
+ children: [
+ { // table
+ role: ROLE_TABLE,
+ children: [
+ { // tr
+ role: ROLE_ROW,
+ children: [
+ { // td
+ role: ROLE_CELL,
+ children: [
+ { // caption text leaf of presentational table
+ role: ROLE_TEXT_LEAF,
+ name: "caption",
+ children: [ ]
+ },
+ { // td generic accessible
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ { // td text leaf of presentational table
+ role: ROLE_TEXT_LEAF,
+ name: "cell4",
+ children: [ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("crazy_grid4", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // grids that could contain whitespace accessibles but shouldn't.
+
+ var accTree =
+ { TREE_TABLE: [
+ { ROW: [
+ { GRID_CELL: [
+ { TEXT_LEAF: [ ] }
+ ] },
+ { GRID_CELL: [
+ { TEXT_LEAF: [ ] }
+ ] },
+ { GRID_CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] },
+ ] };
+
+ testAccessibleTree("whitespaces-grid", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Support ARIA role rowgroup"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=525909">
+ Mozilla Bug 525909
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="grid" role="grid">
+ <div role="rowgroup">
+ <div role="row">
+ <div role="gridcell">cell</div>
+ </div>
+ </div>
+ </div>
+
+ <div id="crazy_grid1" role="grid">
+ <div role="row">
+ <table role="presentation">
+ <caption>caption</caption>
+ <tr>
+ <th>header1</th>
+ <td role="columnheader">header2</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <div id="crazy_grid2" role="grid">
+ <table role="presentation">
+ <tr role="row">
+ <td id="ct_cell1">cell1</td>
+ <td role="gridcell">cell2</td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="crazy_grid3" role="grid">
+ <div role="row">
+ <div role="gridcell">
+ <table role="presentation">
+ <tr>
+ <td>cell3</td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div id="crazy_grid4" role="grid">
+ <div role="row">
+ <div role="gridcell">
+ <table>
+ <tr>
+ <td>
+ <table role="presentation">
+ <caption>caption</caption>
+ <tr><td>cell4</td></tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div role="treegrid" id="whitespaces-grid">
+ <div role="row" aria-selected="false" tabindex="-1">
+ <span role="gridcell">03:30PM-04:30PM</span>
+ <span role="gridcell" style="font-weight:bold;">test</span>
+ <span role="gridcell">a user1</span>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_imgmap.html b/accessible/tests/mochitest/tree/test_aria_imgmap.html
new file mode 100644
index 000000000..30f2eafe6
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_imgmap.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test usemap elements and ARIA</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ function doPreTest()
+ {
+ waitForImageMap("imagemap", doTest);
+ }
+
+ function doTest()
+ {
+ var accTree = {
+ role: ROLE_IMAGE_MAP,
+ children: [
+ {
+ role: ROLE_ENTRY,
+ name: "first name"
+ },
+ {
+ role: ROLE_ENTRY,
+ name: "last name"
+ },
+ {
+ role: ROLE_RADIOBUTTON,
+ name: "male"
+ },
+ {
+ role: ROLE_RADIOBUTTON,
+ name: "female"
+ },
+ {
+ role: ROLE_CHECKBUTTON,
+ name: "have bike"
+ },
+ {
+ role: ROLE_COMBOBOX,
+ name: "bike model"
+ },
+ {
+ role: ROLE_CHECKBUTTON,
+ name: "have car"
+ },
+ {
+ role: ROLE_CHECKBUTTON,
+ name: "have airplane"
+ },
+ {
+ role: ROLE_PUSHBUTTON,
+ name: "submit"
+ }
+ ]
+ };
+
+ // Test image map tree structure, roles, and names.
+ testAccessibleTree("imagemap", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body>
+
+<a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291"
+ title="Accessible tree of ARIA image maps">
+Mozilla Bug 548291
+</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+</pre>
+
+<img id="imagemap" src="../formimage.png" width="219" height="229" border="0" usemap="#ariaMap">
+<map id="ariaMap" name="ariaMap">
+ <area id="t1" role="textbox" shape="rect" tabindex="0" alt="" title="first name" coords="4,20,108,48" href="#" />
+ <area id="t2" role="textbox" shape="rect" alt="" title="last name" coords="111,21,215,50" href="#" />
+ <area id="rb1" role="radio" aria-checked="true" shape="circle" alt="" title="male" coords="60,75,11" href="#" />
+ <area id="rb2" role="radio" shape="circle" alt="" title="female" coords="73,94,11" href="#" />
+ <area id="cb1" role="checkbox" aria-checked="true" shape="rect" alt="" title="have bike" coords="95,123,118,145" href="#" />
+ <area id="cbox" role="combobox" shape="rect" alt="" title="bike model" coords="120,124,184,146" href="#" />
+ <area id="cb2" role="checkbox" shape="rect" alt="" title="have car" coords="90,145,114,164" href="#" />
+ <area id="cb3" role="checkbox" shape="rect" alt="" title="have airplane" coords="130,163,152,184" href="#" />
+ <area id="b1" role="button" shape="rect" alt="" title="submit" coords="4,198,67,224" href="#" />
+</map>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_list.html b/accessible/tests/mochitest/tree/test_aria_list.html
new file mode 100644
index 000000000..b53962709
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_list.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>ARIA lists</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // list
+
+ var accTree =
+ { LIST: [
+ { LISTITEM: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+
+ testAccessibleTree("list", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // crazy list (mad mix of ARIA and HTML)
+
+ accTree = { // div@role="list"
+ role: ROLE_LIST,
+ children: [
+ { // li
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ { // li text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "item1",
+ children: [ ]
+ }
+ ]
+ },
+ { // li@role="listitem"
+ role: ROLE_LISTITEM,
+ children: [
+ { // text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "item2",
+ children: [ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("crazy_list", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Build the context dependent tree"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461">
+ Mozilla Bug 804461
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="list" role="list">
+ <div role="listitem">item1</div>
+ </div>
+
+ <div id="crazy_list" role="list">
+ <ul role="presentation">
+ <li>item1</li>
+ <li role="listitem">item2</li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_menu.html b/accessible/tests/mochitest/tree/test_aria_menu.html
new file mode 100644
index 000000000..aa95b652b
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_menu.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test accessible tree when ARIA role menuitem is used</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Menuitem with no popup.
+ tree =
+ { SECTION: [ // container
+ { MENUPOPUP: [ // menu
+ { MENUITEM: [
+ { STATICTEXT: [] }, // bullet
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ testAccessibleTree("menu", tree);
+
+ // Menuitem with explicit no popup.
+ tree =
+ { SECTION: [ // container
+ { MENUPOPUP: [ // menu
+ { MENUITEM: [
+ { STATICTEXT: [] }, // bullet
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ testAccessibleTree("menu_nopopup", tree);
+
+ // Menuitem with popup.
+ tree =
+ { SECTION: [ // container
+ { MENUPOPUP: [ // menu
+ { PARENT_MENUITEM: [ // menuitem with aria-haspopup="true"
+ { STATICTEXT: [] }, // bullet
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ testAccessibleTree("menu_popup", tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=786566"
+ title="ARIA menuitem acting as submenu should have PARENT_MENUITEM role">
+ Mozilla Bug 786566
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="menu">
+ <ul role="menu">
+ <li role="menuitem">Normal Menu</li>
+ </ul>
+ </div>
+
+ <div id="menu_nopopup">
+ <ul role="menu">
+ <li role="menuitem" aria-haspopup="false">Menu with explicit no popup</li>
+ </ul>
+ </div>
+
+ <div id="menu_popup">
+ <ul role="menu">
+ <li role="menuitem" aria-haspopup="true">Menu with popup</li>
+ </ul>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_owns.html b/accessible/tests/mochitest/tree/test_aria_owns.html
new file mode 100644
index 000000000..da4f52064
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_owns.html
@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>@aria-owns attribute testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+ ////////////////////////////////////////////////////////////////////////////
+
+ //enableLogging("tree,verbose"); // debug stuff
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ var tree =
+ { SECTION: [ // t1_1
+ { HEADING: [ // t1_2
+ // no kids, no loop
+ ] }
+ ] };
+ testAccessibleTree("t1_1", tree);
+
+ tree =
+ { SECTION: [ // t2_1
+ { GROUPING: [ // t2_2
+ { HEADING: [ // t2_3
+ // no kids, no loop
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("t2_1", tree);
+
+ tree =
+ { SECTION: [ // t3_3
+ { GROUPING: [ // t3_1
+ { NOTE: [ // t3_2
+ { HEADING: [ // DOM child of t3_2
+ // no kids, no loop
+ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("t3_3", tree);
+
+ tree =
+ { SECTION: [ // t4_1
+ { GROUPING: [ // DOM child of t4_1, aria-owns ignored
+ // no kids, no loop
+ ] }
+ ] };
+ testAccessibleTree("t4_1", tree);
+
+ tree =
+ { SECTION: [ // t5_1
+ { GROUPING: [ // DOM child of t5_1
+ { NOTE: [ // t5_2
+ { HEADING: [ // DOM child of t5_2
+ { FORM: [ // t5_3
+ { TOOLTIP: [ // DOM child of t5_3
+ // no kids, no loop
+ ]}
+ ]}
+ ]}
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("t5_1", tree);
+
+ tree =
+ { SECTION: [ // t6_1
+ { RADIOBUTTON: [ ] },
+ { CHECKBUTTON: [ ] }, // t6_3, rearranged by aria-owns
+ { PUSHBUTTON: [ ] }, // t6_2, rearranged by aria-owns
+ ] };
+ testAccessibleTree("t6_1", tree);
+
+ tree =
+ { SECTION: [ // ariaowns_container
+ { SECTION: [ // ariaowns_self
+ { SECTION: [ // ariaowns_uncle
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("ariaowns_container", tree);
+
+ tree =
+ { TABLE: [
+ { ROW: [
+ { GRID_CELL: [
+ { TEXT_LEAF: [] }
+ ] },
+ { GRID_CELL: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] },
+ { ROW: [
+ { GRID_CELL: [
+ { TEXT_LEAF: [] }
+ ] },
+ { GRID_CELL: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("grid", tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+ </script>
+</head>
+
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- simple loop -->
+ <div id="t1_1" aria-owns="t1_2"></div>
+ <div id="t1_2" aria-owns="t1_1" role="heading"></div>
+
+ <!-- loop -->
+ <div id="t2_2" aria-owns="t2_3" role="group"></div>
+ <div id="t2_1" aria-owns="t2_2"></div>
+ <div id="t2_3" aria-owns="t2_1" role="heading"></div>
+
+ <!-- loop #2 -->
+ <div id="t3_1" aria-owns="t3_2" role="group"></div>
+ <div id="t3_2" role="note">
+ <div aria-owns="t3_3" role="heading"></div>
+ </div>
+ <div id="t3_3" aria-owns="t3_1"></div>
+
+ <!-- self loop -->
+ <div id="t4_1"><div aria-owns="t4_1" role="group"></div></div>
+
+ <!-- natural and aria-owns hierarchy -->
+ <div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div>
+ <div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
+ <div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
+
+ <!-- rearrange children -->
+ <div id="t6_1" aria-owns="t6_3 t6_2">
+ <div id="t6_2" role="button"></div>
+ <div id="t6_3" role="checkbox"></div>
+ <div role="radio"></div>
+ </div>
+
+ <div id="ariaowns_container">
+ <div id="ariaowns_self"
+ aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
+ </div>
+ <div id="ariaowns_uncle"></div>
+
+ <!-- grid -->
+ <div aria-owns="grid-row2" role="grid" id="grid">
+ <div role="row">
+ <div role="gridcell">cell 1,1</div>
+ <div role="gridcell">cell 1,2</div>
+ </div>
+ </div>
+ <div role="row" id="grid-row2">
+ <div role="gridcell">cell 2,1</div>
+ <div role="gridcell">cell 2,2</div>
+ </div>
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_presentation.html b/accessible/tests/mochitest/tree/test_aria_presentation.html
new file mode 100644
index 000000000..d8133ee9e
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_presentation.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test accessible tree when ARIA role presentation is used</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Presentation role don't allow accessible.
+ var tree =
+ { SECTION: [ // container
+ { TEXT_LEAF: [ ] }, // child text of 'presentation' node
+ { TEXT_LEAF: [ ] } // child text of 'none' node
+ ] };
+ testAccessibleTree("div_cnt", tree);
+
+ // Focusable element, 'presentation' and 'none' roles are ignored.
+ tree =
+ { SECTION: [ // container
+ { PUSHBUTTON: [ // button having 'presentation' role
+ { TEXT_LEAF: [ ] }
+ ] },
+ { PUSHBUTTON: [ // button having 'none' role
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("btn_cnt", tree);
+
+ // Presentation table, no table structure is exposed.
+ tree =
+ { SECTION: [ // container
+ { TEXT_CONTAINER: [ // td generic accessible inside 'presentation' table
+ { TEXT_LEAF: [ ] } // cell text
+ ] },
+ { TEXT_CONTAINER: [ // td generic accessible inside 'none' table
+ { TEXT_LEAF: [ ] } // cell text
+ ] }
+ ] };
+ testAccessibleTree("tbl_cnt", tree);
+
+ // Focusable table, 'presentation' and 'none' roles are ignored.
+ tree =
+ { SECTION: [ // container
+ { TABLE: [ // table having 'presentation' role
+ { ROW: [ // tr
+ { CELL: [ // td
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] },
+ { TABLE: [ // table having 'none' role
+ { ROW: [ // tr
+ { CELL: [ // td
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("tblfocusable_cnt", tree);
+
+ // Presentation list, expose generic accesisble for list items.
+ tree =
+ { SECTION: [ // container
+ { TEXT_CONTAINER: [ // li generic accessible inside 'presentation' role
+ { TEXT_LEAF: [ ] } // li text
+ ] },
+ { TEXT_CONTAINER: [ // li generic accessible inside 'none' role
+ { TEXT_LEAF: [ ] } // li text
+ ] }
+ ] };
+ testAccessibleTree("list_cnt", tree);
+
+ // Has ARIA globals or referred by ARIA relationship, role='presentation'
+ // and role='none' are ignored.
+ tree =
+ { SECTION: [ // container
+ { LABEL: [ // label, has aria-owns
+ { TEXT_LEAF: [ ] },
+ { LABEL: [ // label, referenced by aria-owns
+ { TEXT_LEAF: [ ] }
+ ] },
+ ] },
+ { LABEL: [ // label, has aria-owns
+ { TEXT_LEAF: [ ] },
+ { LABEL: [ // label, referenced by aria-owns
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("airaglobalprop_cnt", tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291"
+ title="Accessible tree of ARIA image maps">
+ Bug 548291
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=666504"
+ title="Ignore role presentation on focusable elements">
+ Bug 666504
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=971212"
+ title="Implement ARIA role=none">
+ Bug 971212
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="div_cnt"><div role="presentation">t</div><div role="none">t</div></div>
+
+ <div id="btn_cnt"><button role="presentation">btn</button><button role="none">btn</button></div>
+
+ <div id="tbl_cnt">
+ <table role="presentation">
+ <tr>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table role="none">
+ <tr>
+ <td>cell</td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="tblfocusable_cnt">
+ <table role="presentation" tabindex="0">
+ <tr>
+ <td>cell</td>
+ </tr>
+ </table>
+ <table role="none" tabindex="0">
+ <tr>
+ <td>cell</td>
+ </tr>
+ </table>
+ </div>
+
+ <div id="list_cnt">
+ <ul role="presentation">
+ <li>item</li>
+ </ul>
+ <ul role="none">
+ <li>item</li>
+ </ul>
+ </div>
+
+ <div id="airaglobalprop_cnt"><label
+ role="presentation" aria-owns="ariaowned">has aria-owns</label><label
+ role="presentation" id="ariaowned">referred by aria-owns</label><label
+ role="none" aria-owns="ariaowned2">has aria-owns</label><label
+ role="none" id="ariaowned2">referred by aria-owns</label></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_aria_table.html b/accessible/tests/mochitest/tree/test_aria_table.html
new file mode 100644
index 000000000..64d3bd891
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_aria_table.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>ARIA table tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // table having rowgroups
+
+ var accTree =
+ { TABLE: [
+ { GROUPING: [
+ { ROW: [
+ { CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] },
+ ] };
+
+ testAccessibleTree("table", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="support ARIA table and cell roles"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364">
+ Bug 1173364
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="table" role="table">
+ <div role="rowgroup">
+ <div role="row">
+ <div role="cell">cell</div>
+ </div>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_brokencontext.html b/accessible/tests/mochitest/tree/test_brokencontext.html
new file mode 100644
index 000000000..d63b4ae11
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_brokencontext.html
@@ -0,0 +1,265 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Broken context hierarchy</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Return true if TD element has a generic accessible.
+ */
+ function isTDGeneric(aID)
+ {
+ return isAccessible(aID) && !isAccessible(aID, nsIAccessibleTableCell);
+ }
+
+ function checkIfNotAccessible(aID)
+ {
+ ok(!isAccessible(aID), "'" + aID + "' shouldn't be accessible");
+ }
+ function checkIfTDGeneric(aID)
+ {
+ ok(isTDGeneric(aID), "'" + aID + "' shouldn't have cell accessible");
+ }
+
+ function doTest()
+ {
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML table elements outside table context.
+
+ // HTML table role="presentation"
+ checkIfNotAccessible("tr_in_presentation_table");
+ checkIfTDGeneric("th_in_presentation_table");
+ checkIfTDGeneric("td_in_presentation_table");
+
+ // HTML table role="button"
+ var tree =
+ { PUSHBUTTON: [ // table
+ { NOTHING: [ // tr
+ { NOTHING: [ // th
+ { TEXT_LEAF: [ ] }
+ ] },
+ { NOTHING: [ // td
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("button_table", tree);
+
+ // HTML table display:inline
+ checkIfNotAccessible("inline_table1");
+ checkIfNotAccessible("tr_in_inline_table1");
+ checkIfTDGeneric("td1_in_inline_table1");
+ checkIfTDGeneric("td2_in_inline_table1");
+
+ // HTML table display:inline inside table. We shouldn't be fooled
+ // by the outside table and shouldn't create table accessible and table cell
+ // accessible in this case.
+ checkIfNotAccessible("inline_table2");
+ checkIfNotAccessible("tr_in_inline_table2");
+ checkIfTDGeneric("td_in_inline_table2");
+
+ // HTML table display:block inside table.
+ checkIfNotAccessible("block_table");
+ checkIfNotAccessible("tr_in_block_table");
+ checkIfTDGeneric("td_in_block_table");
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML list elements outside list context.
+
+ ok(!isAccessible("presentation_ul"),
+ "presentational ul shouldn't be accessible");
+ ok(isAccessible("item_in_presentation_ul"),
+ "li in presentational ul should have generic accessible");
+ ok(isAccessible("styleditem_in_presentation_ul"),
+ "list styled span in presentational ul should have generic accessible");
+
+ ok(!isAccessible("presentation_ol"),
+ "presentational ol shouldn't be accessible");
+ ok(isAccessible("item_in_presentation_ol"),
+ "li in presentational ol should have generic accessible");
+
+ ok(!isAccessible("presentation_dl"),
+ "presentational dl shouldn't be accessible");
+ ok(!isAccessible("dt_in_presentation_dl"),
+ "dt in presentational dl shouldn't be accessible");
+ ok(!isAccessible("dd_in_presentation_dl"),
+ "dd in presentational dl shouldn't be accessible");
+
+ tree =
+ { PUSHBUTTON: [ // ul
+ { NOTHING: [ // li
+ { STATICTEXT: [ ] },
+ { TEXT_LEAF: [ ] }
+ ] },
+ { NOTHING: [ // span styled as a list
+ { STATICTEXT: [ ] },
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("button_ul", tree);
+
+ tree =
+ { PUSHBUTTON: [ // ol
+ { NOTHING: [ // li
+ { STATICTEXT: [ ] },
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("button_ol", tree);
+
+ tree =
+ { PUSHBUTTON: [ // dl
+ { NOTHING: [ // dt
+ { TEXT_LEAF: [ ] }
+ ] },
+ { NOTHING: [ // dd
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+ testAccessibleTree("button_dl", tree);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Styled as HTML table elements, accessible is created by tag name
+
+ tree =
+ { LINK: [ // a
+ { TEXT_LEAF: [ ] }
+ ] };
+ testAccessibleTree("a_as_td", tree);
+
+ tree =
+ { HEADING: [
+ { TEXT_LEAF: [ ] }
+ ] };
+ testAccessibleTree("h1_as_td", tree);
+ testAccessibleTree("h2_as_td", tree);
+ testAccessibleTree("h3_as_td", tree);
+ testAccessibleTree("h4_as_td", tree);
+ testAccessibleTree("h5_as_td", tree);
+ testAccessibleTree("h6_as_td", tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=706849"
+ title="Create accessible by tag name as fallback if table descendant style is used out of table context">
+ Bug 706849
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461"
+ title="Build the context dependent tree ">
+ Bug 804461
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=945435"
+ title="Create generic accessible for td to not jamm the cell text">
+ Bug 945435
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- HTML table elements out of table -->
+ <table role="presentation">
+ <tr id="tr_in_presentation_table">
+ <th id="th_in_presentation_table">not a header</th>
+ <td id="td_in_presentation_table">not a cell</td>
+ </tr>
+ </table>
+
+ <table role="button" id="button_table">
+ <tr id="tr_in_button_table">
+ <th id="th_in_button_table">not a header</th>
+ <td id="td_in_button_table">not a cell</td>
+ </tr>
+ </table>
+
+ <table id="inline_table1" border="1" style="display:inline">
+ <tr id="tr_in_inline_table1">
+ <td id="td1_in_inline_table1">table1 cell1</td>
+ <td id="td2_in_inline_table1">table1 cell2</td>
+ </tr>
+ </table>
+
+ <table id="table_containing_inlinetable"><tr><td>
+ <table id="inline_table2" border="1" style="display:inline">
+ <tr id="tr_in_inline_table2">
+ <td id="td_in_inline_table2">cell</td>
+ </tr>
+ </table>
+ </td></tr></table>
+
+ <table>
+ <tr>
+ <td style="display:block">
+ <table id="block_table" style="display:inline">
+ <tr id="tr_in_block_table">
+ <td id="td_in_block_table">cell0</td>
+ </tr>
+ </table>
+ </td>
+ <td>cell1</td>
+ </tr>
+ </table>
+
+ <!-- HTML list elements out of list -->
+ <ul role="presentation" id="presentation_ul">
+ <li id="item_in_presentation_ul">item</li>
+ <span id="styleditem_in_presentation_ul"
+ style="display:list-item">Oranges</span>
+ </ul>
+
+ <ol role="presentation" id="presentation_ol">
+ <li id="item_in_presentation_ol">item</li>
+ </ol>
+
+ <dl role="presentation" id="presentation_dl">
+ <dt id="dt_in_presentation_dl">term</dt>
+ <dd id="dd_in_presentation_dl">definition</dd>
+ </dl>
+
+ <ul role="button" id="button_ul">
+ <li id="item_in_button_ul">item</li>
+ <span id="styleditem_in_button_ul"
+ style="display:list-item">Oranges</span>
+ </ul>
+
+ <ol role="button" id="button_ol">
+ <li id="item_in_button_ul">item</li>
+ </ol>
+
+ <dl role="button" id="button_dl">
+ <dt id="dt_in_button_dl">term</ld>
+ <dd id="dd_in_button_dl">definition</dd>
+ </dl>
+
+ <!-- styled as HTML table elements -->
+ <a id="a_as_td" style="display:table-cell;" href="http://www.google.com">Google</a>
+ <h1 id="h1_as_td" style="display: table-cell;">h1</h1>
+ <h2 id="h2_as_td" style="display: table-cell;">h2</h2>
+ <h3 id="h3_as_td" style="display: table-cell;">h3</h3>
+ <h4 id="h4_as_td" style="display: table-cell;">h4</h4>
+ <h5 id="h5_as_td" style="display: table-cell;">h5</h5>
+ <h6 id="h6_as_td" style="display: table-cell;">h6</h6>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_button.xul b/accessible/tests/mochitest/tree/test_button.xul
new file mode 100644
index 000000000..39d189f41
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_button.xul
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL button hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // button
+
+ var accTree = {
+ role: ROLE_PUSHBUTTON,
+ name: "hello",
+ children: [ ]
+ };
+ testAccessibleTree("button1", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // toolbarbutton
+
+ var accTree = {
+ role: ROLE_PUSHBUTTON,
+ name: "hello",
+ children: [ ]
+ };
+ testAccessibleTree("button2", accTree);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
+ title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
+ Mozilla Bug 249292
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <button id="button1" label="hello"/>
+ <toolbarbutton id="button2" label="hello"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/tree/test_canvas.html b/accessible/tests/mochitest/tree/test_canvas.html
new file mode 100644
index 000000000..314ad84c7
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_canvas.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=495912
+-->
+<head>
+ <title>File Input Control tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accTree =
+ { CANVAS: [
+ { CHECKBUTTON: [] },
+ { ENTRY: [] }
+ ] };
+
+ testAccessibleTree("canvas", accTree);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Expose alternative content in Canvas element to ATs"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912">Mozilla Bug 495912</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <canvas id="canvas" tabindex="0"><input type="checkbox"><input></canvas>
+
+ <script type="text/javascript">
+ var c=document.getElementById("canvas");
+ var cxt=c.getContext("2d");
+ cxt.fillStyle="#005500";
+ cxt.fillRect(0,0,150,75);
+ </script>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_combobox.xul b/accessible/tests/mochitest/tree/test_combobox.xul
new file mode 100644
index 000000000..79ce866dc
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_combobox.xul
@@ -0,0 +1,291 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL menulist and textbox @autocomplete hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // menulist
+
+ var selectedOptionChildren = [];
+ if (MAC) {
+ // checkmark is part of the Mac menu styling
+ selectedOptionChildren = [{
+ role: ROLE_STATICTEXT,
+ children: []
+ }];
+ }
+
+ var accTree = {
+ role: ROLE_COMBOBOX,
+ children: [
+ {
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: selectedOptionChildren
+ },
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: []
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("menulist", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // editable menulist
+
+ accTree = {
+ role: ROLE_COMBOBOX,
+ children: [
+ {
+ role: ROLE_ENTRY,
+ children: [
+ // no text leaf accessible for text node
+ ]
+ },
+ {
+ role: ROLE_COMBOBOX_LIST, // context menu
+ children: []
+ },
+ {
+ role: ROLE_PUSHBUTTON, // dropmarker
+ children: []
+ },
+ {
+ role: ROLE_COMBOBOX_LIST, // option list
+ children: [
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: []
+ },
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: []
+ }
+ ]
+ }
+ ]
+ };
+
+ if (!MAC) {
+ testAccessibleTree("menulist2", accTree);
+ } else {
+ todo(false, "Make this test pass on OSX (bug 551957)");
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // textbox@type=autocomplete #1 (history)
+
+ accTree = {
+ // textbox
+ role: ROLE_AUTOCOMPLETE,
+ children: [
+ {
+ // html:input
+ role: ROLE_ENTRY,
+ children: [
+ {
+ // #text
+ role: ROLE_TEXT_LEAF,
+ name: "http://mochi.test:8888/redirect-a11y.html",
+ children: []
+ }
+ ]
+ },
+ {
+ // xul:menupopup
+ role: ROLE_COMBOBOX_LIST, // context menu popup
+ children: []
+ }
+ ]
+ };
+
+ // XPFE and Toolkit autocomplete widgets differ.
+ var ac1h = document.getElementById("autocomplete");
+ if ("clearResults" in ac1h) {
+ SimpleTest.ok(true, "Testing (Old) XPFE autocomplete widget. (ac1h)");
+
+ // Popup is always created.
+ accTree.children.push(
+ {
+ // xul:panel
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ {
+ // xul:tree
+ role: ROLE_TABLE,
+ children: [
+ {
+ // xul:treecols
+ role: ROLE_LIST,
+ children: [
+ {
+ // xul:treecol
+ role: ROLE_COLUMNHEADER,
+ children: []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ );
+ } else {
+ SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget. (ac1h)");
+
+ // Popup is lazily created, so not present in this case.
+ }
+
+ testAccessibleTree("autocomplete", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // textbox@type=autocomplete #2 (child menupoup)
+
+ accTree = {
+ // textbox
+ role: ROLE_AUTOCOMPLETE,
+ children: [
+ {
+ // menupopup
+ role: ROLE_COMBOBOX_LIST, // autocomplete menu popup
+ children: [
+ {
+ // menuitem
+ role: ROLE_COMBOBOX_OPTION,
+ children: []
+ }
+ ]
+ },
+ {
+ // html:input
+ role: ROLE_ENTRY,
+ children: [
+ // no text leaf accessible for text node
+ ]
+ },
+ {
+ // xul:menupopup
+ role: ROLE_COMBOBOX_LIST, // context menu popup
+ children: []
+ }
+ ]
+ };
+
+ // XPFE and Toolkit autocomplete widgets differ.
+ var ac2cmp = document.getElementById("autocomplete2");
+ if ("clearResults" in ac2cmp) {
+ SimpleTest.ok(true, "Testing (Old) XPFE autocomplete widget. (ac2mp)");
+
+ // Popup is always created.
+ accTree.children.push(
+ {
+ // xul:panel
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ {
+ // xul:tree
+ role: ROLE_TABLE,
+ children: [
+ {
+ // xul:treecols
+ role: ROLE_LIST,
+ children: [
+ {
+ // xul:treecol
+ role: ROLE_COLUMNHEADER,
+ children: []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ );
+ } else {
+ SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget. (ac2mp)");
+
+ // Popup is lazily created, so not present in this case.
+ }
+
+ testAccessibleTree("autocomplete2", accTree);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
+ title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
+ Mozilla Bug 249292
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660"
+ title="Cache rendered text on a11y side">
+ Mozilla Bug 626660
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menulist id="menulist">
+ <menupopup>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menulist>
+
+ <menulist id="menulist2" editable="true">
+ <menupopup>
+ <menuitem label="item"/>
+ <menuitem label="item"/>
+ </menupopup>
+ </menulist>
+
+ <textbox id="autocomplete" type="autocomplete"
+ autocompletesearch="unifiedcomplete"
+ value="http://mochi.test:8888/redirect-a11y.html"/>
+
+ <textbox id="autocomplete2" type="autocomplete">
+ <menupopup>
+ <menuitem label="item1"/>
+ </menupopup>
+ </textbox>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/tree/test_cssflexbox.html b/accessible/tests/mochitest/tree/test_cssflexbox.html
new file mode 100644
index 000000000..f3786ac92
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_cssflexbox.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>CSS flexbox tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // Ensure that flexbox ordering and absolute positioning do not affect
+ // the accessibility tree.
+ // Note that there is no accessible for a div with display:flex style.
+ var accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // Bug 1277559. Button outside the flexed content
+ role: ROLE_PUSHBUTTON,
+ name: "Button"
+ },
+ { // Visually first button in the 3 button row
+ role: ROLE_PUSHBUTTON,
+ name: "First"
+ },
+ { // Flushed right third button in the 3 button row
+ role: ROLE_PUSHBUTTON,
+ name: "Second"
+ },
+ { // Middle button in the 3 button row
+ role: ROLE_PUSHBUTTON,
+ name: "Third"
+ }, // end bug 1277559
+ { // Bug 962558: DOM first, Order 2.
+ role: ROLE_PUSHBUTTON,
+ name: "two, tab first"
+ },
+ { // DOM order second, flex order 1
+ role: ROLE_PUSHBUTTON,
+ name: "one, tab second"
+ } // end bug 962558
+ ]
+ };
+ testAccessibleTree("flex_elements", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="flex_elements">
+ <button type="button">Button</button>
+ <div style="position: relative; display: flex; width: 200px;">
+ <button type="button" style="order: 1">First</button>
+ <button type="button" style="order: 2; position: absolute; right: 0">Second</button>
+ <button type="button" style="order: 3">Third</button>
+ </div>
+ <div style="display: flex">
+ <button id="two" style="order: 2">two, tab first</button>
+ <button id="one" style="order: 1">one, tab second</button>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_cssoverflow.html b/accessible/tests/mochitest/tree/test_cssoverflow.html
new file mode 100644
index 000000000..c67128fbc
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_cssoverflow.html
@@ -0,0 +1,146 @@
+<html>
+
+<head>
+ <title>CSS overflow testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ a.link:focus {
+ overflow: scroll;
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function focusAnchor(aID)
+ {
+ this.linkNode = getNode(aID);
+ this.link = getAccessible(this.linkNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
+ ];
+
+ this.invoke = function focusAnchor_invoke()
+ {
+ this.linkNode.focus();
+ }
+
+ this.check = function focusAnchor_check(aEvent)
+ {
+ todo_is(this.link, aEvent.accessible,
+ "Focus should be fired against new link accessible!");
+ }
+
+ this.getID = function focusAnchor_getID()
+ {
+ return "focus a:focus{overflow:scroll} #1";
+ }
+ }
+
+ function tabAnchor(aID)
+ {
+ this.linkNode = getNode(aID);
+ this.link = getAccessible(this.linkNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
+ ];
+
+ this.invoke = function tabAnchor_invoke()
+ {
+ synthesizeKey("VK_TAB", { shiftKey: false });
+ }
+
+ this.check = function tabAnchor_check(aEvent)
+ {
+ todo_is(this.link, aEvent.accessible,
+ "Focus should be fired against new link accessible!");
+ }
+
+ this.getID = function tabAnchor_getID()
+ {
+ return "focus a:focus{overflow:scroll} #2";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ // Shift+Tab not working, and a test timeout, bug 746977
+ if (MAC) {
+ todo(false, "Shift+tab isn't working on OS X, needs to be disabled until bug 746977 is fixed!");
+ SimpleTest.finish();
+ return;
+ }
+
+ gQueue = new eventQueue();
+
+ // CSS 'overflow: scroll' property setting and unsetting causes accessible
+ // recreation (and fire show/hide events). For example, the focus and
+ // blur of HTML:a with ':focus {overflow: scroll; }' CSS style causes its
+ // accessible recreation. The focus event should be fired on new
+ // accessible.
+ gQueue.push(new focusAnchor("a"));
+ gQueue.push(new tabAnchor("a2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=591163"
+ title="mochitest for bug 413777: focus the a:focus {overflow: scroll;} shouldn't recreate HTML a accessible">
+ Mozilla Bug 591163
+ </a><br>
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275
+ </a><br>
+ <a target="_blank"
+ title="Text control frames should accept dynamic changes to the CSS overflow property"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=686247">
+ Mozilla Bug 686247
+ </a><br>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div>
+ <a id="a" class="link" href="www">link</a>
+ </div>
+ <div>
+ <a id="a2" class="link" href="www">link2</a>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_dochierarchy.html b/accessible/tests/mochitest/tree/test_dochierarchy.html
new file mode 100644
index 000000000..0104c6aba
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_dochierarchy.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test document hierarchy</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // tabDoc and testDoc are different documents depending on whether test
+ // is running in standalone mode or not.
+
+ var root = getRootAccessible();
+ var tabDoc = window.parent ?
+ getAccessible(window.parent.document, [nsIAccessibleDocument]) :
+ getAccessible(document, [nsIAccessibleDocument]);
+ var testDoc = getAccessible(document, [nsIAccessibleDocument]);
+ var iframeDoc = getAccessible("iframe").firstChild.
+ QueryInterface(nsIAccessibleDocument);
+
+ is(root.parentDocument, null,
+ "Wrong parent document of root accessible");
+ ok(root.childDocumentCount >= 1,
+ "Wrong child document count of root accessible");
+
+ var tabDocumentFound = false;
+ for (var i = 0; i < root.childDocumentCount && !tabDocumentFound; i++) {
+ tabDocumentFound = root.getChildDocumentAt(i) == tabDoc;
+ }
+ ok(tabDocumentFound,
+ "Tab document not found in children of the root accessible");
+
+ is(tabDoc.parentDocument, root,
+ "Wrong parent document of tab document");
+ is(tabDoc.childDocumentCount, 1,
+ "Wrong child document count of tab document");
+ is(tabDoc.getChildDocumentAt(0), (tabDoc == testDoc ? iframeDoc : testDoc),
+ "Wrong child document at index 0 of tab document");
+
+ if (tabDoc != testDoc) {
+ is(testDoc.parentDocument, tabDoc,
+ "Wrong parent document of test document");
+ is(testDoc.childDocumentCount, 1,
+ "Wrong child document count of test document");
+ is(testDoc.getChildDocumentAt(0), iframeDoc,
+ "Wrong child document at index 0 of test document");
+ }
+
+ is(iframeDoc.parentDocument, (tabDoc == testDoc ? tabDoc : testDoc),
+ "Wrong parent document of iframe document");
+ is(iframeDoc.childDocumentCount, 0,
+ "Wrong child document count of iframe document");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=592913"
+ title="Provide a way to quickly determine whether an accessible object is a descendant of a tab document">
+ Mozilla Bug 592913
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe src="about:mozilla" id="iframe"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_dockids.html b/accessible/tests/mochitest/tree/test_dockids.html
new file mode 100644
index 000000000..943ac18b3
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_dockids.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test document hierarchy</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose");
+ function doTest()
+ {
+ var tree =
+ { DOCUMENT: [
+ { TEXT_CONTAINER: [ // head
+ { TEXT_CONTAINER: [ // link
+ { STATICTEXT: [] }, // generated content
+ { STATICTEXT: [] } // generated content
+ ] }
+ ] },
+ { TEXT_LEAF: [ ] }, // body text
+ { ENTRY: [ ] }, // input under document element
+ { TEXT_CONTAINER: [ // link under document element
+ { TEXT_LEAF: [ ] }, // link content
+ { STATICTEXT: [ ] }, // generated content
+ { STATICTEXT: [ ] } // generated content
+ ] },
+ { LINK: [ // anchor under document element
+ { TEXT_LEAF: [ ] } // anchor content
+ ] },
+ ] };
+ testAccessibleTree(getNode("iframe").contentDocument, tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887"
+ title="Elements appended outside the body aren't accessible">
+ Mozilla Bug 608887
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe src="dockids.html" id="iframe"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_filectrl.html b/accessible/tests/mochitest/tree/test_filectrl.html
new file mode 100644
index 000000000..f989d1eea
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_filectrl.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=483573
+-->
+<head>
+ <title>File Input Control tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accTree = {
+ role: ROLE_TEXT_CONTAINER,
+ children: [
+ {
+ role: ROLE_PUSHBUTTON
+ },
+ {
+ role: ROLE_LABEL,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ }
+ ],
+ },
+ ]
+ };
+ testAccessibleTree("filectrl", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Expose HTML5 video and audio elements' embedded controls through accessibility APIs"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input type="file" id="filectrl" />
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_formctrl.html b/accessible/tests/mochitest/tree/test_formctrl.html
new file mode 100644
index 000000000..c5d5bc0b1
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_formctrl.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>HTML form controls tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // input@type="checkbox"
+ var accTree = {
+ role: ROLE_CHECKBUTTON,
+ children: [ ]
+ };
+
+ testAccessibleTree("checkbox", accTree);
+
+ // input@type="radio"
+ accTree = {
+ role: ROLE_RADIOBUTTON,
+ children: [ ]
+ };
+
+ testAccessibleTree("radio", accTree);
+
+ // input@type="button" and input@type="submit"
+ // button
+ accTree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF // XXX Bug 567203
+ }
+ ]
+ };
+
+ testAccessibleTree("btn1", accTree);
+ testAccessibleTree("submit", accTree);
+ testAccessibleTree("btn2", accTree);
+
+ // input@type="image"
+ accTree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_STATICTEXT
+ }
+ ]
+ };
+ testAccessibleTree("image_submit", accTree);
+
+ // input@type="range"
+ accTree = { SLIDER: [ ] };
+ testAccessibleTree("range", accTree);
+
+ // input@type="number"
+ accTree =
+ { SPINBUTTON: [
+ { ENTRY: [ ] },
+ { PUSHBUTTON: [ ] },
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree("number", accTree);
+
+ // output
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ };
+ testAccessibleTree("output", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix O(n^2) access to all the children of a container"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
+ Bug 342045
+ </a>
+ <a target="_blank"
+ title="add test for role of input type='image'"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=524521">
+ Bug 524521
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
+ title="make HTML <output> accessible">
+ Bug 558036
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
+ title="make HTML5 input@type=range element accessible">
+ Bug 559764
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input type="checkbox" id="checkbox">
+ <input type="radio" id="radio">
+ <input type="button" value="button" id="btn1">
+ <button id="btn2">button</button>
+
+ <input type="submit" id="submit">
+ <input type="image" id="image_submit">
+ <input type="range" id="range">
+ <input type="number" id="number">
+ <output id="output">1337</output>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_formctrl.xul b/accessible/tests/mochitest/tree/test_formctrl.xul
new file mode 100644
index 000000000..34dc3795e
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_formctrl.xul
@@ -0,0 +1,130 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<!-- Firefox toolbar -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL checkbox and radio hierarchy tests">
+
+ <!-- Firefox toolbar -->
+ <script type="application/javascript"
+ src="chrome://browser/content/browser.js"/>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ // checkbox
+ var accTree = {
+ role: ROLE_CHECKBUTTON,
+ children: [ ]
+ };
+
+ testAccessibleTree("checkbox", accTree);
+
+ // radiogroup
+ accTree = {
+ role: ROLE_RADIO_GROUP,
+ children: [
+ {
+ role: ROLE_RADIOBUTTON,
+ children: [ ]
+ },
+ {
+ role: ROLE_RADIOBUTTON,
+ children: [ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("radiogroup", accTree);
+
+ // toolbar
+ accTree = {
+ role: ROLE_TOOLBAR,
+ name: "My toolbar",
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ name: "hello",
+ children: [ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("toolbar", accTree);
+
+ // toolbar
+ accTree = {
+ role: ROLE_TOOLBAR,
+ name: "My second toolbar",
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ name: "hello",
+ children: [ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("toolbar2", accTree);
+
+ if (!SEAMONKEY)
+ testAccessibleTree("tb_customizable", { TOOLBAR: [] });
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"
+ title="Fix O(n^2) access to all the children of a container">
+ Mozilla Bug 342045
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <checkbox id="checkbox" label="checkbox"/>
+ <radiogroup id="radiogroup">
+ <radio label="radio1"/>
+ <radio label="radio2"/>
+ </radiogroup>
+ <toolbar id="toolbar" toolbarname="My toolbar">
+ <toolbarbutton id="button1" label="hello"/>
+ </toolbar>
+ <toolbar id="toolbar2" toolbarname="2nd" aria-label="My second toolbar">
+ <toolbarbutton id="button2" label="hello"/>
+ </toolbar>
+
+ <toolbar id="tb_customizable" customizable="true"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/tree/test_gencontent.html b/accessible/tests/mochitest/tree/test_gencontent.html
new file mode 100644
index 000000000..0932f5c29
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_gencontent.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Generated content tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ .gentext:before {
+ content: "START"
+ }
+ .gentext:after {
+ content: "END"
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // :before and :after pseudo styles
+ var accTree = {
+ role: ROLE_SECTION,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ name: "START"
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "MIDDLE"
+ },
+ {
+ role: ROLE_STATICTEXT,
+ name: "END"
+ }
+ ]
+ };
+
+ testAccessibleTree("gentext", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Clean up our tree walker"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=530081">
+ Mozilla Bug 530081
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div class="gentext" id="gentext">MIDDLE</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_groupbox.xul b/accessible/tests/mochitest/tree/test_groupbox.xul
new file mode 100644
index 000000000..0b68e6018
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_groupbox.xul
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL groupbox hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ var accTree =
+ { GROUPING: [
+ { LABEL: [
+ { TEXT_LEAF: [ ] }
+ ] },
+ { CHECKBUTTON: [ ] }
+ ] };
+ testAccessibleTree("groupbox", accTree);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"
+ title="Fix O(n^2) access to all the children of a container">
+ Mozilla Bug 342045
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <groupbox id="groupbox">
+ <caption label="Some caption" />
+ <checkbox label="some checkbox label" />
+ </groupbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/tree/test_iframe.html b/accessible/tests/mochitest/tree/test_iframe.html
new file mode 100644
index 000000000..dae398fd8
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_iframe.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Outer document accessible tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accTree = {
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ role: ROLE_DOCUMENT
+ }
+ ]
+ };
+
+ testAccessibleTree("iframe", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix O(n^2) access to all the children of a container"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
+ Mozilla Bug 342045
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe id="iframe" src="about:mozilla">
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_img.html b/accessible/tests/mochitest/tree/test_img.html
new file mode 100644
index 000000000..9ac52da39
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_img.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML img tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+ function doPreTest()
+ {
+ waitForImageMap("imgmap", doTest);
+ }
+
+ function doTest()
+ {
+ // image map
+ var accTree = {
+ role: ROLE_IMAGE_MAP,
+ children: [
+ {
+ role: ROLE_LINK,
+ children: []
+ },
+ {
+ role: ROLE_LINK,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("imgmap", accTree);
+
+ // img
+ accTree = {
+ role: ROLE_GRAPHIC,
+ children: []
+ };
+
+ testAccessibleTree("img", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix O(n^2) access to all the children of a container"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
+ Mozilla Bug 342045
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <map name="atoz_map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,13,14" alt="a" shape="rect">
+ </map>
+
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="../letters.gif">
+
+ <img id="img" src="../moz.png">
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_invalid_img.xhtml b/accessible/tests/mochitest/tree/test_invalid_img.xhtml
new file mode 100644
index 000000000..2079c86ee
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_invalid_img.xhtml
@@ -0,0 +1,50 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>invalid html img</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script>
+ <![CDATA[
+ function doTest()
+ {
+ document.getElementsByTagName("img")[0].firstChild.data = "2";
+
+ var accTree = {
+ role: ROLE_TEXT,
+ children: [ { role: ROLE_TEXT_LEAF } ]
+ };
+ testAccessibleTree("the_img", accTree);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="use HyperTextAccessible for invalid img"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=852129">
+ Mozilla Bug 852129
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <img id="the_img">1</img>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_invalidationlist.html b/accessible/tests/mochitest/tree/test_invalidationlist.html
new file mode 100644
index 000000000..c42891a5d
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_invalidationlist.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test document hierarchy</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var tree =
+ { SECTION: [
+ { SECTION: [ // div
+ { LABEL: [ ] } // link
+ ] },
+ { SECTION: [ // div table-cell referred by label
+ { TEXT_LEAF: [ ] }, // 'Z'
+ { TEXT_LEAF: [ ] } // ' '
+ ] }
+ ] };
+ testAccessibleTree("container", tree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673757"
+ title="Do not process invalidation list while tree is created">
+ Mozilla Bug 673757
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container">
+ <div><label for="x"></label></div>
+ <div style="display: table-cell;" id="x">Z<span style='white-space:pre'> </span><span></span></div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_list.html b/accessible/tests/mochitest/tree/test_list.html
new file mode 100644
index 000000000..bf5e8768d
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_list.html
@@ -0,0 +1,247 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML ul/li element tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function listItemTree(aBulletText, aName, aSubtree)
+ {
+ var obj = {
+ role: ROLE_LISTITEM,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ name: aBulletText
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ name: aName
+ }
+ ]
+ };
+
+ if (aSubtree)
+ obj.children.push(aSubtree);
+
+ return obj;
+ }
+
+ function doTest()
+ {
+ // list1
+ var discAccTree = {
+ role: ROLE_LIST,
+ children: [
+ new listItemTree(kDiscBulletText, "Oranges"),
+ new listItemTree(kDiscBulletText, "Apples"),
+ new listItemTree(kDiscBulletText, "Bananas")
+ ]
+ };
+
+ testAccessibleTree("list1", discAccTree);
+
+ // list2
+ var circleAccTree = {
+ role: ROLE_LIST,
+ children: [
+ new listItemTree(kCircleBulletText, "Oranges"),
+ new listItemTree(kCircleBulletText, "Apples"),
+ new listItemTree(kCircleBulletText, "Bananas")
+ ]
+ };
+
+ testAccessibleTree("list2", circleAccTree);
+
+ // list3
+ var squareAccTree = {
+ role: ROLE_LIST,
+ children: [
+ new listItemTree(kSquareBulletText, "Oranges"),
+ new listItemTree(kSquareBulletText, "Apples"),
+ new listItemTree(kSquareBulletText, "Bananas")
+ ]
+ };
+
+ testAccessibleTree("list3", squareAccTree);
+
+ // list4
+ var nestedAccTree = {
+ role: ROLE_LIST,
+ children: [
+ new listItemTree("1. ", "Oranges"),
+ new listItemTree("2. ", "Apples"),
+ new listItemTree("3. ", "Bananas", circleAccTree)
+ ]
+ };
+
+ testAccessibleTree("list4", nestedAccTree);
+
+ // dl list
+ var tree =
+ { DEFINITION_LIST: [ // dl
+ { TERM: [ // dt
+ { TEXT_LEAF: [] },
+ ] },
+ { DEFINITION: [ // dd
+ { TEXT_LEAF: [] }
+ ] },
+ { TERM: [ // dt
+ { TEXT_LEAF: [] }
+ ] },
+ { DEFINITION: [ // dd
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+
+ testAccessibleTree("list5", tree);
+
+ // dl list inside ordered list
+ tree =
+ { LIST: [ // ol
+ { LISTITEM: [ // li
+ { STATICTEXT: [ ] },
+ { DEFINITION_LIST: [ // dl
+ { TERM: [ // dt
+ { TEXT_LEAF: [] }
+ ] },
+ { DEFINITION: [ // dd
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ ] };
+
+ testAccessibleTree("list6", tree);
+
+ // li having no display:list-item style
+ var tree =
+ { LIST: [ // ul
+ { LISTITEM: [ // li
+ { TEXT_LEAF: [] },
+ ] },
+ { TEXT_LEAF: [] },
+ { LISTITEM: [ // li
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree("list7", tree);
+
+ var tree =
+ { LIST: [ // ul
+ { LISTITEM: [ // li
+ { TEXT_LEAF: [] },
+ ] },
+ { LISTITEM: [ // li
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree("list8", tree);
+
+ // span having display:list-item style
+ testAccessibleTree("list9", discAccTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Fix O(n^2) access to all the children of a container"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045">
+ Mozilla Bug 342045
+ </a>
+ <a target="_blank"
+ title="Wrong accessible is created for HTML:li having block display style"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=507555">
+ Mozilla Bug 507555
+ </a>
+ <a target="_blank"
+ title="Bullets of nested not ordered lists have one and the same character."
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=604587">
+ Mozilla Bug 604587
+ </a>
+ <a target="_blank"
+ title="Fix list bullets for DL list (crash [@ nsBulletFrame::GetListItemText])"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=629114">
+ Mozilla Bug 629114
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ul id="list1">
+ <li id="l1_li1">Oranges</li>
+ <li id="l1_li2">Apples</li>
+ <li id="l1_li3">Bananas</li>
+ </ul>
+
+ <ul id="list2" style="list-style-type: circle">
+ <li id="l2_li1">Oranges</li>
+ <li id="l2_li2">Apples</li>
+ <li id="l2_li3">Bananas</li>
+ </ul>
+
+ <ul id="list3" style="list-style-type: square">
+ <li id="l3_li1">Oranges</li>
+ <li id="l3_li2">Apples</li>
+ <li id="l3_li3">Bananas</li>
+ </ul>
+
+ <ol id="list4">
+ <li id="li4">Oranges</li>
+ <li id="li5">Apples</li>
+ <li id="li6">Bananas<ul>
+ <li id="n_li4">Oranges</li>
+ <li id="n_li5">Apples</li>
+ <li id="n_li6">Bananas</li>
+ </ul>
+ </li>
+ </ol>
+
+ <dl id="list5">
+ <dt>item1</dt><dd>description</dd>
+ <dt>item2</td><dd>description</dd>
+ </dl>
+
+ <ol id="list6">
+ <li>
+ <dl id="dl">
+ <dt>item1</dt><dd>description</dd>
+ </dl>
+ </li>
+ </ol>
+
+ <!-- display style different than list-item -->
+ <ul id="list7">
+ <li id="l7_li1" style="display:inline-block;">Oranges</li>
+ <li id="l7_li2" style="display:inline-block;">Apples</li>
+ </ul>
+
+ <ul id="list8">
+ <li id="l8_li1" style="display:inline; float:right;">Oranges</li>
+ <li id="l8_li2" style="display:inline; float:right;">Apples</li>
+ </ul>
+
+ <!-- list-item display style -->
+ <ul id="list9">
+ <span id="l9_li1" style="display:list-item">Oranges</span>
+ <span id="l9_li2" style="display:list-item">Apples</span>
+ <span id="l9_li3" style="display:list-item">Bananas</span>
+ </ul>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_map.html b/accessible/tests/mochitest/tree/test_map.html
new file mode 100644
index 000000000..6f0b0f2b6
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_map.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML map accessible tree tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // map used as imagemap, not accessible
+ var accTree =
+ { SECTION: [ ] };
+
+ testAccessibleTree("imagemapcontainer", accTree);
+
+ // map group. Imagemaps are inlines by default, so TEXT_CONTAINER.
+ accTree =
+ { TEXT_CONTAINER: [
+ { PARAGRAPH: [
+ { TEXT_LEAF: [ ] },
+ { LINK: [
+ { TEXT_LEAF: [ ] }
+ ] },
+ { TEXT_LEAF: [ ] },
+ { LINK: [
+ { TEXT_LEAF: [ ] }
+ ] },
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] };
+
+ testAccessibleTree("mapgroup", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Map used for grouping is not accessible under certain circumstances"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=627718">
+ Mozilla Bug 627718
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="imagemapcontainer">
+ <map name="atoz_map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a"
+ coords="0,0,13,14" alt="a" shape="rect">
+ </map>
+ </div>
+
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="../letters.gif">
+
+ <map id="mapgroup" title="Navigation Bar" name="mapgroup">
+ <p>
+ [<a href="#how">Bypass navigation bar</a>]
+ [<a href="home.html">Home</a>]
+ </p>
+ </map>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_media.html b/accessible/tests/mochitest/tree/test_media.html
new file mode 100644
index 000000000..b44a7293d
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_media.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=483573
+-->
+<head>
+ <title>HTML5 audio/video tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // test the accessible tree
+
+ var accTree = {
+ role: ROLE_GROUPING,
+ children: [
+ { // start/stop button
+ role: ROLE_PUSHBUTTON,
+ name: "Play",
+ children: []
+ },
+ { // buffer bar
+ role: ROLE_PROGRESSBAR,
+ children: []
+ },
+ { // progress bar
+ role: ROLE_PROGRESSBAR,
+ children: []
+ },
+ { // slider of progress bar
+ role: ROLE_SLIDER,
+ //name: "0:00 of 0:02 elapsed",
+ children: []
+ },
+ { // mute button
+ role: ROLE_PUSHBUTTON,
+ name: "Mute",
+ children: []
+ },
+ { // slider of volume bar
+ role: ROLE_SLIDER,
+ children: []
+ },
+ ]
+ };
+ testAccessibleTree("audio", accTree);
+
+ todo(false, "Enable name test for slider. Fail on Linux.");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Expose HTML5 video and audio elements' embedded controls through accessibility APIs"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <audio id="audio" src="../bug461281.ogg"
+ controls="true"></audio>
+
+ <div id="eventDump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_select.html b/accessible/tests/mochitest/tree/test_select.html
new file mode 100644
index 000000000..151985622
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_select.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML select control tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accTree = {
+ role: ROLE_LISTBOX,
+ children: [
+ {
+ role: ROLE_GROUPING,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ children: [ ]
+ },
+ {
+ role: ROLE_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ },
+ {
+ role: ROLE_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ }
+ ]
+ },
+ {
+ role: ROLE_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree("listbox", accTree);
+
+ accTree = {
+ role: ROLE_COMBOBOX,
+ children: [
+ {
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ {
+ role: ROLE_GROUPING,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ children: [ ]
+ },
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ },
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ },
+ ]
+ },
+ {
+ role: ROLE_COMBOBOX_OPTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree("combobox", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="remove all the code in #ifdef COMBO_BOX_WITH_THREE_CHILDREN"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=506616">
+ Mozilla Bug 506616
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="listbox" size="4">
+ <optgroup label="Colors">
+ <option>Red</option>
+ <option>Blue</option>
+ </optgroup>
+ <option>Animal</option>
+ </select>
+
+ <select id="combobox">
+ <optgroup label="Colors">
+ <option>Red</option>
+ <option>Blue</option>
+ </optgroup>
+ <option>Animal</option>
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_tabbox.xul b/accessible/tests/mochitest/tree/test_tabbox.xul
new file mode 100644
index 000000000..4d4101388
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_tabbox.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tabbox hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // tabbox
+
+ var accTree = {
+ role: ROLE_PAGETABLIST,
+ children: [
+ {
+ role: ROLE_PAGETAB,
+ children: []
+ },
+ {
+ role: ROLE_PAGETAB,
+ children: []
+ }
+ ]
+ };
+ testAccessibleTree("tabs", accTree);
+
+ accTree = {
+ role: ROLE_PANE,
+ children: [
+ {
+ role: ROLE_PROPERTYPAGE,
+ children: []
+ },
+ {
+ role: ROLE_PROPERTYPAGE,
+ children: []
+ }
+ ]
+ };
+ testAccessibleTree("tabpanels", accTree);
+
+ SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
+ title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
+ Mozilla Bug 540389
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+ title="No relationship between tabs and associated property page in new tabbrowser construct">
+ Mozilla Bug 552944
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tabbox>
+ <tabs id="tabs">
+ <tab label="tab1"/>
+ <tab label="tab2"/>
+ </tabs>
+ <tabpanels id="tabpanels">
+ <tabpanel/>
+ <tabpanel/>
+ </tabpanels>
+ </tabbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/tree/test_tabbrowser.xul b/accessible/tests/mochitest/tree/test_tabbrowser.xul
new file mode 100644
index 000000000..1e25fdb9c
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul
@@ -0,0 +1,255 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tabbrowser hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // invoker
+ function testTabHierarchy()
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0),
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.invoke = function testTabHierarchy_invoke()
+ {
+ var docURIs = ["about:", "about:mozilla"];
+ tabBrowser().loadTabs(docURIs, false, true);
+ }
+
+ this.finalCheck = function testTabHierarchy_finalCheck(aEvent)
+ {
+ ////////////////////
+ // Tab bar
+ ////////////////////
+ var tabsAccTree = {
+ // xul:tabs
+ role: ROLE_PAGETABLIST,
+ children: [
+ // Children depend on application (UI): see below.
+ ]
+ };
+
+ // SeaMonkey and Firefox tabbrowser UIs differ.
+ if (SEAMONKEY) {
+ SimpleTest.ok(true, "Testing SeaMonkey tabbrowser UI.");
+
+ tabsAccTree.children.splice(0, 0,
+ {
+ // xul:toolbarbutton ("Open a new tab")
+ role: ROLE_PUSHBUTTON,
+ children: []
+ },
+ {
+ // xul:tab ("about:")
+ role: ROLE_PAGETAB,
+ children: []
+ },
+ {
+ // tab ("about:mozilla")
+ role: ROLE_PAGETAB,
+ children: []
+ },
+ {
+ // xul:toolbarbutton ("List all tabs")
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ // xul:menupopup
+ role: ROLE_MENUPOPUP,
+ children: []
+ }
+ ]
+ },
+ {
+ // xul:toolbarbutton ("Close current tab")
+ role: ROLE_PUSHBUTTON,
+ children: []
+ }
+ );
+ } else {
+ SimpleTest.ok(true, "Testing Firefox tabbrowser UI.");
+ let newTabChildren = [];
+ if (SpecialPowers.getBoolPref("privacy.userContext.enabled")) {
+ newTabChildren = [
+ {
+ role: ROLE_MENUPOPUP,
+ children: []
+ }
+ ];
+ }
+
+ // NB: The (3) buttons are not visible, unless manually hovered,
+ // probably due to size reduction in this test.
+ tabsAccTree.children.splice(0, 0,
+ {
+ // xul:tab ("about:")
+ role: ROLE_PAGETAB,
+ children: [
+ {
+ // xul:toolbarbutton ("Close Tab")
+ role: ROLE_PUSHBUTTON,
+ children: []
+ }
+ ]
+ },
+ {
+ // tab ("about:mozilla")
+ role: ROLE_PAGETAB,
+ children: [
+ {
+ // xul:toolbarbutton ("Close Tab")
+ role: ROLE_PUSHBUTTON,
+ children: []
+ }
+ ]
+ },
+ {
+ // xul:toolbarbutton ("Open a new tab")
+ role: ROLE_PUSHBUTTON,
+ children: newTabChildren
+ }
+ // "List all tabs" dropdown
+ // XXX: This child(?) is not present in this test.
+ // I'm not sure why (though probably expected).
+ );
+ }
+
+ testAccessibleTree(tabBrowser().tabContainer, tabsAccTree);
+
+ ////////////////////
+ // Tab contents
+ ////////////////////
+ var tabboxAccTree = {
+ // xul:tabpanels
+ role: ROLE_PANE,
+ children: [
+ {
+ // xul:notificationbox
+ role: ROLE_PROPERTYPAGE,
+ children: [
+ {
+ // xul:browser
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ // #document ("about:")
+ role: ROLE_DOCUMENT
+ // children: [ ... ] // Ignore document content.
+ }
+ ]
+ }
+ ]
+ },
+ {
+ // notificationbox
+ role: ROLE_PROPERTYPAGE,
+ children: [
+ {
+ // browser
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ // #document ("about:mozilla")
+ role: ROLE_DOCUMENT
+ // children: [ ... ] // Ignore document content.
+ }
+ ]
+ }
+ ]
+ },
+ {
+ // notificationbox
+ role: ROLE_PROPERTYPAGE,
+ children: [
+ {
+ // browser
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ // #document ("about:newtab" preloaded)
+ role: ROLE_DOCUMENT
+ // children: [ ... ] // Ignore document content.
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree(tabBrowser().mTabBox.tabpanels, tabboxAccTree);
+ }
+
+ this.getID = function testTabHierarchy_getID()
+ {
+ return "hierarchy of tabs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose,stack");
+
+ var gQueue = null;
+ function doTest()
+ {
+ // Load documents into tabs and wait for docLoadComplete events caused by these
+ // documents load before we start the test.
+ gQueue = new eventQueue();
+
+ gQueue.push(new testTabHierarchy());
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTest);
+ ]]>
+ </script>
+
+ <vbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389"
+ title=" WARNING: Bad accessible tree!: [tabbrowser tab] ">
+ Mozilla Bug 540389
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944"
+ title="No relationship between tabs and associated property page in new tabbrowser construct">
+ Mozilla Bug 552944
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="eventdump"></vbox>
+ </vbox>
+
+</window>
diff --git a/accessible/tests/mochitest/tree/test_table.html b/accessible/tests/mochitest/tree/test_table.html
new file mode 100644
index 000000000..bfbd6ae5a
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_table.html
@@ -0,0 +1,282 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML table tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // tables having captions
+
+ // Two captions, first is used, second is ignored.
+ var accTree =
+ { TABLE: [
+ { CAPTION: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "caption"
+ }
+ ] },
+ { ROW: [
+ { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] },
+ { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }
+ ] },
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] },
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] },
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] }
+ ] };
+
+ testAccessibleTree("table", accTree);
+
+ // One caption, empty text, caption is ignored.
+ accTree =
+ { TABLE: [
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] }
+ ] };
+
+ testAccessibleTree("table_caption_empty", accTree);
+
+ // Two captions, first has empty text, both are ignored.
+ accTree =
+ { TABLE: [
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] }
+ ] };
+
+ testAccessibleTree("table_caption_firstempty", accTree);
+
+ // One caption, placed in the end of table. In use.
+ accTree =
+ { TABLE: [
+ { CAPTION: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "caption"
+ }
+ ] },
+ { ROW: [
+ { CELL: [ { TEXT_LEAF: [ ] } ] },
+ { CELL: [ { TEXT_LEAF: [ ] } ] }
+ ] }
+ ] };
+
+ testAccessibleTree("table_caption_intheend", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // table2 (consist of one column)
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ {
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_COLUMNHEADER
+ }
+ ]
+ },
+ {
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_CELL
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("table2", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // table3 (consist of one row)
+
+ accTree = {
+ role: ROLE_TABLE,
+ children: [
+ {
+ role: ROLE_ROW,
+ children: [
+ {
+ role: ROLE_ROWHEADER
+ },
+ {
+ role: ROLE_CELL
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("table3", accTree);
+
+ /////////////////////////////////////////////////////////////////////////
+ // table4 (display: table-row)
+ accTree =
+ { TABLE: [
+ { ROW: [
+ { CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] } ]
+ };
+ testAccessibleTree("table4", accTree);
+
+ /////////////////////////////////////////////////////////////////////////
+ // table5 (intermediate accessible for tbody)
+ accTree =
+ { TABLE: [
+ { TEXT_CONTAINER: [
+ { ROW: [
+ { CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] } ]
+ };
+ testAccessibleTree("table5", accTree);
+
+ /////////////////////////////////////////////////////////////////////////
+ // log table
+ accTree =
+ { TABLE: [
+ { ROW: [
+ { CELL: [
+ { TEXT_LEAF: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree("logtable", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="When a table has only one column per row and that column happens to be a column header its role is exposed wrong"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=529621">
+ Mozilla Bug 529621
+ </a>
+ <a target="_blank"
+ title="when div has display style table-row"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=727722">
+ Mozilla Bug 727722
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table id="table">
+ <thead>
+ <tr>
+ <th>col1</th><th>col2</th>
+ </tr>
+ </thead>
+ <caption>caption</caption>
+ <tbody>
+ <tr>
+ <td>cell1</td><td>cell2</td>
+ </tr>
+ </tbody>
+ <tr>
+ <td>cell3</td><td>cell4</td>
+ </tr>
+ <caption>caption2</caption>
+ <tfoot>
+ <tr>
+ <td>cell5</td><td>cell6</td>
+ </tr>
+ </tfoot>
+ </table>
+
+ <table id="table_caption_empty">
+ <caption></caption>
+ <tr>
+ <td>cell1</td><td>cell2</td>
+ </tr>
+ </table>
+
+ <table id="table_caption_firstempty">
+ <caption></caption>
+ <tr>
+ <td>cell1</td><td>cell2</td>
+ </tr>
+ <caption>caption</caption>
+ </table>
+
+ <table id="table_caption_intheend">
+ <tr>
+ <td>cell1</td><td>cell2</td>
+ </tr>
+ <caption>caption</caption>
+ </table>
+
+ <table id="table2">
+ <thead>
+ <tr>
+ <th>colheader</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>bla</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="table3">
+ <tr>
+ <th>rowheader</th>
+ <td>cell</td>
+ </tr>
+ </table>
+
+ <table id="table4">
+ <div style="display: table-row">
+ <td>cell1</td>
+ </div>
+ </table>
+
+ <table id="table5">
+ <tbody style="display:block;overflow:auto;">
+ <tr>
+ <td>bla</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <table id="logtable" role="log"><tr><td>blah</td></tr></table>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_tree.xul b/accessible/tests/mochitest/tree/test_tree.xul
new file mode 100644
index 000000000..0fd222c42
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_tree.xul
@@ -0,0 +1,182 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Accessible tree testers
+
+ function getTreeItemAccTree(aTableRole, acolumnCount)
+ {
+ var treeItemRole;
+ switch (aTableRole) {
+ case ROLE_LIST:
+ treeItemRole = ROLE_LISTITEM;
+ break;
+ case ROLE_OUTLINE:
+ treeItemRole = ROLE_OUTLINEITEM;
+ break;
+ case ROLE_TABLE: case ROLE_TREE_TABLE:
+ treeItemRole = ROLE_ROW;
+ break;
+ }
+
+ var accTree = {
+ role: treeItemRole,
+ children: []
+ };
+
+ if (aTableRole == ROLE_TABLE || aTableRole == ROLE_TREE_TABLE) {
+ for (var idx = 0; idx < acolumnCount; idx++) {
+ var cellAccTree = {
+ role: ROLE_GRID_CELL,
+ children: []
+ };
+ accTree.children.push(cellAccTree);
+ }
+ }
+
+ return accTree;
+ }
+
+ function testAccessibleTreeFor(aTree, aRole)
+ {
+ var accTreeForColumns = {
+ role: ROLE_LIST,
+ children: []
+ };
+
+ var accTreeForTree = {
+ role: aRole,
+ children: [
+ accTreeForColumns
+ ]
+ };
+
+ var treeBoxObject = aTree.treeBoxObject;
+ var view = aTree.view;
+ var columnCount = treeBoxObject.columns.count;
+
+ for (var idx = 0; idx < columnCount; idx++)
+ accTreeForColumns.children.push({ COLUMNHEADER: [ ] });
+ if (!aTree.hasAttribute("hidecolumnpicker"))
+ accTreeForColumns.children.push({ PUSHBUTTON: [ { MENUPOPUP: [] } ] });
+
+ for (var idx = 0; idx < view.rowCount; idx++)
+ accTreeForTree.children.push(getTreeItemAccTree(aRole, columnCount));
+
+ testAccessibleTree(aTree, accTreeForTree);
+ }
+
+ /**
+ * Event queue invoker object to test accessible tree for XUL tree element.
+ */
+ function treeChecker(aID, aView, aRole)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function invoke()
+ {
+ this.DOMNode.view = aView;
+ }
+ this.check = function check(aEvent)
+ {
+ testAccessibleTreeFor(this.DOMNode, aRole);
+ }
+ this.getID = function getID()
+ {
+ return "Tree testing of " + aID;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+ var gQueue = null;
+
+ function doTest()
+ {
+ var gQueue = new eventQueue(EVENT_REORDER);
+
+ gQueue.push(new treeChecker("list", new nsTableTreeView(3), ROLE_LIST));
+ gQueue.push(new treeChecker("tree", new nsTreeTreeView(), ROLE_OUTLINE));
+ gQueue.push(new treeChecker("table", new nsTableTreeView(3), ROLE_TABLE));
+ gQueue.push(new treeChecker("treetable", new nsTreeTreeView(), ROLE_TREE_TABLE));
+
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Mozilla Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="list" flex="1" hidecolumnpicker="true">
+ <treecols>
+ <treecol id="col" flex="1" hideheader="true"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="table" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treetable" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <vbox id="debug"/>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/tree/test_txtcntr.html b/accessible/tests/mochitest/tree/test_txtcntr.html
new file mode 100644
index 000000000..80a225ce7
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_txtcntr.html
@@ -0,0 +1,234 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>HTML text containers tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ var accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("c1", accTree);
+ testAccessibleTree("c2", accTree);
+
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "Hello1"
+ },
+ {
+ role: ROLE_WHITESPACE
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "Hello2"
+ },
+ {
+ role: ROLE_SEPARATOR
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "Hello3 "
+ },
+ {
+ role: ROLE_PARAGRAPH,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "Hello4 "
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("c3", accTree);
+
+ // contentEditable div
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "helllo "
+ },
+ {
+ role: ROLE_PARAGRAPH,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "blabla"
+ }
+ ]
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "hello "
+ }
+ ]
+ };
+
+ testAccessibleTree("c4", accTree);
+
+ // blockquote
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // block quote
+ role: ROLE_SECTION,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ name: "Hello",
+ children: []
+ }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree("c5", accTree);
+
+ // abbreviation tag
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "This ",
+ children: []
+ },
+ { // abbr tag
+ role: ROLE_TEXT,
+ name: "accessibility",
+ children: [
+ { // text leaf with actual text
+ role: ROLE_TEXT_LEAF,
+ name: "a11y",
+ children: []
+ }
+ ]
+ },
+ { // text leaf
+ role: ROLE_TEXT_LEAF,
+ name: " test",
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("c6", accTree);
+
+ // acronym tag
+ accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // text leaf
+ role: ROLE_TEXT_LEAF,
+ name: "This ",
+ children: []
+ },
+ { // acronym tag
+ role: ROLE_TEXT,
+ name: "personal computer",
+ children: [
+ { // text leaf with actual text
+ role: ROLE_TEXT_LEAF,
+ name: "PC",
+ children: []
+ }
+ ]
+ },
+ { // text leaf
+ role: ROLE_TEXT_LEAF,
+ name: " is broken",
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("c7", accTree);
+
+ // only whitespace between images should be exposed
+ accTree = {
+ SECTION: [
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] }
+ ]
+ };
+ testAccessibleTree("c8", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="overflowed content doesn't expose child text accessibles"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">
+ Mozilla Bug 489306</a>
+ <a target="_blank"
+ title="Create child accessibles for text controls from native anonymous content"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824">
+ Mozilla Bug 542824</a>
+ <a target="_blank"
+ title="Update accessible tree on content insertion after layout"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015">
+ Mozilla Bug 498015</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1" style="width: 100px; height: 100px; overflow: auto;">
+ 1hellohello 2hellohello 3hellohello 4hellohello 5hellohello 6hellohello 7hellohello
+ </div>
+ <div id="c2">
+ 1hellohello 2hellohello 3hellohello 4hellohello 5hellohello 6hellohello 7hellohello
+ </div>
+ <div id="c3">
+ Hello1<br>
+ Hello2<hr>
+ Hello3
+ <p>
+ Hello4
+ </p>
+ </div>
+ <div id="c4" contentEditable="true">
+ helllo <p>blabla</p> hello
+ </div>
+ <div id="c5"><blockquote>Hello</blockquote></div>
+ <div id="c6">This <abbr title="accessibility">a11y</abbr> test</div>
+ <div id="c7">This <acronym title="personal computer">PC</acronym> is broken</div>
+
+ <!-- only whitespace between images should be exposed -->
+ <div id="c8"> <img src="../moz.png"> <img src="../moz.png"> </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_txtctrl.html b/accessible/tests/mochitest/tree/test_txtctrl.html
new file mode 100644
index 000000000..79613cd41
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_txtctrl.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>HTML text controls tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // editable div
+ var accTree = {
+ role: ROLE_SECTION,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc1", accTree);
+
+ // input@type="text", value
+ accTree = {
+ role: ROLE_ENTRY,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc2", accTree);
+
+ // input@type="text", no value
+ accTree =
+ { ENTRY: [ ] };
+
+ testAccessibleTree("txc3", accTree);
+
+ // textarea
+ accTree = {
+ role: ROLE_ENTRY,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF // hello1\nhello2 text
+ }
+ ]
+ };
+
+ testAccessibleTree("txc4", accTree);
+
+ // input@type="password"
+ accTree = {
+ role: ROLE_PASSWORD_TEXT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc5", accTree);
+
+ // input@type="tel", value
+ accTree = {
+ role: ROLE_ENTRY,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc6", accTree);
+
+ // input@type="email", value
+ accTree = {
+ role: ROLE_ENTRY,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc7", accTree);
+
+ // input@type="search", value
+ accTree = {
+ role: ROLE_ENTRY,
+ children: [
+ { // text child
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("txc8", accTree);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="overflowed content doesn't expose child text accessibles"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306">
+ Mozilla Bug 489306
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824"
+ title="Create child accessibles for text controls from native anonymous content">
+ Mozilla Bug 542824
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"
+ title="Make sure accessible tree is correct when rendered text is changed">
+ Mozilla Bug 625652
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="txc1" contentEditable="true">
+ 1hellohello
+ </div>
+ <input id="txc2" value="hello">
+ <input id="txc3">
+ <textarea id="txc4">
+ hello1
+ hello2
+ </textarea>
+ <input id="txc5" type="password" value="hello">
+ <input id="txc6" type="tel" value="4167771234">
+
+ Email Address:
+ <input id="txc7" type="email" list="contacts" value="xyzzy">
+ <datalist id="contacts">
+ <option>xyzzy@plughs.com</option>
+ <option>nobody@mozilla.org</option>
+ </datalist>
+
+ </br>Search for:
+ <input id="txc8" type="search" list="searchhisty" value="Gamma">
+ <datalist id="searchhisty">
+ <option>Gamma Rays</option>
+ <option>Gamma Ray Bursts</option>
+ </datalist>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/tree/test_txtctrl.xul b/accessible/tests/mochitest/tree/test_txtctrl.xul
new file mode 100644
index 000000000..cb43b498f
--- /dev/null
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xul
@@ -0,0 +1,219 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL textbox and textarea hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose"); // debug stuff
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function doTest()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // textboxes
+
+ var accTree =
+ { SECTION: [
+ { ENTRY: [ { TEXT_LEAF: [] } ] },
+ { MENUPOPUP: [] }
+ ] };
+
+ // default textbox
+ testAccessibleTree("txc", accTree);
+
+ // multiline
+ testAccessibleTree("txc_multiline", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // search textbox
+ accTree =
+ { SECTION: [
+ { ENTRY: [ { TEXT_LEAF: [] } ] },
+ { MENUPOPUP: [] }
+ ] };
+ testAccessibleTree("txc_search", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // search textbox with search button
+
+ if (MAC) {
+ accTree =
+ { SECTION: [
+ { ENTRY: [ { TEXT_LEAF: [] } ] },
+ { MENUPOPUP: [] }
+ ] };
+ } else {
+ accTree =
+ { SECTION: [
+ { ENTRY: [ { TEXT_LEAF: [] } ] },
+ { PUSHBUTTON: [] },
+ { MENUPOPUP: [] }
+ ] };
+ }
+
+ testAccessibleTree("txc_search_searchbutton", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // number textbox
+
+ accTree =
+ { SECTION: [
+ { ENTRY: [ { TEXT_LEAF: [] } ] },
+ { MENUPOPUP: [] },
+ { PUSHBUTTON: [] },
+ { PUSHBUTTON: [] }
+ ] };
+
+ testAccessibleTree("txc_number", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // password textbox
+
+ accTree =
+ { SECTION: [
+ { PASSWORD_TEXT: [ { TEXT_LEAF: [] } ] },
+ { MENUPOPUP: [] }
+ ] };
+
+ testAccessibleTree("txc_password", accTree);
+
+ //////////////////////////////////////////////////////////////////////////
+ // autocomplete textbox
+
+ accTree = {
+ // textbox
+ role: ROLE_AUTOCOMPLETE,
+ children: [
+ {
+ // html:input
+ role: ROLE_ENTRY,
+ children: [
+ {
+ // #text
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ },
+ {
+ // xul:menupopup
+ role: ROLE_COMBOBOX_LIST,
+ children: []
+ }
+ ]
+ };
+
+ function test_AutocompleteControl() {
+ testAccessibleTree("txc_autocomplete", accTree);
+ SimpleTest.finish();
+ }
+
+ // XPFE and Toolkit autocomplete widgets differ.
+ var txc = document.getElementById("txc_autocomplete");
+ if ("clearResults" in txc) {
+ SimpleTest.ok(true, "Testing (Old) XPFE autocomplete widget.");
+
+ // Popup is always created. (See code below.)
+
+ accTree.children.push(
+ {
+ // xul:panel
+ role: ROLE_COMBOBOX_LIST,
+ children: [
+ {
+ // xul:tree
+ role: ROLE_TABLE,
+ children: [
+ {
+ // xul:treecols
+ role: ROLE_LIST,
+ children: [
+ {
+ // xul:treecol
+ role: ROLE_COLUMNHEADER,
+ children: []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ );
+ test_AutocompleteControl();
+
+ } else {
+ SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget.");
+
+ // Dumb access to trigger popup lazy creation.
+ dump("Trigget popup lazy creation");
+ waitForEvent(EVENT_REORDER, txc, test_AutocompleteControl);
+ txc.popup;
+
+ accTree.children.push(
+ {
+ role: ROLE_LIST,
+ children: [
+ {
+ role: ROLE_LIST,
+ children: [
+ {
+ role: ROLE_COLUMNHEADER,
+ children: []
+ }
+ ]
+ }
+ ]
+ }
+ );
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824"
+ title="Create child accessibles for text controls from native anonymous content">
+ Mozilla Bug 542824
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="txc" value="hello"/>
+ <textbox id="txc_search" type="search" value="hello"/>
+ <textbox id="txc_search_searchbutton" searchbutton="true" type="search" value="hello"/>
+ <textbox id="txc_number" type="number" value="44"/>
+ <textbox id="txc_password" type="password" value="hello"/>
+ <textbox id="txc_multiline" multiline="true" value="hello"/>
+ <textbox id="txc_autocomplete" type="autocomplete" value="hello"/>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/tree/wnd.xul b/accessible/tests/mochitest/tree/wnd.xul
new file mode 100644
index 000000000..3b87cb5e0
--- /dev/null
+++ b/accessible/tests/mochitest/tree/wnd.xul
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Empty Window">
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini
new file mode 100644
index 000000000..d725ebff3
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -0,0 +1,41 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+ !/accessible/tests/mochitest/moz.png
+
+[test_ariadialog.html]
+[test_ariaowns.html]
+[test_bug852150.xhtml]
+[test_bug883708.xhtml]
+[test_bug884251.xhtml]
+[test_bug895082.html]
+[test_bug1040735.html]
+[test_bug1100602.html]
+[test_bug1175913.html]
+[test_bug1189277.html]
+[test_bug1276857.html]
+[test_canvas.html]
+[test_colorpicker.xul]
+[test_contextmenu.xul]
+[test_cssoverflow.html]
+[test_deck.xul]
+[test_doc.html]
+[test_gencontent.html]
+[test_general.html]
+[test_hidden.html]
+[test_imagemap.html]
+skip-if = buildapp == "mulet"
+[test_list.html]
+[test_list_editabledoc.html]
+[test_listbox.xul]
+[test_menu.xul]
+[test_menubutton.xul]
+[test_optgroup.html]
+[test_recreation.html]
+[test_select.html]
+[test_shutdown.xul]
+[test_table.html]
+[test_textleaf.html]
+[test_visibility.html]
+[test_whitespace.html]
diff --git a/accessible/tests/mochitest/treeupdate/test_ariadialog.html b/accessible/tests/mochitest/treeupdate/test_ariadialog.html
new file mode 100644
index 000000000..559431626
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_ariadialog.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Table creation in ARIA dialog test</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function showARIADialog(aID)
+ {
+ this.node = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.node)
+ ];
+
+ this.invoke = function showARIADialog_invoke()
+ {
+ getNode("dialog").style.display = "block";
+ getNode("table").style.visibility = "visible";
+ getNode("a").textContent = "link";
+ getNode("input").value = "hello";
+ getNode("input").focus();
+ }
+
+ this.finalCheck = function showARIADialog_finalCheck()
+ {
+ var tree = {
+ role: ROLE_DIALOG,
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ children: [ { role: ROLE_TEXT_LEAF } ]
+ },
+ {
+ role: ROLE_ENTRY
+ }
+ ]
+ };
+ testAccessibleTree(aID, tree);
+ }
+
+ this.getID = function showARIADialog_getID()
+ {
+ return "show ARIA dialog";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ //enableLogging("tree");
+ gQueue = new eventQueue();
+
+ // make the accessible an inaccessible
+ gQueue.push(new showARIADialog("dialog"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="dialog" role="dialog" style="display: none;">
+ <table id="table" role="presentation"
+ style="display: block; position: fixed; top: 88px; left: 312.5px; z-index: 10010; visibility: hidden;">
+ <tbody>
+ <tr>
+ <td role="presentation">
+ <div role="presentation">
+ <a id="a" role="button">text</a>
+ </div>
+ <input id="input">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_ariaowns.html b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
new file mode 100644
index 000000000..44fc22a2d
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -0,0 +1,693 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>@aria-owns attribute testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+ ////////////////////////////////////////////////////////////////////////////
+
+ function changeARIAOwns()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_button")),
+ // no hide for t1_subdiv because it is contained by hidden t1_checkbox
+ new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_button")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
+ new invokerChecker(EVENT_REORDER, getNode("t1_container"))
+ ];
+
+ this.invoke = function setARIAOwns_invoke()
+ {
+ // children are swapped by ARIA owns
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [
+ { SECTION: [] }
+ ] },
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree("t1_container", tree);
+
+ getNode("t1_container").
+ setAttribute("aria-owns", "t1_button t1_subdiv");
+ }
+
+ this.finalCheck = function setARIAOwns_finalCheck()
+ {
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] }, // checkbox, native order
+ { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
+ { SECTION: [ ] } // subdiv from the subtree, ARIA owned
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function setARIAOwns_getID()
+ {
+ return "Change @aria-owns attribute";
+ }
+ }
+
+ function removeARIAOwns()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_button")),
+ new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
+ new orderChecker(),
+ new asyncInvokerChecker(EVENT_SHOW, getNode("t1_button")),
+ new asyncInvokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
+ new orderChecker(),
+ new invokerChecker(EVENT_REORDER, getNode("t1_container")),
+ new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox"))
+ ];
+
+ this.invoke = function removeARIAOwns_invoke()
+ {
+ getNode("t1_container").removeAttribute("aria-owns");
+ }
+
+ this.finalCheck = function removeARIAOwns_finalCheck()
+ {
+ // children follow the DOM order
+ var tree =
+ { SECTION: [
+ { PUSHBUTTON: [ ] },
+ { CHECKBUTTON: [
+ { SECTION: [] }
+ ] }
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function removeARIAOwns_getID()
+ {
+ return "Remove @aria-owns attribute";
+ }
+ }
+
+ function setARIAOwns()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_button")),
+ new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_button")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
+ new invokerChecker(EVENT_REORDER, getNode("t1_container"))
+ ];
+
+ this.invoke = function setARIAOwns_invoke()
+ {
+ getNode("t1_container").
+ setAttribute("aria-owns", "t1_button t1_subdiv");
+ }
+
+ this.finalCheck = function setARIAOwns_finalCheck()
+ {
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] }, // checkbox
+ { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
+ { SECTION: [ ] } // subdiv from the subtree, ARIA owned
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function setARIAOwns_getID()
+ {
+ return "Set @aria-owns attribute";
+ }
+ }
+
+ function addIdToARIAOwns()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_group")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_group")),
+ new invokerChecker(EVENT_REORDER, document)
+ ];
+
+ this.invoke = function addIdToARIAOwns_invoke()
+ {
+ getNode("t1_container").
+ setAttribute("aria-owns", "t1_button t1_subdiv t1_group");
+ }
+
+ this.finalCheck = function addIdToARIAOwns_finalCheck()
+ {
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] }, // t1_checkbox
+ { PUSHBUTTON: [ ] }, // button, t1_button
+ { SECTION: [ ] }, // subdiv from the subtree, t1_subdiv
+ { GROUPING: [ ] } // group from outside, t1_group
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function addIdToARIAOwns_getID()
+ {
+ return "Add id to @aria-owns attribute value";
+ }
+ }
+
+ function appendEl()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getNode, "t1_child3"),
+ new invokerChecker(EVENT_REORDER, getNode("t1_container"))
+ ];
+
+ this.invoke = function appendEl_invoke()
+ {
+ var div = document.createElement("div");
+ div.setAttribute("id", "t1_child3");
+ div.setAttribute("role", "radio")
+ getNode("t1_container").appendChild(div);
+ }
+
+ this.finalCheck = function appendEl_finalCheck()
+ {
+ // children are invalidated, they includes aria-owns swapped kids and
+ // newly inserted child.
+ var 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("t1_container", tree);
+ }
+
+ this.getID = function appendEl_getID()
+ {
+ return "Append child under @aria-owns element";
+ }
+ }
+
+ function removeEl()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode, "t1_checkbox"),
+ new invokerChecker(EVENT_SHOW, getNode, "t1_checkbox"),
+ new invokerChecker(EVENT_REORDER, getNode("t1_container"))
+ ];
+
+ this.invoke = function removeEl_invoke()
+ {
+ // remove a container of t1_subdiv
+ getNode("t1_span").parentNode.removeChild(getNode("t1_span"));
+ }
+
+ this.finalCheck = function removeEl_finalCheck()
+ {
+ // subdiv should go away
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] }, // explicit, t1_checkbox
+ { RADIOBUTTON: [ ] }, // explicit, t1_child3
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { GROUPING: [ ] } // ARIA owned, t1_group
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function removeEl_getID()
+ {
+ return "Remove a container of ARIA owned element";
+ }
+ }
+
+ function removeId()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_group")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_group")),
+ new invokerChecker(EVENT_REORDER, document)
+ ];
+
+ this.invoke = function removeId_invoke()
+ {
+ getNode("t1_group").removeAttribute("id");
+ }
+
+ this.finalCheck = function removeId_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] } // ARIA owned, t1_button
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function removeId_getID()
+ {
+ return "Remove ID from ARIA owned element";
+ }
+ }
+
+ function setId()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")),
+ new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")),
+ new invokerChecker(EVENT_REORDER, document)
+ ];
+
+ this.invoke = function setId_invoke()
+ {
+ getNode("t1_grouptmp").setAttribute("id", "t1_group");
+ }
+
+ this.finalCheck = function setId_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
+ ] };
+ testAccessibleTree("t1_container", tree);
+ }
+
+ this.getID = function setId_getID()
+ {
+ return "Set ID that is referred by ARIA owns";
+ }
+ }
+
+ /**
+ * Remove an accessible DOM element containing an element referred by
+ * ARIA owns.
+ */
+ function removeA11eteiner()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode("t2_container1"))
+ ];
+
+ this.invoke = function removeA11eteiner_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [ ] } // ARIA owned, 't2_owned'
+ ] };
+ testAccessibleTree("t2_container1", tree);
+
+ getNode("t2_container2").removeChild(getNode("t2_container3"));
+ }
+
+ this.finalCheck = function removeA11eteiner_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ ] };
+ testAccessibleTree("t2_container1", tree);
+ }
+
+ this.getID = function removeA11eteiner_getID()
+ {
+ return "Remove an accessible DOM element containing an element referred by ARIA owns";
+ }
+ }
+
+ /**
+ * Steal an element from other ARIA owns element. This use case guarantees
+ * that result of setAttribute/removeAttribute doesn't depend on their order.
+ */
+ function stealFromOtherARIAOwns()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
+ ];
+
+ this.invoke = function stealFromOtherARIAOwns_invoke()
+ {
+ getNode("t3_container2").setAttribute("aria-owns", "t3_child");
+ }
+
+ this.finalCheck = function stealFromOtherARIAOwns_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ ] };
+ testAccessibleTree("t3_container1", tree);
+
+ tree =
+ { SECTION: [
+ { CHECKBUTTON: [
+ ] }
+ ] };
+ testAccessibleTree("t3_container2", tree);
+ }
+
+ this.getID = function stealFromOtherARIAOwns_getID()
+ {
+ return "Steal an element from other ARIA owns element";
+ }
+ }
+
+ function appendElToRecacheChildren()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode("t3_container2"))
+ ];
+
+ this.invoke = function appendElToRecacheChildren_invoke()
+ {
+ var div = document.createElement("div");
+ div.setAttribute("role", "radio")
+ getNode("t3_container2").appendChild(div);
+ }
+
+ this.finalCheck = function appendElToRecacheChildren_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ ] };
+ testAccessibleTree("t3_container1", tree);
+
+ tree =
+ { SECTION: [
+ { RADIOBUTTON: [ ] },
+ { CHECKBUTTON: [ ] } // ARIA owned
+ ] };
+ testAccessibleTree("t3_container2", tree);
+ }
+
+ this.getID = function appendElToRecacheChildren_getID()
+ {
+ return "Append a child under @aria-owns element to trigger children recache";
+ }
+ }
+
+ function showHiddenElement()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode("t4_container1"))
+ ];
+
+ this.invoke = function showHiddenElement_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { RADIOBUTTON: [] }
+ ] };
+ testAccessibleTree("t4_container1", tree);
+
+ getNode("t4_child1").style.display = "block";
+ }
+
+ this.finalCheck = function showHiddenElement_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { CHECKBUTTON: [] },
+ { RADIOBUTTON: [] }
+ ] };
+ testAccessibleTree("t4_container1", tree);
+ }
+
+ this.getID = function showHiddenElement_getID()
+ {
+ return "Show hidden ARIA owns referred element";
+ }
+ }
+
+ function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList)
+ {
+ this.eventSeq = [];
+ for (var id of aIdList) {
+ this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id)));
+ }
+
+ for (var id of aIdList) {
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id)));
+ }
+ this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer)));
+
+ this.invoke = function rearrangeARIAOwns_invoke()
+ {
+ getNode(aContainer).setAttribute("aria-owns", aAttr);
+ }
+
+ this.finalCheck = function rearrangeARIAOwns_finalCheck()
+ {
+ var tree = { SECTION: [ ] };
+ for (var role of aRoleList) {
+ var ch = {};
+ ch[role] = [];
+ tree["SECTION"].push(ch);
+ }
+ testAccessibleTree(aContainer, tree);
+ }
+
+ this.getID = function rearrangeARIAOwns_getID()
+ {
+ return `Rearrange @aria-owns attribute to '${aAttr}'`;
+ }
+ }
+
+ function removeNotARIAOwnedEl(aContainer, aChild)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aContainer)
+ ];
+
+ this.invoke = function removeNotARIAOwnedEl_invoke()
+ {
+ var tree = {
+ SECTION: [
+ { TEXT_LEAF: [ ] },
+ { GROUPING: [ ] }
+ ]
+ };
+ testAccessibleTree(aContainer, tree);
+
+ getNode(aContainer).removeChild(getNode(aChild));
+ }
+
+ this.finalCheck = function removeNotARIAOwnedEl_finalCheck()
+ {
+ var tree = {
+ SECTION: [
+ { GROUPING: [ ] }
+ ]
+ };
+ testAccessibleTree(aContainer, tree);
+ }
+
+ this.getID = function removeNotARIAOwnedEl_getID()
+ {
+ return `remove not ARIA owned child`;
+ }
+ }
+
+ function setARIAOwnsOnElToRemove(aParent, aChild)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aParent))
+ ];
+
+ this.invoke = function setARIAOwnsOnElToRemove_invoke()
+ {
+ getNode(aChild).setAttribute("aria-owns", "no_id");
+ getNode(aParent).removeChild(getNode(aChild));
+ getNode(aParent).parentNode.removeChild(getNode(aParent));
+ }
+
+ this.getID = function setARIAOwnsOnElToRemove_getID()
+ {
+ return `set ARIA owns on an element, and then remove it, and then remove its parent`;
+ }
+ }
+
+ /**
+ * Set ARIA owns on inaccessible span element that contains
+ * accessible children. This will move children from the container for
+ * the span.
+ */
+ function test8()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, "t8_container")
+ ];
+
+ this.invoke = function test8_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { PUSHBUTTON: [] },
+ { ENTRY: [] },
+ { ENTRY: [] },
+ { ENTRY: [] }
+ ] };
+ testAccessibleTree("t8_container", tree);
+
+ getNode(t8_container).setAttribute("aria-owns", "t8_span t8_button");
+ }
+
+ this.finalCheck = function test8_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { TEXT: [
+ { ENTRY: [] },
+ { ENTRY: [] },
+ { ENTRY: [] }
+ ] },
+ { PUSHBUTTON: [] }
+ ] };
+ testAccessibleTree("t8_container", tree);
+ }
+
+ this.getID = function test8_getID()
+ {
+ return `Set ARIA owns on inaccessible span element that contains accessible children`;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+ ////////////////////////////////////////////////////////////////////////////
+
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,eventTree,verbose"); // debug stuff
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ // test1
+ gQueue.push(new changeARIAOwns());
+ gQueue.push(new removeARIAOwns());
+ gQueue.push(new setARIAOwns());
+ gQueue.push(new addIdToARIAOwns());
+ gQueue.push(new appendEl());
+ gQueue.push(new removeEl());
+ gQueue.push(new removeId());
+ gQueue.push(new setId());
+
+ // test2
+ gQueue.push(new removeA11eteiner());
+
+ // test3
+ gQueue.push(new stealFromOtherARIAOwns());
+ gQueue.push(new appendElToRecacheChildren());
+
+ // test4
+ gQueue.push(new showHiddenElement());
+
+ // test5
+ gQueue.push(new rearrangeARIAOwns(
+ "t5_container", "t5_checkbox t5_radio t5_button",
+ [ "t5_checkbox", "t5_radio", "t5_button" ],
+ [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ]));
+ gQueue.push(new rearrangeARIAOwns(
+ "t5_container", "t5_radio t5_button t5_checkbox",
+ [ "t5_radio", "t5_button" ],
+ [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ]));
+
+ gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span"));
+
+ gQueue.push(new setARIAOwnsOnElToRemove("t7_parent", "t7_child"));
+
+ gQueue.push(new test8());
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+ </script>
+</head>
+
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="t1_container" aria-owns="t1_checkbox t1_button">
+ <div role="button" id="t1_button"></div>
+ <div role="checkbox" id="t1_checkbox">
+ <span id="t1_span">
+ <div id="t1_subdiv"></div>
+ </span>
+ </div>
+ </div>
+ <div id="t1_group" role="group"></div>
+ <div id="t1_grouptmp" role="group"></div>
+
+ <div id="t2_container1" aria-owns="t2_owned"></div>
+ <div id="t2_container2">
+ <div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div>
+ </div>
+
+ <div id="t3_container1" aria-owns="t3_child"></div>
+ <div id="t3_child" role="checkbox"></div>
+ <div id="t3_container2"></div>
+
+ <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
+ <div id="t4_container2">
+ <div id="t4_child1" style="display:none" role="checkbox"></div>
+ <div id="t4_child2" role="radio"></div>
+ </div>
+
+ <div id="t5_container">
+ <div role="button" id="t5_button"></div>
+ <div role="checkbox" id="t5_checkbox"></div>
+ <div role="radio" id="t5_radio"></div>
+ </div>
+
+ <div id="t6_container" aria-owns="t6_fake">
+ <span id="t6_span">hey</span>
+ </div>
+ <div id="t6_fake" role="group"></div>
+
+ <div id="t7_container">
+ <div id="t7_parent">
+ <div id="t7_child"></div>
+ </div>
+ </div>
+
+ <div id="t8_container">
+ <input id="t8_button" type="button"><span id="t8_span"><input><input><input></span>
+ </div>
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1040735.html b/accessible/tests/mochitest/treeupdate/test_bug1040735.html
new file mode 100644
index 000000000..527f23197
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1040735.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Adopt DOM node from anonymous subtree</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="../common.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ document.body.appendChild(document.getElementById("mw_a"));
+ setTimeout(function() { ok(true, "no crash and assertions"); SimpleTest.finish(); } , 0);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1040735"
+ title="Bug 1040735 - DOM node reinsertion under anonymous content may trigger a11y child adoption">
+ Bug 1040735</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <marquee>
+ <div id="mw_a" style="visibility: hidden;">
+ <div style="visibility: visible;" id="mw_inside"></div>
+ </div>
+ </marquee>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1100602.html b/accessible/tests/mochitest/treeupdate/test_bug1100602.html
new file mode 100644
index 000000000..1637e5057
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1100602.html
@@ -0,0 +1,114 @@
+<html>
+
+<head>
+ <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Change list style type to none.
+ */
+ function hideBullet()
+ {
+ this.eventSeq = [];
+ this.liAcc = getAccessible("list_element");
+ this.bullet = this.liAcc.firstChild;
+
+ this.eventSeq.push(new invokerChecker(EVENT_HIDE, this.bullet));
+ this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc));
+
+ this.invoke = function hideBullet_invoke()
+ {
+ getNode("list").setAttribute("style", "list-style-type: none;");
+ }
+
+ this.finalCheck = function hideBullet_finalCheck()
+ {
+ is(this.liAcc.name, "list element",
+ "Check that first child of LI is not a bullet.");
+ }
+
+ this.getID = function hideBullet_getID()
+ {
+ return "Hide bullet by setting style to none";
+ }
+ }
+
+ /**
+ * Change list style type to circles.
+ */
+ function showBullet()
+ {
+ this.eventSeq = [];
+ this.liAcc = getAccessible("list_element");
+
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW,
+ function(aNode) { return aNode.firstChild; },
+ this.liAcc));
+ this.eventSeq.push(new invokerChecker(EVENT_REORDER, this.liAcc));
+
+ this.invoke = function showBullet_invoke()
+ {
+ getNode("list").setAttribute("style", "list-style-type: circle;");
+ }
+
+ this.finalCheck = function showBullet_finalCheck()
+ {
+ is(this.liAcc.name, "◦ list element",
+ "Check that first child of LI is a circle bullet.");
+ }
+
+ this.getID = function showBullet_getID()
+ {
+ return "Show bullet by setting style to circle";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+
+ var list = getNode("list");
+ list.setAttribute("style", "list-style-type: circle;");
+
+ gQueue = new eventQueue();
+ gQueue.push(new hideBullet());
+ gQueue.push(new showBullet());
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100602"
+ title="[e10s] crash in mozilla::a11y::ProxyAccessible::Shutdown()">
+ Mozilla Bug 1100602
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ol id="list">
+ <li id="list_element">list element</li>
+ </ol>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1175913.html b/accessible/tests/mochitest/treeupdate/test_bug1175913.html
new file mode 100644
index 000000000..5dab9b5a8
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1175913.html
@@ -0,0 +1,105 @@
+<html>
+
+<head>
+ <title>Test hide/show events on event listener changes</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ function dummyListener() {}
+
+ function testAddListener()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getNode("parent")),
+ ];
+
+ this.invoke = function testAddListener_invoke()
+ {
+ is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible.");
+ is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible.");
+ getNode("parent").addEventListener("click", dummyListener);
+ }
+
+ this.finalCheck = function testAddListener_finalCheck()
+ {
+ var tree = { TEXT: [] };
+ testAccessibleTree("parent", tree);
+ }
+
+ this.getID = function testAddListener_getID()
+ {
+ return "Test that show event is sent when click listener is added";
+ }
+ }
+
+ function testRemoveListener()
+ {
+ this.eventSeq = [
+ new unexpectedInvokerChecker(EVENT_HIDE, getNode("parent")),
+ ];
+
+ this.invoke = function testRemoveListener_invoke()
+ {
+ getNode("parent").removeEventListener("click", dummyListener);
+ }
+
+ this.finalCheck = function testRemoveListener_finalCheck()
+ {
+ ok(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC),
+ "Parent stays accessible after click event listener is removed");
+ ok(!getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC),
+ "Child stays inaccessible");
+ }
+
+ this.getID = function testRemoveListener_getID()
+ {
+ return "Test that hide event is sent when click listener is removed";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new testAddListener());
+ gQueue.push(new testRemoveListener());
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175913"
+ title="Crash in mozilla::a11y::DocAccessibleParent::RemoveAccessible(ProxyAccessible* aAccessible)">
+ Mozilla Bug 1175913
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <span id="parent">
+ <span id="child">
+ </span>
+ </span>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1189277.html b/accessible/tests/mochitest/treeupdate/test_bug1189277.html
new file mode 100644
index 000000000..9c995d49e
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1189277.html
@@ -0,0 +1,86 @@
+<html>
+
+<head>
+ <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../name.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function runTest()
+ {
+ this.containerNode = getNode("outerDiv");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("child")),
+ new invokerChecker(EVENT_HIDE, getNode("childDoc")),
+ new invokerChecker(EVENT_SHOW, "newChildDoc"),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function runTest_invoke()
+ {
+ this.containerNode.removeChild(getNode("child"));
+
+ var docContainer = getNode("docContainer");
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "http://example.com");
+ iframe.setAttribute("id", "newChildDoc");
+
+ docContainer.removeChild(getNode("childDoc"));
+ docContainer.appendChild(iframe);
+ }
+
+ this.getID = function runTest_getID()
+ {
+ return "check show events are not incorrectly coalesced";
+ }
+ }
+
+ //enableLogging("tree");
+ gA11yEventDumpToConsole = true;
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new runTest());
+ gQueue.invoke(); // SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1189277"
+ title="content process crash caused by missing show event">
+ Mozilla Bug 1189277
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="outerDiv">
+ <div id="child">foo</div>
+ <div id="docContainer">
+ <iframe id="childDoc" src="about:blank">
+ </iframe>
+ </div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857.html b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
new file mode 100644
index 000000000..5eceae9eb
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
@@ -0,0 +1,143 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>DOM mutations test</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function runTest()
+ {
+ // children change will recreate the table
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode('c1'))
+ ];
+
+ this.invoke = function runTest_invoke() {
+ var tree = {
+ SECTION: [ // c1
+ { TEXT_LEAF: [] }, // Some text
+ { TEXT_CONTAINER: [
+ { TEXT_LEAF: [] } // something with ..
+ ] },
+ { TEXT_LEAF: [] } // More text
+ ]
+ };
+ testAccessibleTree('c1', tree);
+
+ getNode('c1_t').querySelector('span').remove();
+ };
+
+ this.finalCheck = function runTest_finalCheck() {
+ var tree = {
+ SECTION: [ // c1
+ { TEXT_LEAF: [] }, // Some text
+ { TEXT_LEAF: [] } // More text
+ ]
+ };
+ testAccessibleTree('c1', tree);
+ };
+
+ this.getID = function runTest_getID()
+ {
+ return 'child DOM node is removed before the layout notifies the a11y about parent removal/show';
+ };
+ }
+
+ function runShadowTest()
+ {
+ // children change will recreate the table
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, 'c2')
+ ];
+
+ this.invoke = function runShadowTest_invoke() {
+ var tree = {
+ SECTION: [ // c2
+ { TEXT_LEAF: [] }, // Some text
+ { TEXT_CONTAINER: [
+ { TEXT_LEAF: [] } // something with ..
+ ] },
+ { TEXT_LEAF: [] } // More text
+ ]
+ };
+ testAccessibleTree('c2', tree);
+
+ gShadowRoot.firstElementChild.querySelector('span').remove();
+ };
+
+ this.finalCheck = function runShadowTest_finalCheck() {
+ var tree = {
+ SECTION: [ // c2
+ { TEXT_LEAF: [] }, // Some text
+ { TEXT_LEAF: [] } // More text
+ ]
+ };
+ testAccessibleTree('c2', tree);
+ };
+
+ this.getID = function runShadowTest_getID() {
+ return 'child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM';
+ };
+ }
+
+ //enableLogging("tree");
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new runTest());
+ gQueue.push(new runShadowTest());
+ gQueue.invoke(); // will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1">
+ <div id="c1_t" style="display: table" role="presentation">
+ Some text
+ <span style="display: table-cell">something with accessibles goes here</span>
+ More text
+ </div>
+ </div>
+
+ <template id="tmpl">
+ <div style="display: table" role="presentation">
+ Some text
+ <span style="display: table-cell">something with accessibles goes here</span>
+ More text
+ </div>
+ </template>
+
+ <div id="c2"><div id="c2_c" role="presentation"></div></div>
+
+ <script>
+ var gShadowRoot = document.getElementById('c2_c').createShadowRoot();
+ var tmpl = document.getElementById('tmpl');
+ gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+ </script>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml
new file mode 100644
index 000000000..094d148a0
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml
@@ -0,0 +1,59 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Canvas subdom mutation</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+
+ <script>
+ <![CDATA[
+ function doTest()
+ {
+ var the_displayNone = getNode("the_displaynone");
+ var the_table = getNode("the_table");
+ var the_row = getNode("the_row");
+ ok(isAccessible(the_table), "table should be accessible");
+ the_displayNone.appendChild(the_table);
+ ok(!isAccessible(the_table), "table in display none tree shouldn't be accessible");
+
+ setTimeout(function() {
+ document.body.removeChild(the_row);
+ // make sure no accessibles have stuck around.
+ ok(!isAccessible(the_row), "row shouldn't be accessible");
+ ok(!isAccessible(the_table), "table shouldn't be accessible");
+ ok(!isAccessible(the_displayNone), "display none things shouldn't be accessible");
+ SimpleTest.finish();
+ }, 0);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="test accessible removal when reframe root isn't accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=852150">
+ Mozilla Bug 852150
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="the_displaynone" style="display: none;"></div>
+ <table id="the_table"></table>
+ <tr id="the_row"></tr>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml
new file mode 100644
index 000000000..6265a1c7f
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml
@@ -0,0 +1,33 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<script>
+
+function boom()
+{
+ var newSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ c.insertBefore(newSpan, d);
+ a.style.visibility = "visible";
+ ok(true, "test didn't crash or assert");
+ SimpleTest.finish();
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+ <a target="_blank"
+ title="test reparenting accessible subtree when inaccessible element becomes accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=883708">
+ Mozilla Bug 883708
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+<div style="visibility: collapse;" id="a"><div style="float: right; visibility: visible;"><div id="c"><td id="d"></td></div></div></div></body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml
new file mode 100644
index 000000000..d1821188b
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml
@@ -0,0 +1,21 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+<script>
+
+function boom()
+{
+ document.getElementById("k").removeAttribute("href");
+ ok(true, "changing iframe contents doesn't cause assertions");
+ SimpleTest.finish();
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<iframe src="data:text/html,1"><link id="k" href="data:text/html,2" /></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_bug895082.html b/accessible/tests/mochitest/treeupdate/test_bug895082.html
new file mode 100644
index 000000000..aaefdc46c
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_bug895082.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Replace body test</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+function doTest()
+{
+ var y = document.getElementById("y");
+ var oldBody = document.body;
+ var newBody = document.createElement("body")
+ document.documentElement.insertBefore(newBody, oldBody);
+ setTimeout(function() {
+ document.documentElement.removeChild(oldBody);
+ newBody.appendChild(y);
+ ok(true, "we didn't assert");
+ SimpleTest.finish();
+ }, 0);
+}
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=895082"
+ title="Bug 895082 - replacing body element asserts">
+ Bug 895082</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+<div><div id="y"></div></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_canvas.html b/accessible/tests/mochitest/treeupdate/test_canvas.html
new file mode 100644
index 000000000..6ae129a67
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_canvas.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Canvas subdom mutation</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function addSubtree(aID)
+ {
+ this.node = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.node)
+ ];
+
+ this.invoke = function addSubtree_invoke()
+ {
+ // ensure we start with no subtree
+ testAccessibleTree("canvas", { CANVAS: [] });
+ getNode("dialog").style.display = "block";
+ }
+
+ this.finalCheck = function addSubtree_finalCheck() {
+ testAccessibleTree("dialog", { DIALOG: [] });
+ }
+
+ this.getID = function addSubtree_getID()
+ {
+ return "show canvas subdom";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ // make the subdom come alive!
+ gQueue.push(new addSubtree("dialog"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Expose content in Canvas element"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912">
+ Mozilla Bug 495912
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <canvas id="canvas">
+ <div id="dialog" role="dialog" style="display: none;">
+ </div>
+ </canvas>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_colorpicker.xul b/accessible/tests/mochitest/treeupdate/test_colorpicker.xul
new file mode 100644
index 000000000..9b15c9174
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_colorpicker.xul
@@ -0,0 +1,150 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL button hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function openColorpicker(aColorpickerID)
+ {
+ this.node = getNode(aColorpickerID);
+
+ this.eventSeq = [];
+
+ var it = new colorButtonIterator(this.node);
+ for (var btnNode = it.next(); btnNode = it.next(); btnNode)
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW, btnNode));
+
+ var popupNode = getPopupNode(this.node);
+ this.eventSeq.push(new invokerChecker(EVENT_REORDER, popupNode));
+
+ this.invoke = function openColorpicker_invoke()
+ {
+ var tree =
+ { BUTTONDROPDOWNGRID: [
+ { ALERT: [ ] }
+ ] };
+ testAccessibleTree(this.node, tree);
+
+ this.node.showPopup();
+ }
+
+ this.finalCheck = function openColorpicker_finalCheck()
+ {
+ var tree =
+ { BUTTONDROPDOWNGRID: [
+ { ALERT: [ ] }
+ ] };
+
+ var colorButtons = tree.BUTTONDROPDOWNGRID[0].ALERT;
+ var it = new colorButtonIterator(this.node);
+ while (it.next()) {
+ var obj = { PUSHBUTTON: [ ] };
+ colorButtons.push(obj);
+ }
+
+ testAccessibleTree(this.node, tree);
+ }
+
+ this.getID = function openColorpicker_getID()
+ {
+ return "open colorpicker " + prettyName(aColorpickerID);
+ }
+ }
+
+ function getPopupNode(aColorpickerNode)
+ {
+ return aColorpickerNode.mPicker.parentNode;
+ }
+
+ function colorButtonIterator(aColorpickerNode)
+ {
+ this.container = aColorpickerNode.mPicker.mBox;
+ this.group = this.container.firstChild;
+ this.item = null;
+
+ this.next = function colorButtonIterator_next()
+ {
+ if (!this.group)
+ return null;
+
+ if (!this.item) {
+ this.item = this.group.firstChild;
+ return this.item;
+ }
+
+ if (this.item.nextSibling) {
+ this.item = this.item.nextSibling;
+ return this.item;
+ }
+
+ if (this.group.nextSibling) {
+ this.group = this.group.nextSibling;
+ this.item = this.group.firstChild;
+ return this.item;
+ }
+
+ this.group = null;
+ this.item = null;
+ return null;
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new openColorpicker("colorpicker"));
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
+ title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
+ Mozilla Bug 249292
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486"
+ title="Don't force accessible creation for popup children.">
+ Mozilla Bug 630486
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <colorpicker id="colorpicker" type="button"/>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/test_contextmenu.xul b/accessible/tests/mochitest/treeupdate/test_contextmenu.xul
new file mode 100644
index 000000000..5b31e0136
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_contextmenu.xul
@@ -0,0 +1,317 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="menu tree and events">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ function openMenu(aID, aTree)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_START, getNode(aID))
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ var button = getNode("button");
+ getNode(aID).openPopup(button, "after_start", 0, 0, true, false);
+ }
+
+ this.finalCheck = function openMenu_finalCheck(aEvent)
+ {
+ testAccessibleTree(aID, aTree);
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "open menu " + prettyName(aID);
+ }
+ }
+
+ function selectNextMenuItem(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode(aID))
+ ];
+
+ this.invoke = function selectMenuItem_invoke()
+ {
+ synthesizeKey("VK_DOWN", { });
+ }
+
+ this.getID = function selectMenuItem_getID()
+ {
+ return "select menuitem " + prettyName(aID);
+ }
+ }
+
+ function openSubMenu(aSubMenuID, aItemID, aMenuID, aTree)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode(aItemID)),
+ ];
+
+ this.invoke = function openSubMenu_invoke()
+ {
+ synthesizeKey("VK_RETURN", { });
+ }
+
+ this.finalCheck = function openSubMenu_finalCheck(aEvent)
+ {
+ testAccessibleTree(aMenuID, aTree);
+ }
+
+ this.getID = function openSubMenu_getID()
+ {
+ return "open submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID);
+ }
+ }
+
+ function closeSubMenu(aSubMenuID, aItemID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode(aItemID)),
+ ];
+
+ this.invoke = function closeSubMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeSubMenu_getID()
+ {
+ return "close submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID);
+ }
+ }
+
+ function closeMenu(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_END, getNode(aID))
+ ];
+
+ this.invoke = function closeMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeMenu_getID()
+ {
+ return "close menu " + prettyName(aID);
+ }
+ }
+
+ //gA11yEventDumpToConsole = true;
+ //enableLogging("tree,verbose");
+
+ var gQueue = null;
+ var gContextTree = {};
+
+ // Linux and Windows menu trees discrepancy: bug 527646.
+
+ /**
+ * Return the context menu tree before submenus were open.
+ */
+ function getMenuTree1()
+ {
+ if (LINUX || SOLARIS) {
+ var tree = {
+ role: ROLE_MENUPOPUP,
+ children: [
+ {
+ name: "item0",
+ role: ROLE_MENUITEM,
+ children: []
+ },
+ {
+ name: "item1",
+ role: ROLE_MENUITEM,
+ children: []
+ },
+ {
+ name: "item2",
+ role: ROLE_PARENT_MENUITEM,
+ children: [ ]
+ }
+ ]
+ };
+ return tree;
+ }
+
+ // Windows
+ var tree = {
+ role: ROLE_MENUPOPUP,
+ children: [
+ {
+ name: "item0",
+ role: ROLE_MENUITEM,
+ children: []
+ },
+ {
+ name: "item1",
+ role: ROLE_MENUITEM,
+ children: []
+ },
+ {
+ name: "item2",
+ role: ROLE_PARENT_MENUITEM,
+ children: [
+ {
+ name: "item2",
+ role: ROLE_MENUPOPUP,
+ children: [ ]
+ }
+ ]
+ }
+ ]
+ };
+ return tree;
+ }
+
+ /**
+ * Return context menu tree when submenu was open.
+ */
+ function getMenuTree2()
+ {
+ var tree = getMenuTree1();
+ if (LINUX || SOLARIS) {
+ var submenuTree =
+ {
+ name: "item2.0",
+ role: ROLE_PARENT_MENUITEM,
+ children: [ ]
+ };
+ tree.children[2].children.push(submenuTree);
+ return tree;
+ }
+
+ // Windows
+ var submenuTree =
+ {
+ name: "item2.0",
+ role: ROLE_PARENT_MENUITEM,
+ children: [
+ {
+ name: "item2.0",
+ role: ROLE_MENUPOPUP,
+ children: [ ]
+ }
+ ]
+ };
+
+ tree.children[2].children[0].children.push(submenuTree);
+ return tree;
+ }
+
+ /**
+ * Return context menu tree when subsub menu was open.
+ */
+ function getMenuTree3()
+ {
+ var tree = getMenuTree2();
+ var subsubmenuTree =
+ {
+ name: "item2.0.0",
+ role: ROLE_MENUITEM,
+ children: []
+ };
+
+ if (LINUX || SOLARIS)
+ tree.children[2].children[0].children.push(subsubmenuTree);
+ else
+ tree.children[2].children[0].children[0].children[0].children.push(subsubmenuTree);
+
+ return tree;
+ }
+
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Check initial empty tree
+ testAccessibleTree("context", { MENUPOPUP: [] });
+
+ // Open context menu and check that menu item accesibles are created.
+ gQueue.push(new openMenu("context", getMenuTree1()));
+
+ // Select items and check focus event on them.
+ gQueue.push(new selectNextMenuItem("item0"));
+ gQueue.push(new selectNextMenuItem("item1"));
+ gQueue.push(new selectNextMenuItem("item2"));
+
+ // Open sub menu and check menu accessible tree and focus event.
+ gQueue.push(new openSubMenu("submenu2", "item2.0",
+ "context", getMenuTree2()));
+ gQueue.push(new openSubMenu("submenu2.0", "item2.0.0",
+ "context", getMenuTree3()));
+
+ // Close submenus and check that focus goes to parent.
+ gQueue.push(new closeSubMenu("submenu2.0", "item2.0"));
+ gQueue.push(new closeSubMenu("submenu2", "item2"));
+
+ gQueue.push(new closeMenu("context"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630194"
+ title="Update accessible tree when opening the menu popup">
+ Mozilla Bug 630194
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486"
+ title="Don't force accessible creation for popup children.">
+ Mozilla Bug 630486
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+
+ <menupopup id="context">
+ <menuitem id="item0" label="item0"/>
+ <menuitem id="item1" label="item1"/>
+ <menu id="item2" label="item2">
+ <menupopup id="submenu2">
+ <menu id="item2.0" label="item2.0">
+ <menupopup id="submenu2.0">
+ <menuitem id="item2.0.0" label="item2.0.0"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <button context="context" id="button">btn</button>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/treeupdate/test_cssoverflow.html b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html
new file mode 100644
index 000000000..ed9edd66e
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html
@@ -0,0 +1,143 @@
+<html>
+
+<head>
+ <title>Testing HTML scrollable frames (css overflow style)</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Change scroll range to not empty size and inserts a child into container
+ * to trigger tree update of the container. Prior to bug 677154 not empty
+ * size resulted to accessible creation for scroll area, container tree
+ * update picked up that accessible unattaching scroll area accessible
+ * subtree.
+ */
+ function changeScrollRange(aContainerID, aScrollAreaID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.container = getAccessible(this.containerNode);
+ this.scrollAreaNode = getNode(aScrollAreaID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function changeScrollRange_invoke()
+ {
+ this.scrollAreaNode.style.width = "20px";
+ this.containerNode.appendChild(document.createElement("input"));
+ }
+
+ this.finalCheck = function changeScrollRange_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { SECTION: [ // scroll area
+ { ENTRY: [] } // child content
+ ] },
+ { ENTRY: [] } // inserted input
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function changeScrollRange_getID()
+ {
+ return "change scroll range for " + prettyName(aScrollAreaID);
+ }
+ }
+
+ /**
+ * Change scrollbar styles from hidden to auto. That makes us to create an
+ * accessible for scroll area.
+ */
+ function changeScrollbarStyles(aContainerID, aScrollAreaID)
+ {
+ this.container = getAccessible(aContainerID);
+ this.scrollAreaNode = getNode(aScrollAreaID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getAccessible, this.scrollAreaNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function changeScrollbarStyles_invoke()
+ {
+ var accTree =
+ { SECTION: [] };
+ testAccessibleTree(this.container, accTree);
+
+ this.scrollAreaNode.style.overflow = "auto";
+ }
+
+ this.finalCheck = function changeScrollbarStyles_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { SECTION: [] } // scroll area
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function changeScrollbarStyles_getID()
+ {
+ return "change scrollbar styles " + prettyName(aScrollAreaID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ ////////////////////////////////////////////////////////////////////////////
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeScrollRange("container", "scrollarea"));
+ gQueue.push(new changeScrollbarStyles("container2", "scrollarea2"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=677154"
+ title="Detached document accessibility tree">
+ Mozilla Bug 677154</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="container"><div id="scrollarea" style="overflow:auto;"><input></div></div>
+ <div id="container2"><div id="scrollarea2" style="overflow:hidden;"></div></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_deck.xul b/accessible/tests/mochitest/treeupdate/test_deck.xul
new file mode 100644
index 000000000..13561abab
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_deck.xul
@@ -0,0 +1,109 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tree update on XUL deck panel switching">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function switchDeckPanel(aContainerID, aDeckID)
+ {
+ this.panelIndex = 0;
+
+ this.container = getAccessible(aContainerID);
+ this.deckNode = getNode(aDeckID);
+ this.prevPanel = getAccessible(this.deckNode.selectedPanel);
+ this.panelNode = this.deckNode.childNodes[this.panelIndex];
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.prevPanel),
+ new invokerChecker(EVENT_SHOW, this.panelNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function switchDeckPanel_invoke()
+ {
+ var tree =
+ { GROUPING: [ // role="group"
+ { GROUPING: [ // groupbox, a selected panel #2
+ { PUSHBUTTON: [ ] } // button
+ ] }
+ ] };
+ testAccessibleTree(this.container, tree);
+
+ this.deckNode.selectedIndex = this.panelIndex;
+ }
+
+ this.finalCheck = function switchDeckPanel_finalCheck()
+ {
+ var tree =
+ { GROUPING: [ // role="group"
+ { LABEL: [ // description, a selected panel #1
+ { TEXT_LEAF: [] } // text leaf, a description value
+ ] }
+ ] };
+ testAccessibleTree(this.container, tree);
+ }
+
+ this.getID = function switchDeckPanel_getID()
+ {
+ return "switch deck panel";
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new switchDeckPanel("container", "deck"));
+ gQueue.invoke(); // will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=814836"
+ title=" xul:deck element messes up screen reader">
+ Mozilla Bug 814836
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1" id="container" role="group">
+
+ <deck id="deck" selectedIndex="1">
+ <description>This is the first page</description>
+ <groupbox>
+ <button label="This is the second page"/>
+ </groupbox>
+ </deck>
+
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/test_doc.html b/accessible/tests/mochitest/treeupdate/test_doc.html
new file mode 100644
index 000000000..05896e7b4
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_doc.html
@@ -0,0 +1,466 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test document root content mutations</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function getDocNode(aID)
+ {
+ return getNode(aID).contentDocument;
+ }
+ function getDocChildNode(aID)
+ {
+ return getDocNode(aID).body.firstChild;
+ }
+
+ function rootContentReplaced(aID, aTextName, aRootContentRole)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
+ new invokerChecker(EVENT_REORDER, getDocNode, aID)
+ ];
+
+ this.finalCheck = function rootContentReplaced_finalCheck()
+ {
+ var tree = {
+ role: aRootContentRole || ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: aTextName
+ }
+ ]
+ };
+ testAccessibleTree(getDocNode(aID), tree);
+ }
+ }
+
+ function rootContentRemoved(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, null),
+ new invokerChecker(EVENT_REORDER, getDocNode, aID)
+ ];
+
+ this.preinvoke = function rootContentRemoved_preinvoke()
+ {
+ // Set up target for hide event before we invoke.
+ var text = getAccessible(getAccessible(getDocNode(aID)).firstChild);
+ this.eventSeq[0].target = text;
+ }
+
+ this.finalCheck = function rootContentRemoved_finalCheck()
+ {
+ var tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(getDocNode(aID), tree);
+ }
+ }
+
+ function rootContentInserted(aID, aTextName)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
+ new invokerChecker(EVENT_REORDER, getDocNode, aID)
+ ];
+
+ this.finalCheck = function rootContentInserted_finalCheck()
+ {
+ var tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: aTextName
+ }
+ ]
+ };
+ testAccessibleTree(getDocNode(aID), tree);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function writeIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentReplaced(aID, "hello");
+
+ this.invoke = function writeIFrameDoc_invoke()
+ {
+ var docNode = getDocNode(aID);
+
+ // We can't use open/write/close outside of iframe document because of
+ // security error.
+ var script = docNode.createElement("script");
+ script.textContent = "document.open(); document.write('hello'); document.close();";
+ docNode.body.appendChild(script);
+ }
+
+ this.getID = function writeIFrameDoc_getID()
+ {
+ return "write document";
+ }
+ }
+
+ /**
+ * Replace HTML element.
+ */
+ function replaceIFrameHTMLElm(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getDocChildNode, aID),
+ new invokerChecker(EVENT_REORDER, getDocNode, aID)
+ ];
+
+ this.invoke = function replaceIFrameHTMLElm_invoke()
+ {
+ var docNode = getDocNode(aID);
+ var newHTMLNode = docNode.createElement("html");
+ newHTMLNode.innerHTML = `<body><p>New Wave</p></body`;
+ docNode.replaceChild(newHTMLNode, docNode.documentElement);
+ }
+
+ this.finalCheck = function replaceIFrameHTMLElm_finalCheck()
+ {
+ var tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_PARAGRAPH,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Wave'
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree(getDocNode(aID), tree);
+ }
+
+ this.getID = function replaceIFrameHTMLElm_getID()
+ {
+ return "replace HTML element";
+ }
+ }
+
+ /**
+ * Replace HTML body on new body having ARIA role.
+ */
+ function replaceIFrameBody(aID)
+ {
+ this.__proto__ = new rootContentReplaced(aID, "New Hello");
+
+ this.invoke = function replaceIFrameBody_invoke()
+ {
+ var docNode = getDocNode(aID);
+ var newBodyNode = docNode.createElement("body");
+ var newTextNode = docNode.createTextNode("New Hello");
+ newBodyNode.appendChild(newTextNode);
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ }
+
+ this.getID = function replaceIFrameBody_getID()
+ {
+ return "replace body";
+ }
+ }
+
+ /**
+ * Replace HTML body on new body having ARIA role.
+ */
+ function replaceIFrameBodyOnARIARoleBody(aID)
+ {
+ this.__proto__ = new rootContentReplaced(aID, "New Hello",
+ ROLE_PUSHBUTTON);
+
+ this.invoke = function replaceIFrameBodyOnARIARoleBody_invoke()
+ {
+ var docNode = getDocNode(aID);
+ var newBodyNode = docNode.createElement("body");
+ var newTextNode = docNode.createTextNode("New Hello");
+ newBodyNode.appendChild(newTextNode);
+ newBodyNode.setAttribute("role", "button");
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ }
+
+ this.getID = function replaceIFrameBodyOnARIARoleBody_getID()
+ {
+ return "replace body on body having ARIA role";
+ }
+ }
+
+ /**
+ * Open/close document pair.
+ */
+ function openIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentRemoved(aID);
+
+ this.invoke = function openIFrameDoc_invoke()
+ {
+ this.preinvoke();
+
+ // Open document.
+ var docNode = getDocNode(aID);
+ var script = docNode.createElement("script");
+ script.textContent = "function closeMe() { document.write('Works?'); document.close(); } window.closeMe = closeMe; document.open();";
+ docNode.body.appendChild(script);
+ }
+
+ this.getID = function openIFrameDoc_getID()
+ {
+ return "open document";
+ }
+ }
+
+ function closeIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentInserted(aID, "Works?");
+
+ this.invoke = function closeIFrameDoc_invoke()
+ {
+ // Write and close document.
+ getDocNode(aID).write('Works?'); getDocNode(aID).close();
+ }
+
+ this.getID = function closeIFrameDoc_getID()
+ {
+ return "close document";
+ }
+ }
+
+ /**
+ * Remove/insert HTML element pair.
+ */
+ function removeHTMLFromIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentRemoved(aID);
+
+ this.invoke = function removeHTMLFromIFrameDoc_invoke()
+ {
+ this.preinvoke();
+
+ // Remove HTML element.
+ var docNode = getDocNode(aID);
+ docNode.removeChild(docNode.firstChild);
+ }
+
+ this.getID = function removeHTMLFromIFrameDoc_getID()
+ {
+ return "remove HTML element";
+ }
+ }
+
+ function insertHTMLToIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentInserted(aID, "Haha");
+
+ this.invoke = function insertHTMLToIFrameDoc_invoke()
+ {
+ // Insert HTML element.
+ var docNode = getDocNode(aID);
+ var html = docNode.createElement("html");
+ var body = docNode.createElement("body");
+ var text = docNode.createTextNode("Haha");
+ body.appendChild(text);
+ html.appendChild(body);
+ docNode.appendChild(html);
+ }
+
+ this.getID = function insertHTMLToIFrameDoc_getID()
+ {
+ return "insert HTML element document";
+ }
+ }
+
+ /**
+ * Remove/insert HTML body pair.
+ */
+ function removeBodyFromIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentRemoved(aID);
+
+ this.invoke = function removeBodyFromIFrameDoc_invoke()
+ {
+ this.preinvoke();
+
+ // Remove body element.
+ var docNode = getDocNode(aID);
+ docNode.documentElement.removeChild(docNode.body);
+ }
+
+ this.getID = function removeBodyFromIFrameDoc_getID()
+ {
+ return "remove body element";
+ }
+ }
+
+ function insertElmUnderDocElmWhileBodyMissed(aID)
+ {
+ this.docNode = null;
+ this.inputNode = null;
+
+ function getInputNode()
+ { return this.inputNode; }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getInputNode.bind(this)),
+ new invokerChecker(EVENT_REORDER, getDocNode, aID)
+ ];
+
+ this.invoke = function invoke()
+ {
+ this.docNode = getDocNode(aID);
+ this.inputNode = this.docNode.createElement("input");
+ this.docNode.documentElement.appendChild(this.inputNode);
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { DOCUMENT: [
+ { ENTRY: [ ] }
+ ] };
+ testAccessibleTree(this.docNode, tree);
+
+ // Remove aftermath of this test before next test starts.
+ this.docNode.documentElement.removeChild(this.inputNode);
+ }
+
+ this.getID = function getID()
+ {
+ return "Insert element under document element while body is missed.";
+ }
+ }
+
+ function insertBodyToIFrameDoc(aID)
+ {
+ this.__proto__ = new rootContentInserted(aID, "Yo ho ho i butylka roma!");
+
+ this.invoke = function insertBodyToIFrameDoc_invoke()
+ {
+ // Insert body element.
+ var docNode = getDocNode(aID);
+ var body = docNode.createElement("body");
+ var text = docNode.createTextNode("Yo ho ho i butylka roma!");
+ body.appendChild(text);
+ docNode.documentElement.appendChild(body);
+ }
+
+ this.getID = function insertBodyToIFrameDoc_getID()
+ {
+ return "insert body element";
+ }
+ }
+
+ function changeSrc(aID)
+ {
+ this.containerNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function changeSrc_invoke()
+ {
+ this.containerNode.src = "data:text/html,<html><input></html>";
+ }
+
+ this.finalCheck = function changeSrc_finalCheck()
+ {
+ var tree =
+ { INTERNAL_FRAME: [
+ { DOCUMENT: [
+ { ENTRY: [ ] }
+ ] }
+ ] };
+ testAccessibleTree(this.containerNode, tree);
+ }
+
+ this.getID = function changeSrc_getID()
+ {
+ return "change src on iframe";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpToConsole = true;
+ //enableLogging('tree,verbose');
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new writeIFrameDoc("iframe"));
+ gQueue.push(new replaceIFrameHTMLElm("iframe"));
+ gQueue.push(new replaceIFrameBody("iframe"));
+ gQueue.push(new openIFrameDoc("iframe"));
+ gQueue.push(new closeIFrameDoc("iframe"));
+ gQueue.push(new removeHTMLFromIFrameDoc("iframe"));
+ gQueue.push(new insertHTMLToIFrameDoc("iframe"));
+ gQueue.push(new removeBodyFromIFrameDoc("iframe"));
+ gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe"));
+ gQueue.push(new insertBodyToIFrameDoc("iframe"));
+ gQueue.push(new changeSrc("iframe"));
+ gQueue.push(new replaceIFrameBodyOnARIARoleBody("iframe"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Update accessible tree when root element is changed"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=606082">Mozilla Bug 606082</a>
+ <a target="_blank"
+ title="Elements inserted outside the body aren't accessible"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
+ <a target="_blank"
+ title="Reorder event for document must be fired after document initial tree creation"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=669263">Mozilla Bug 669263</a>
+ <a target="_blank"
+ title="Changing the HTML body doesn't pick up ARIA role"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=818407">Mozilla Bug 818407</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <iframe id="iframe"></iframe>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_gencontent.html b/accessible/tests/mochitest/treeupdate/test_gencontent.html
new file mode 100644
index 000000000..1cf22b36f
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_gencontent.html
@@ -0,0 +1,160 @@
+<html>
+
+<head>
+ <title>Elements with CSS generated content</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ .gentext:before {
+ content: "START"
+ }
+ .gentext:after {
+ content: "END"
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Insert new node with CSS generated content style applied to container.
+ */
+ function insertNodeHavingGenContent(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.container = getAccessible(this.containerNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getFirstChild, this.container),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function insertNodeHavingGenContent_invoke()
+ {
+ var node = document.createElement("div");
+ node.textContent = "text";
+ node.setAttribute("class", "gentext");
+ this.containerNode.appendChild(node);
+ }
+
+ this.finalCheck = function insertNodeHavingGenContent_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { SECTION: [ // inserted node
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function insertNodeHavingGenContent_getID()
+ {
+ return "insert node having generated content to " + prettyName(aContainerID);
+ }
+ }
+
+ /**
+ * Add CSS generated content to the given node contained by container node.
+ */
+ function addGenContent(aContainerID, aNodeID)
+ {
+ this.container = getAccessible(aContainerID);
+ this.node = getNode(aNodeID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.container.firstChild),
+ new invokerChecker(EVENT_SHOW, getFirstChild, this.container),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function addGenContent_invoke()
+ {
+ this.node.setAttribute("class", "gentext");
+ }
+
+ this.finalCheck = function insertNodeHavingGenContent_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { SECTION: [ // inserted node
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function addGenContent_getID()
+ {
+ return "add generated content to" + prettyName(aNodeID);
+ }
+ }
+
+ /**
+ * Target getters.
+ */
+ function getFirstChild(aAcc)
+ {
+ try { return aAcc.getChildAt(0); }
+ catch (e) { return null; }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ ////////////////////////////////////////////////////////////////////////////
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new insertNodeHavingGenContent("container1"));
+ gQueue.push(new addGenContent("container2", "container2_child"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=646350"
+ title="Add a test for dynamic chnages of CSS generated content">
+ Mozilla Bug 646350</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="container1"></div>
+ <div id="container2"><div id="container2_child">text</div></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_general.html b/accessible/tests/mochitest/treeupdate/test_general.html
new file mode 100644
index 000000000..9a8862bea
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_general.html
@@ -0,0 +1,150 @@
+<html>
+
+<head>
+ <title>Testing the tree updates</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+ ////////////////////////////////////////////////////////////////////////////
+
+ function prependAppend(aContainer)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aContainer)
+ ];
+
+ this.invoke = function prependAppend_invoke()
+ {
+ var checkbox = document.createElement('input');
+ checkbox.setAttribute('type', 'checkbox');
+ getNode(aContainer).insertBefore(checkbox, getNode(aContainer).firstChild);
+
+ var button = document.createElement('input');
+ button.setAttribute('type', 'button');
+ getNode(aContainer).appendChild(button);
+ }
+
+ this.finalCheck = function prependAppend_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { CHECKBUTTON: [ ] },
+ { ENTRY: [ ] },
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree(aContainer, accTree);
+ }
+
+ this.getID = function prependAppend_getID()
+ {
+ return "prepends a child and appends a child";
+ }
+ }
+
+ function removeRemove(aContainer)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aContainer)
+ ];
+
+ this.invoke = function removeRemove_invoke()
+ {
+ getNode(aContainer).removeChild(getNode(aContainer).firstChild);
+ }
+
+ this.finalCheck = function removeRemove_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree(aContainer, accTree);
+ }
+
+ this.getID = function removeRemove_getID()
+ {
+ return "remove first and second children";
+ }
+ }
+
+ function insertInaccessibleAccessibleSiblings()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, "c3")
+ ];
+
+ this.invoke = function insertInaccessibleAccessibleSiblings_invoke()
+ {
+ getNode("c3").appendChild(document.createElement("span"));
+ getNode("c3").appendChild(document.createElement("input"));
+ }
+
+ this.finalCheck = function insertInaccessibleAccessibleSiblings_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ // container
+ { PUSHBUTTON: [
+ { TEXT_LEAF: [] }
+ ] },
+ { ENTRY: [ ] }
+ ] };
+ testAccessibleTree("c3", accTree);
+ }
+
+ this.getID = function insertInaccessibleAccessibleSiblings_getID()
+ {
+ return "insert inaccessible and then accessible siblings";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ ////////////////////////////////////////////////////////////////////////////
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new prependAppend("c1"));
+ gQueue.push(new removeRemove("c2"));
+ gQueue.push(new insertInaccessibleAccessibleSiblings());
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1"><input></div>
+ <div id="c2"><span><input type="checkbox"><input></span><input type="button"></div>
+
+ <div id="c3"><input type="button" value="button"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_hidden.html b/accessible/tests/mochitest/treeupdate/test_hidden.html
new file mode 100644
index 000000000..2adb9efeb
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_hidden.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>@hidden attribute testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set @hidden attribute
+ */
+ function setHiddenAttr(aContainerID, aChildID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function setHiddenAttr_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { ENTRY: [
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aChildID).setAttribute("hidden", "true");
+ }
+
+ this.finalCheck = function setHiddenAttr_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function setHiddenAttr_getID()
+ {
+ return "Set @hidden attribute on input and test accessible tree for div";
+ }
+ }
+
+ /**
+ * Remove @hidden attribute
+ */
+ function removeHiddenAttr(aContainerID, aChildID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function removeHiddenAttr_invoke()
+ {
+ var tree =
+ { SECTION: [
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aChildID).removeAttribute("hidden");
+ }
+
+ this.finalCheck = function removeHiddenAttr_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { ENTRY: [
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function removeHiddenAttr_getID()
+ {
+ return "Remove @hidden attribute on input and test accessible tree for div";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+ ////////////////////////////////////////////////////////////////////////////
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setHiddenAttr("container", "child"));
+ gQueue.push(new removeHiddenAttr("container", "child"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+ </script>
+
+</head>
+
+<body>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container"><input id="child"></div>
+
+ <div id="eventdump"></div>
+
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_imagemap.html b/accessible/tests/mochitest/treeupdate/test_imagemap.html
new file mode 100644
index 000000000..c7c06b3d1
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html
@@ -0,0 +1,442 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>HTML img map accessible tree update tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ function insertArea(aImageMapID, aMapID)
+ {
+ this.imageMap = getAccessible(aImageMapID);
+ this.mapNode = getNode(aMapID);
+
+ function getInsertedArea(aThisObj)
+ {
+ return aThisObj.imageMap.firstChild;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getInsertedArea, this),
+ new invokerChecker(EVENT_REORDER, this.imageMap)
+ ];
+
+ this.invoke = function insertArea_invoke()
+ {
+ var areaElm = document.createElement("area");
+ 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");
+
+ this.mapNode.insertBefore(areaElm, this.mapNode.firstChild);
+ }
+
+ this.finalCheck = function insertArea_finalCheck()
+ {
+ var accTree =
+ { IMAGE_MAP: [
+ {
+ role: ROLE_LINK,
+ name: "a",
+ children: [ ]
+ },
+ {
+ role: ROLE_LINK,
+ name: "b",
+ children: [ ]
+ },
+ ] };
+ testAccessibleTree(this.imageMap, accTree);
+ }
+
+ this.getID = function insertArea_getID()
+ {
+ return "insert area element";
+ }
+ }
+
+ function appendArea(aImageMapID, aMapID)
+ {
+ this.imageMap = getAccessible(aImageMapID);
+ this.mapNode = getNode(aMapID);
+
+ function getAppendedArea(aThisObj)
+ {
+ return aThisObj.imageMap.lastChild;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getAppendedArea, this),
+ new invokerChecker(EVENT_REORDER, this.imageMap)
+ ];
+
+ this.invoke = function appendArea_invoke()
+ {
+ var areaElm = document.createElement("area");
+ 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");
+
+ this.mapNode.appendChild(areaElm);
+ }
+
+ this.finalCheck = function appendArea_finalCheck()
+ {
+ var accTree =
+ { IMAGE_MAP: [
+ {
+ role: ROLE_LINK,
+ name: "a",
+ children: [ ]
+ },
+ {
+ role: ROLE_LINK,
+ name: "b",
+ children: [ ]
+ },
+ {
+ role: ROLE_LINK,
+ name: "c",
+ children: [ ]
+ }
+ ] };
+ testAccessibleTree(this.imageMap, accTree);
+ }
+
+ this.getID = function appendArea_getID()
+ {
+ return "append area element";
+ }
+ }
+
+ function removeArea(aImageMapID, aMapID)
+ {
+ this.imageMap = getAccessible(aImageMapID);
+ this.area = null;
+ this.mapNode = getNode(aMapID);
+
+ function getRemovedArea(aThisObj)
+ {
+ return aThisObj.area;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getRemovedArea, this),
+ new invokerChecker(EVENT_REORDER, this.imageMap)
+ ];
+
+ this.invoke = function removeArea_invoke()
+ {
+ this.area = this.imageMap.firstChild;
+ this.mapNode.removeChild(this.mapNode.firstElementChild);
+ }
+
+ this.finalCheck = function removeArea_finalCheck()
+ {
+ var accTree =
+ { IMAGE_MAP: [
+ {
+ role: ROLE_LINK,
+ name: "b",
+ children: [ ]
+ },
+ {
+ role: ROLE_LINK,
+ name: "c",
+ children: [ ]
+ }
+ ] };
+ testAccessibleTree(this.imageMap, accTree);
+ }
+
+ this.getID = function removeArea_getID()
+ {
+ return "remove area element";
+ }
+ }
+
+ function removeNameOnMap(aImageMapContainerID, aImageMapID, aMapID)
+ {
+ this.container = getAccessible(aImageMapContainerID);
+ this.containerNode = this.container.DOMNode;
+ this.imageMap = getAccessible(aImageMapID);
+ this.imgNode = this.imageMap.DOMNode;
+ this.mapNode = getNode(aMapID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.imageMap),
+ new invokerChecker(EVENT_SHOW, this.imgNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function removeNameOnMap_invoke()
+ {
+ this.mapNode.removeAttribute("name");
+ }
+
+ this.finalCheck = function removeNameOnMap_finalCheck()
+ {
+ var accTree =
+ { SECTION: [
+ { GRAPHIC: [ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function removeNameOnMap_getID()
+ {
+ return "remove @name on map element";
+ }
+ }
+
+ function restoreNameOnMap(aImageMapContainerID, aImageMapID, aMapID)
+ {
+ this.container = getAccessible(aImageMapContainerID);
+ this.containerNode = this.container.DOMNode;
+ this.imageMap = null;
+ this.imgNode = getNode(aImageMapID);
+ this.mapNode = getNode(aMapID);
+
+ function getImageMap(aThisObj)
+ {
+ return aThisObj.imageMap;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getImageMap, this),
+ new invokerChecker(EVENT_SHOW, this.imgNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function restoreNameOnMap_invoke()
+ {
+ this.imageMap = getAccessible(aImageMapID);
+ this.mapNode.setAttribute("name", "atoz_map");
+
+ // XXXhack: force repainting of the image (see bug 745788 for details).
+ waveOverImageMap(aImageMapID);
+ }
+
+ this.finalCheck = function removeNameOnMap_finalCheck()
+ {
+ var accTree =
+ { SECTION: [
+ { IMAGE_MAP: [
+ { LINK: [ ] },
+ { LINK: [ ] }
+ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function removeNameOnMap_getID()
+ {
+ return "restore @name on map element";
+ }
+ }
+
+ function removeMap(aImageMapContainerID, aImageMapID, aMapID)
+ {
+ this.container = getAccessible(aImageMapContainerID);
+ this.containerNode = this.container.DOMNode;
+ this.imageMap = null;
+ this.imgNode = getNode(aImageMapID);
+ this.mapNode = getNode(aMapID);
+
+ function getImageMap(aThisObj)
+ {
+ return aThisObj.imageMap;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getImageMap, this),
+ new invokerChecker(EVENT_SHOW, this.imgNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function removeMap_invoke()
+ {
+ this.imageMap = getAccessible(aImageMapID);
+ this.mapNode.parentNode.removeChild(this.mapNode);
+ }
+
+ this.finalCheck = function removeMap_finalCheck()
+ {
+ var accTree =
+ { SECTION: [
+ { GRAPHIC: [ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function removeMap_getID()
+ {
+ return "remove map element";
+ }
+ }
+
+ function insertMap(aImageMapContainerID, aImageID)
+ {
+ this.container = getAccessible(aImageMapContainerID);
+ this.containerNode = this.container.DOMNode;
+ this.image = null;
+ this.imgMapNode = getNode(aImageID);
+
+ function getImage(aThisObj)
+ {
+ return aThisObj.image;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getImage, this),
+ new invokerChecker(EVENT_SHOW, this.imgMapNode),
+ new invokerChecker(EVENT_REORDER, this.container)
+ ];
+
+ this.invoke = function insertMap_invoke()
+ {
+ this.image = getAccessible(aImageID);
+
+ var map = document.createElement("map");
+ map.setAttribute("name", "atoz_map");
+ map.setAttribute("id", "map");
+
+ var area = document.createElement("area")
+ 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);
+
+ this.containerNode.appendChild(map);
+ }
+
+ this.finalCheck = function insertMap_finalCheck()
+ {
+ var accTree =
+ { SECTION: [
+ { IMAGE_MAP: [
+ { LINK: [ ] }
+ ] }
+ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function insertMap_getID()
+ {
+ return "insert map element";
+ }
+ }
+
+ function hideImageMap(aContainerID, aImageID)
+ {
+ this.container = getAccessible(aContainerID);
+ this.imageMap = null;
+ this.imageMapNode = getNode(aImageID);
+
+ function getImageMap(aThisObj)
+ {
+ return aThisObj.imageMap;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getImageMap, this),
+ new invokerChecker(EVENT_REORDER, aContainerID)
+ ];
+
+ this.invoke = function hideImageMap_invoke()
+ {
+ this.imageMap = getAccessible(this.imageMapNode);
+ this.imageMapNode.style.display = "none";
+ }
+
+ this.finalCheck = function hideImageMap_finalCheck()
+ {
+ var accTree =
+ { SECTION: [ ] };
+ testAccessibleTree(this.container, accTree);
+ }
+
+ this.getID = function hideImageMap_getID()
+ {
+ return "display:none image";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ function doPreTest()
+ {
+ waitForImageMap("imgmap", doTest);
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new insertArea("imgmap", "map"));
+ gQueue.push(new appendArea("imgmap", "map"));
+ gQueue.push(new removeArea("imgmap", "map"));
+ gQueue.push(new removeNameOnMap("container", "imgmap", "map"));
+ gQueue.push(new restoreNameOnMap("container", "imgmap", "map"));
+ gQueue.push(new removeMap("container", "imgmap", "map"));
+ gQueue.push(new insertMap("container", "imgmap"));
+ gQueue.push(new hideImageMap("container", "imgmap"));
+
+ //enableLogging("tree"); // debug stuff
+ //gQueue.onFinish = function() { disableLogging("tree"); }
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doPreTest);
+ </script>
+
+</head>
+<body>
+
+ <a target="_blank"
+ title="Image map accessible tree is not updated when image map is changed"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=732389">
+ Mozilla Bug 732389
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <map name="atoz_map" id="map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ </map>
+
+ <div id="container">
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="../letters.gif"><!--
+ Important: no whitespace between the <img> and the </div>, so we
+ don't end up with textframes there, because those would be reflected
+ in our accessible tree in some cases.
+ --></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_list.html b/accessible/tests/mochitest/treeupdate/test_list.html
new file mode 100644
index 000000000..9196142d9
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_list.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test HTML li and listitem bullet accessible cache</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function testLiAccessibleTree()
+ {
+ // Test accessible tree.
+ var accTree = {
+ role: ROLE_LISTITEM,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ children: []
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ };
+
+ testAccessibleTree("li", accTree);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Sequence item processors
+
+ function hideProcessor()
+ {
+ this.liNode = getNode("li");
+ this.li = getAccessible(this.liNode);
+ this.bullet = this.li.firstChild;
+
+ this.process = function hideProcessor_process()
+ {
+ this.liNode.style.display = "none";
+ }
+
+ this.onProcessed = function hideProcessor_onProcessed()
+ {
+ window.setTimeout(
+ function(aLiAcc, aLiNode, aBulletAcc)
+ {
+ testDefunctAccessible(aLiAcc, aLiNode);
+ testDefunctAccessible(aBulletAcc);
+
+ gSequence.processNext();
+ },
+ 0, this.li, this.liNode, this.bullet
+ );
+ }
+ };
+
+ function showProcessor()
+ {
+ this.liNode = getNode("li");
+
+ this.process = function showProcessor_process()
+ {
+ this.liNode.style.display = "list-item";
+ }
+
+ this.onProcessed = function showProcessor_onProcessed()
+ {
+ testLiAccessibleTree();
+ gSequence.processNext();
+ }
+ };
+
+ function textReplaceProcessor()
+ {
+ this.liNode = getNode("li");
+
+ this.process = function textReplaceProcessor_process()
+ {
+ this.liNode.textContent = "hey";
+ }
+
+ this.onProcessed = function textReplaceProcessor_onProcessed()
+ {
+ var tree = {
+ LISTITEM: [
+ { STATICTEXT: [] },
+ { TEXT_LEAF: [] }
+ ]
+ };
+ testAccessibleTree(this.liNode, tree);
+ SimpleTest.finish();
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpToConsole = true;
+
+ var gSequence = null;
+ function doTest()
+ {
+ testLiAccessibleTree();
+
+ gSequence = new sequence();
+
+ gSequence.append(new hideProcessor(), EVENT_HIDE, getAccessible("li"),
+ "hide HTML li");
+ gSequence.append(new showProcessor(), EVENT_SHOW, getNode("li"),
+ "show HTML li");
+ gSequence.append(new textReplaceProcessor(), EVENT_REORDER, getNode("li"),
+ "change text of HTML li");
+
+ gSequence.processNext(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="setParent shouldn't be virtual"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=496783">Mozilla Bug 496783</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ul>
+ <li id="li">item1</li>
+ </ul>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html
new file mode 100644
index 000000000..d4c178cb9
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test HTML li and listitem bullet accessible insertion into editable document</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function addLi(aID)
+ {
+ this.listNode = getNode(aID);
+ this.liNode = document.createElement("li");
+ this.liNode.textContent = "item";
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getAccessible, this.liNode),
+ new invokerChecker(EVENT_REORDER, this.listNode)
+ ];
+
+ this.invoke = function addLi_invoke()
+ {
+ this.listNode.appendChild(this.liNode);
+ }
+
+ this.finalCheck = function addLi_finalCheck()
+ {
+ var tree = {
+ role: ROLE_LIST,
+ children: [
+ {
+ role: ROLE_LISTITEM,
+ children: [
+ {
+ role: ROLE_STATICTEXT,
+ name: "1. ",
+ children: []
+ },
+ {
+ role: ROLE_TEXT_LEAF,
+ children: []
+ }
+ ]
+ }
+ ]
+ };
+ testAccessibleTree(aID, tree);
+ }
+
+ this.getID = function addLi_getID()
+ {
+ return "add li";
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new addLi("list"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body contentEditable="true">
+
+ <a target="_blank"
+ title="Wrong list bullet text of accessible for the first numbered HTML:li in CKEditor"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=557795">Mozilla Bug 557795</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <ol id="list">
+ </ol>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_listbox.xul b/accessible/tests/mochitest/treeupdate/test_listbox.xul
new file mode 100644
index 000000000..862b4dde8
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_listbox.xul
@@ -0,0 +1,180 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL listbox hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ function insertListitem(aListboxID)
+ {
+ this.listboxNode = getNode(aListboxID);
+
+ this.listitemNode = document.createElement("listitem");
+ this.listitemNode.setAttribute("label", "item1");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.listitemNode),
+ new invokerChecker(EVENT_REORDER, this.listboxNode)
+ ];
+
+ this.invoke = function insertListitem_invoke()
+ {
+ this.listboxNode.insertBefore(this.listitemNode,
+ this.listboxNode.firstChild);
+ }
+
+ this.finalCheck = function insertListitem_finalCheck()
+ {
+ var tree =
+ { LISTBOX: [
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item1"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item2"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item3"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item4"
+ }
+ ] };
+ testAccessibleTree(this.listboxNode, tree);
+ }
+
+ this.getID = function insertListitem_getID()
+ {
+ return "insert listitem ";
+ }
+ }
+
+ function removeListitem(aListboxID)
+ {
+ this.listboxNode = getNode(aListboxID);
+ this.listitemNode = null;
+ this.listitem;
+
+ function getListitem(aThisObj)
+ {
+ return aThisObj.listitem;
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getListitem, this),
+ new invokerChecker(EVENT_REORDER, this.listboxNode)
+ ];
+
+ this.invoke = function removeListitem_invoke()
+ {
+ this.listitemNode = this.listboxNode.firstChild;
+ this.listitem = getAccessible(this.listitemNode);
+
+ this.listboxNode.removeChild(this.listitemNode);
+ }
+
+ this.finalCheck = function removeListitem_finalCheck()
+ {
+ var tree =
+ { LISTBOX: [
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item2"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item3"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item4"
+ }
+ ] };
+ testAccessibleTree(this.listboxNode, tree);
+ }
+
+ this.getID = function removeListitem_getID()
+ {
+ return "remove listitem ";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ var tree =
+ { LISTBOX: [
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item2"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item3"
+ },
+ {
+ role: ROLE_RICH_OPTION,
+ name: "item4"
+ }
+ ] };
+ testAccessibleTree("listbox", tree);
+
+ gQueue = new eventQueue();
+ gQueue.push(new insertListitem("listbox"));
+ gQueue.push(new removeListitem("listbox"));
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=656225"
+ title="XUL listbox accessible tree doesn't get updated">
+ Mozilla Bug 656225
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <listbox id="listbox" rows="2">
+ <listitem label="item2"/>
+ <listitem label="item3"/>
+ <listitem label="item4"/>
+ </listbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/test_menu.xul b/accessible/tests/mochitest/treeupdate/test_menu.xul
new file mode 100644
index 000000000..abdea217e
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_menu.xul
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL menu hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function openMenu(aID)
+ {
+ this.menuNode = getNode(aID),
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.menuNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ var tree;
+ if (LINUX || SOLARIS) {
+ tree =
+ { PARENT_MENUITEM: [ ] };
+
+ } else {
+ tree =
+ { PARENT_MENUITEM: [
+ { MENUPOPUP: [ ] }
+ ] };
+ }
+ testAccessibleTree(aID, tree);
+
+ // Show menu.
+ this.menuNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ var tree;
+ if (LINUX || SOLARIS) {
+ tree =
+ { PARENT_MENUITEM: [
+ { MENUITEM: [ ] },
+ { MENUITEM: [ ] }
+ ] };
+
+ } else {
+ tree =
+ { PARENT_MENUITEM: [
+ { MENUPOPUP: [
+ { MENUITEM: [ ] },
+ { MENUITEM: [ ] }
+ ] }
+ ] };
+ }
+ testAccessibleTree(aID, tree);
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "open menu " + prettyName(aID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new openMenu("menu"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
+ title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
+ Mozilla Bug 249292
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486"
+ title="Don't force accessible creation for popup children.">
+ Mozilla Bug 630486
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar>
+ <menu id="menu" label="menu">
+ <menupopup>
+ <menuitem label="menuitem"/>
+ <menuitem label="menuitem"/>
+ </menupopup>
+ </menu>
+ </menubar>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/test_menubutton.xul b/accessible/tests/mochitest/treeupdate/test_menubutton.xul
new file mode 100644
index 000000000..4821a265b
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_menubutton.xul
@@ -0,0 +1,198 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL button hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function openMenu(aButtonID, aMenuItemRole)
+ {
+ var menuItemRole = aMenuItemRole || ROLE_MENUITEM;
+ this.button = getAccessible(aButtonID);
+ this.menupopup = this.button.firstChild;
+
+ var checker = new invokerChecker(EVENT_REORDER, this.menupopup);
+ this.__proto__ = new synthClick(aButtonID, checker);
+
+ this.invoke = function openMenu_invoke()
+ {
+ var tree =
+ { PUSHBUTTON: [
+ { MENUPOPUP: [ ] }
+ ] };
+ testAccessibleTree(this.button, tree);
+
+ this.__proto__.invoke();
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ var tree =
+ { PUSHBUTTON: [
+ { MENUPOPUP: [
+ { role: menuItemRole, children: [ ] },
+ { role: menuItemRole, children: [ ] }
+ ] }
+ ] };
+ testAccessibleTree(this.button, tree);
+
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "open menu of the button " + prettyName(aButtonID);
+ }
+ }
+
+ function openMenuButton(aButtonID)
+ {
+ this.buttonNode = getNode(aButtonID);
+ this.menupoupNode = this.buttonNode.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.menupoupNode)
+ ];
+
+ this.invoke = function openMenu_invoke()
+ {
+ var tree =
+ { PUSHBUTTON: [
+ { MENUPOPUP: [ ] },
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree(this.buttonNode, tree);
+
+ this.buttonNode.open = true;
+ }
+
+ this.finalCheck = function openMenu_finalCheck()
+ {
+ var tree =
+ { PUSHBUTTON: [
+ { MENUPOPUP: [
+ { MENUITEM: [ ] },
+ { MENUITEM: [ ] }
+ ] },
+ { PUSHBUTTON: [ ] }
+ ] };
+ testAccessibleTree(this.buttonNode, tree);
+
+ this.buttonNode.open = false;
+ }
+
+ this.getID = function openMenu_getID()
+ {
+ return "open menu for menu button " + prettyName(aButtonID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do test
+
+ gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new openMenu("button1"));
+ gQueue.push(new openMenuButton("button2"));
+ gQueue.push(new openMenu("button3"));
+ gQueue.push(new openMenuButton("button4"));
+
+ var columnPickerBtn = getAccessible("tree").firstChild.lastChild;
+ gQueue.push(new openMenu(columnPickerBtn, ROLE_CHECK_MENU_ITEM));
+ gQueue.invoke(); // SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292"
+ title="Ensure accessible children for toolbarbutton types 'menu' and 'menu-button'">
+ Bug 249292
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486"
+ title="Don't force accessible creation for popup children">
+ Bug 630486
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=722265"
+ title="Column header selection popup no longer exposed to accessibility APIs">
+ Bug 722265
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <button id="button1" type="menu" label="button">
+ <menupopup>
+ <menuitem label="menuitem"/>
+ <menuitem label="menuitem"/>
+ </menupopup>
+ </button>
+ <button id="button2" type="menu-button" label="menu button">
+ <menupopup>
+ <menuitem label="menuitem"/>
+ <menuitem label="menuitem"/>
+ </menupopup>
+ </button>
+
+ <toolbarbutton id="button3" type="menu" label="toolbarbutton">
+ <menupopup>
+ <menuitem label="menuitem"/>
+ <menuitem label="menuitem"/>
+ </menupopup>
+ </toolbarbutton>
+ <toolbarbutton id="button4" type="menu-button" label="menu toolbarbutton">
+ <menupopup>
+ <menuitem label="menuitem"/>
+ <menuitem label="menuitem"/>
+ </menupopup>
+ </toolbarbutton>
+
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="another column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/treeupdate/test_optgroup.html b/accessible/tests/mochitest/treeupdate/test_optgroup.html
new file mode 100644
index 000000000..27323bbc3
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_optgroup.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Add and remove optgroup test</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ function addOptGroup(aID)
+ {
+ this.selectNode = getNode(aID);
+ this.select = getAccessible(this.selectNode);
+ this.selectList = this.select.firstChild;
+
+ this.invoke = function addOptGroup_invoke()
+ {
+ var optGroup = document.createElement("optgroup");
+ for (i = 0; i < 2; i++) {
+ var opt = document.createElement("option");
+ opt.value = i;
+ opt.text = "Option: Value " + i;
+
+ optGroup.appendChild(opt);
+ }
+
+ this.selectNode.add(optGroup, null);
+ var option = document.createElement("option");
+ this.selectNode.add(option, null);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.selectList)
+ ];
+
+ this.finalCheck = function addOptGroup_finalCheck()
+ {
+ var tree =
+ { COMBOBOX: [
+ { COMBOBOX_LIST: [
+ { GROUPING: [
+ { COMBOBOX_OPTION: [
+ { TEXT_LEAF: [] }
+ ] },
+ { COMBOBOX_OPTION: [
+ { TEXT_LEAF: [] }
+ ] },
+ ]},
+ { COMBOBOX_OPTION: [] }
+ ] }
+ ] };
+ testAccessibleTree(this.select, tree);
+ }
+
+ this.getID = function addOptGroup_getID()
+ {
+ return "test optgroup's insertion into a select";
+ }
+ }
+
+ function removeOptGroup(aID)
+ {
+ this.selectNode = getNode(aID);
+ this.select = getAccessible(this.selectNode);
+ this.selectList = this.select.firstChild;
+
+ this.invoke = function removeOptGroup_invoke()
+ {
+ this.option1Node = this.selectNode.firstChild.firstChild;
+ this.selectNode.removeChild(this.selectNode.firstChild);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.selectList)
+ ];
+
+ this.finalCheck = function removeOptGroup_finalCheck()
+ {
+ var tree =
+ { COMBOBOX: [
+ { COMBOBOX_LIST: [
+ { COMBOBOX_OPTION: [] }
+ ] }
+ ] };
+ testAccessibleTree(this.select, tree);
+ is(isAccessible(this.option1Node), false, "removed option shouldn't be accessible anymore!");
+ }
+
+ this.getID = function removeOptGroup_getID()
+ {
+ return "test optgroup's removal from a select";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new addOptGroup("select"));
+ gQueue.push(new removeOptGroup("select"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
+ title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
+ Bug 616452</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="select"></select>
+
+ <div id="debug"/>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_recreation.html b/accessible/tests/mochitest/treeupdate/test_recreation.html
new file mode 100644
index 000000000..7754eb703
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_recreation.html
@@ -0,0 +1,155 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test accessible recreation</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function recreateAccessible(aID, aWontBeAccessible)
+ {
+ this.node = getNode(aID);
+ this.accessible =
+ isAccessible(this.node) ? getAccessible(this.node) : null;
+
+ this.eventSeq = [ ];
+
+ if (this.accessible)
+ this.eventSeq.push(new invokerChecker(EVENT_HIDE,
+ this.accessible));
+
+ if (!aWontBeAccessible)
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW, getAccessible,
+ this.node));
+
+ this.eventSeq.push(new invokerChecker(EVENT_REORDER,
+ getContainerAccessible(this.node)));
+
+ if (this.accessible) {
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_SHOW, this.accessible)
+ ];
+ }
+ }
+
+ function changeAttr(aID, aAttr, aValue)
+ {
+ this.__proto__ = new recreateAccessible(aID);
+
+ this.invoke = function changeAttr_invoke()
+ {
+ this.node.setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function changeAttr_getID()
+ {
+ return "change " + aAttr + "attribute for " + aID;
+ }
+ }
+
+ function removeAttr(aID, aAttr)
+ {
+ this.__proto__ = new recreateAccessible(aID, true);
+
+ this.invoke = function remvoeAttr_invoke()
+ {
+ this.node.removeAttribute(aAttr);
+ }
+
+ this.getID = function remvoeAttr_getID()
+ {
+ return "remove " + aAttr + "attribute for " + aID;
+ }
+ }
+
+ function changeRole(aID, aHasAccessible)
+ {
+ this.__proto__ = new changeAttr(aID, "role", "button");
+ }
+
+ function removeRole(aID)
+ {
+ this.__proto__ = new removeAttr(aID, "role");
+ }
+
+ function changeHref(aID)
+ {
+ this.__proto__ = new changeAttr(aID, "href", "www");
+ }
+
+ function changeMultiselectable(aID)
+ {
+ this.__proto__ = new changeAttr(aID, "aria-multiselectable", "true");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ // make the accessible an inaccessible
+ gQueue.push(new changeRole("span"));
+
+ // make the inaccessible an accessible
+ gQueue.push(new removeRole("span"));
+
+ // recreate an accessible by role change
+ gQueue.push(new changeRole("div1"));
+
+ // recreate an accessible by href change
+ gQueue.push(new changeHref("anchor"));
+
+ // recreate an accessible by aria-multiselectable change
+ gQueue.push(new changeMultiselectable("div3"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <span id="span">span</span>
+ <div id="div1">div</div>
+ <a id="anchor">anchor</a>
+ <div id="div3" role="listbox">list</div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_select.html b/accessible/tests/mochitest/treeupdate/test_select.html
new file mode 100644
index 000000000..006618b80
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_select.html
@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Add select options test</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ function addOptions(aID)
+ {
+ this.selectNode = getNode(aID);
+ this.select = getAccessible(this.selectNode);
+ this.selectList = this.select.firstChild;
+
+ this.invoke = function addOptions_invoke()
+ {
+ for (i = 0; i < 2; i++) {
+ var opt = document.createElement("option");
+ opt.value = i;
+ opt.text = "Option: Value " + i;
+
+ this.selectNode.add(opt, null);
+ }
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.selectList)
+ ];
+
+ this.finalCheck = function addOptions_finalCheck()
+ {
+ var tree =
+ { COMBOBOX: [
+ { COMBOBOX_LIST: [
+ { COMBOBOX_OPTION: [
+ { TEXT_LEAF: [] }
+ ] },
+ { COMBOBOX_OPTION: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(this.select, tree);
+ }
+
+ this.getID = function addOptions_getID()
+ {
+ return "test elements insertion into a select";
+ }
+ }
+
+ function removeOptions(aID)
+ {
+ this.selectNode = getNode(aID);
+ this.select = getAccessible(this.selectNode);
+ this.selectList = this.select.firstChild;
+
+ this.invoke = function removeOptions_invoke()
+ {
+ while (this.selectNode.length)
+ this.selectNode.remove(0);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.selectList)
+ ];
+
+ this.finalCheck = function removeOptions_finalCheck()
+ {
+ var tree =
+ { COMBOBOX: [
+ { COMBOBOX_LIST: [] }
+ ] };
+ testAccessibleTree(this.select, tree);
+ }
+
+ this.getID = function removeptions_getID()
+ {
+ return "test elements removal from a select";
+ }
+ }
+
+ //gA11yEventDumpID = "debug";
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new addOptions("select"));
+ gQueue.push(new removeOptions("select"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
+ title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
+ Mozilla Bug 616452</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=616940"
+ title="Removed select option accessibles aren't removed until hide event is fired">
+ Mozilla Bug 616940</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="select"></select>
+
+ <div id="debug"/>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_shutdown.xul b/accessible/tests/mochitest/treeupdate/test_shutdown.xul
new file mode 100644
index 000000000..2e6f7a7b3
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_shutdown.xul
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible XUL tree hierarchy tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../treeview.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../role.js" />
+ <script type="application/javascript"
+ src="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function setXULTreeView(aTreeID, aTreeView)
+ {
+ this.treeNode = getNode(aTreeID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.treeNode)
+ ];
+
+ this.invoke = function loadXULTree_invoke()
+ {
+ this.treeNode.view = aTreeView;
+ };
+
+ this.getID = function loadXULTree_getID()
+ {
+ return "Load XUL tree " + prettyName(aTreeID);
+ };
+ }
+
+ function removeTree(aID)
+ {
+ this.tree = getAccessible(aID);
+ this.lastItem = null;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, document)
+ ];
+
+ this.invoke = function invoke()
+ {
+ this.lastItem = getAccessible(aID).lastChild;
+ this.lastCell = this.lastItem.lastChild;
+ getNode(aID).parentNode.removeChild(getNode(aID));
+ };
+
+ this.check = function check(aEvent)
+ {
+ testIsDefunct(this.tree, aID);
+ testIsDefunct(this.lastItem, "last item of " + aID);
+ if (this.lastCell) {
+ testIsDefunct(this.lastCell, "last item cell of " + aID);
+ }
+ };
+
+ this.getID = function getID()
+ {
+ return "Remove tree from DOM";
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ // gA11yEventDumpID = "debug";
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setXULTreeView("tree", new nsTreeTreeView()));
+ gQueue.push(new removeTree("tree"));
+
+ gQueue.push(new setXULTreeView("treetable", new nsTreeTreeView()));
+ gQueue.push(new removeTree("treetable"));
+
+ gQueue.invoke(); // Will call SimpleTest.finish()
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
+ title="Reorganize implementation of XUL tree accessibility">
+ Bug 503727
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col" flex="1" primary="true" label="column"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+
+ <tree id="treetable" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ </vbox>
+ </hbox>
+
+</window>
diff --git a/accessible/tests/mochitest/treeupdate/test_table.html b/accessible/tests/mochitest/treeupdate/test_table.html
new file mode 100644
index 000000000..abadefdb0
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_table.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Table update tests</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ function appendCaption(aTableID)
+ {
+ this.invoke = function appendCaption_invoke()
+ {
+ // append a caption, it should appear as a first element in the
+ // accessible tree.
+ var caption = document.createElement("caption");
+ caption.textContent = "table caption";
+ getNode(aTableID).appendChild(caption);
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, aTableID)
+ ];
+
+ this.finalCheck = function appendCaption_finalCheck()
+ {
+ var tree =
+ { TABLE: [
+ { CAPTION: [
+ { TEXT_LEAF: [] }
+ ] },
+ { ROW: [
+ { CELL: [ {TEXT_LEAF: [] }]},
+ { CELL: [ {TEXT_LEAF: [] }]}
+ ] }
+ ] };
+ testAccessibleTree(aTableID, tree);
+ }
+
+ this.getID = function appendCaption_getID()
+ {
+ return "append caption";
+ }
+ }
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new appendCaption("table"));
+ gQueue.invoke(); // Will call SimpleTest.finish();
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <table id="table">
+ <tr>
+ <td>cell1</td>
+ <td>cell2</td>
+ </tr>
+ </table>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_textleaf.html b/accessible/tests/mochitest/treeupdate/test_textleaf.html
new file mode 100644
index 000000000..16d3a1a2b
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Test accessible recreation</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function textLeafUpdate(aID, aIsTextLeafLinkable)
+ {
+ this.node = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.node.parentNode)
+ ];
+
+ this.finalCheck = function textLeafUpdate_finalCheck()
+ {
+ var textLeaf = getAccessible(this.node).firstChild;
+ is(textLeaf.actionCount, (aIsTextLeafLinkable ? 1 : 0),
+ "Wrong action numbers!");
+ }
+ }
+
+ function setOnClickAttr(aID)
+ {
+ var node = getNode(aID);
+ node.setAttribute("onclick", "alert(3);");
+ var textLeaf = getAccessible(node).firstChild;
+ is(textLeaf.actionCount, 1, "setOnClickAttr: wrong action numbers!");
+ }
+
+ function removeOnClickAttr(aID)
+ {
+ var node = getNode(aID);
+ node.removeAttribute("onclick");
+ var textLeaf = getAccessible(node).firstChild;
+ is(textLeaf.actionCount, 0,
+ "removeOnClickAttr: wrong action numbers!");
+ }
+
+ function setOnClickNRoleAttrs(aID)
+ {
+ this.__proto__ = new textLeafUpdate(aID, true);
+
+ this.invoke = function setOnClickAttr_invoke()
+ {
+ this.node.setAttribute("role", "link");
+ this.node.setAttribute("onclick", "alert(3);");
+ }
+
+ this.getID = function setOnClickAttr_getID()
+ {
+ return "make " + prettyName(aID) + " linkable";
+ }
+ }
+
+ function removeTextData(aID, aRole)
+ {
+ this.containerNode = getNode(aID);
+ this.textNode = this.containerNode.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function removeTextData_invoke()
+ {
+ var tree = {
+ role: aRole,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: "text"
+ }
+ ]
+ };
+ testAccessibleTree(this.containerNode, tree);
+
+ this.textNode.data = "";
+ }
+
+ this.finalCheck = function removeTextData_finalCheck()
+ {
+ var tree = {
+ role: aRole,
+ children: []
+ };
+ testAccessibleTree(this.containerNode, tree);
+ }
+
+ this.getID = function removeTextData_finalCheck()
+ {
+ return "remove text data of text node inside '" + aID + "'.";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ // adds onclick on element, text leaf should inherit its action
+ setOnClickAttr("div");
+ // remove onclick attribute, text leaf shouldn't have any action
+ removeOnClickAttr("div");
+
+ // Call rest of event tests.
+ gQueue = new eventQueue();
+
+ // set onclick attribute making span accessible, it's inserted into tree
+ // and adopts text leaf accessible, text leaf should have an action
+ gQueue.push(new setOnClickNRoleAttrs("span"));
+
+ // text data removal of text node should remove its text accessible
+ gQueue.push(new removeTextData("p", ROLE_PARAGRAPH));
+ gQueue.push(new removeTextData("pre", ROLE_TEXT_CONTAINER));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Clean up the code of accessible initialization and binding to the tree"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465">
+ Mozilla Bug 545465
+ </a>
+ <a target="_blank"
+ title="Make sure accessible tree is correct when rendered text is changed"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652">
+ Mozilla Bug 625652
+ </a>
+ <a target="_blank"
+ title="Remove text accesible getting no text inside a preformatted area"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=706335">
+ Mozilla Bug 706335
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container">
+ <div id="div">div</div>
+ <span id="span">span</span>
+ </div>
+
+ <p id="p">text</p>
+ <pre id="pre">text</pre>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_visibility.html b/accessible/tests/mochitest/treeupdate/test_visibility.html
new file mode 100644
index 000000000..a1c130fb6
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_visibility.html
@@ -0,0 +1,437 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Style visibility tree update test</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Hide parent while child stays visible.
+ */
+ function test1(aContainerID, aParentID, aChildID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode(aParentID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [
+ { SECTION: [
+ { SECTION: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aParentID).style.visibility = "hidden";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { SECTION: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "hide parent while child stays visible";
+ }
+ }
+
+ /**
+ * Hide grand parent while its children stay visible.
+ */
+ function test2(aContainerID, aGrandParentID, aChildID, aChild2ID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode(aGrandParentID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // grand parent
+ { SECTION: [
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] },
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aGrandParentID).style.visibility = "hidden";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] },
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "hide grand parent while its children stay visible";
+ }
+ }
+
+ /**
+ * Change container style, hide parents while their children stay visible.
+ */
+ function test3(aContainerID, aParentID, aParent2ID, aChildID, aChild2ID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode(aParentID)),
+ new invokerChecker(EVENT_HIDE, getNode(aParent2ID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // parent
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] }
+ ] },
+ { SECTION: [ // parent2
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] },
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aContainerID).style.color = "red";
+ getNode(aParentID).style.visibility = "hidden";
+ getNode(aParent2ID).style.visibility = "hidden";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] },
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "change container style, hide parents while their children stay visible";
+ }
+ }
+
+ /**
+ * Change container style and make visible child inside the table.
+ */
+ function test4(aContainerID, aChildID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_REORDER, getNode(aChildID).parentNode)
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [
+ { TABLE: [
+ { ROW: [
+ { CELL: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aContainerID).style.color = "red";
+ getNode(aChildID).style.visibility = "visible";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { TABLE: [
+ { ROW: [
+ { CELL: [
+ { SECTION: [
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "change container style, make visible child insdie the table";
+ }
+ }
+
+ /**
+ * Hide subcontainer while child inside the table stays visible.
+ */
+ function test5(aContainerID, aSubContainerID, aChildID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // subcontainer
+ { TABLE: [
+ { ROW: [
+ { CELL: [
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+
+ getNode(aSubContainerID).style.visibility = "hidden";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "hide subcontainer while child inside the table stays visible";
+ }
+ }
+
+ /**
+ * Hide subcontainer while its child and child inside the nested table stays visible.
+ */
+ function test6(aContainerID, aSubContainerID, aChildID, aChild2ID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChildID)),
+ new invokerChecker(EVENT_SHOW, getNode(aChild2ID)),
+ new invokerChecker(EVENT_REORDER, getNode(aContainerID))
+ ];
+
+ this.invoke = function invoke()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // subcontainer
+ { TABLE: [
+ { ROW: [
+ { CELL: [
+ { TABLE: [ // nested table
+ { ROW: [
+ { CELL: [
+ { SECTION: [ // child
+ { TEXT_LEAF: [] } ]} ]} ]} ]} ]} ]} ]},
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] } ]} ]} ]};
+
+ testAccessibleTree(aContainerID, tree);
+
+ // invoke
+ getNode(aSubContainerID).style.visibility = "hidden";
+ }
+
+ this.finalCheck = function finalCheck()
+ {
+ var tree =
+ { SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] } ]},
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] } ]} ]};
+
+ testAccessibleTree(aContainerID, tree);
+ }
+
+ this.getID = function getID()
+ {
+ return "hide subcontainer while its child and child inside the nested table stays visible";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new test1("t1_container", "t1_parent", "t1_child"));
+ gQueue.push(new test2("t2_container", "t2_grandparent", "t2_child", "t2_child2"));
+ gQueue.push(new test3("t3_container", "t3_parent", "t3_parent2", "t3_child", "t3_child2"));
+ gQueue.push(new test4("t4_container", "t4_child"));
+ gQueue.push(new test5("t5_container", "t5_subcontainer", "t5_child"));
+ gQueue.push(new test6("t6_container", "t6_subcontainer", "t6_child", "t6_child2"));
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Develop a way to handle visibility style"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125">
+ Mozilla Bug 606125
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <!-- hide parent while child stays visible -->
+ <div id="t1_container">
+ <div id="t1_parent">
+ <div id="t1_child" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- hide grandparent while its children stay visible -->
+ <div id="t2_container">
+ <div id="t2_grandparent">
+ <div>
+ <div id="t2_child" style="visibility: visible">text</div>
+ <div id="t2_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- change container style, hide parents while their children stay visible -->
+ <div id="t3_container">
+ <div id="t3_parent">
+ <div id="t3_child" style="visibility: visible">text</div>
+ </div>
+ <div id="t3_parent2">
+ <div id="t3_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- change container style, show child inside the table -->
+ <div id="t4_container">
+ <table>
+ <tr>
+ <td>
+ <div id="t4_child" style="visibility: hidden;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <!-- hide subcontainer while child inside the table stays visible -->
+ <div id="t5_container">
+ <div id="t5_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <div id="t5_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <!-- hide subcontainer while its child and child inside the nested table stays visible -->
+ <div id="t6_container">
+ <div id="t6_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <table>
+ <tr>
+ <td>
+ <div id="t6_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <div id="t6_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeupdate/test_whitespace.html b/accessible/tests/mochitest/treeupdate/test_whitespace.html
new file mode 100644
index 000000000..e7ba9b059
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_whitespace.html
@@ -0,0 +1,187 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>Whitespace text accessible creation/desctruction</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Middle image accessible removal results in text accessible removal.
+ *
+ * Before:
+ * DOM: whitespace img1 whitespace img2 whitespace img3 whitespace,
+ * a11y: img1 whitespace img2 whitespace img3
+ * After:
+ * DOM: whitespace img1 whitespace whitespace img3 whitespace,
+ * a11y: img1 whitespace img3
+ */
+ function removeImg()
+ {
+ this.containerNode = getNode("container1");
+ this.imgNode = getNode("img1");
+ this.img = getAccessible(this.imgNode);
+ this.text = this.img.nextSibling;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.img),
+ new invokerChecker(EVENT_HIDE, this.text),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.finalCheck = function textLeafUpdate_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] }
+ ] };
+
+ testAccessibleTree(this.containerNode, tree);
+ }
+
+ this.invoke = function setOnClickAttr_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] }
+ ] };
+
+ testAccessibleTree(this.containerNode, tree);
+
+ this.containerNode.removeChild(this.imgNode);
+ }
+
+ this.getID = function setOnClickAttr_getID()
+ {
+ return "remove middle img";
+ }
+ }
+
+ /**
+ * Append image making the whitespace visible and thus accessible.
+ * Note: images and whitespaces are on different leves of accessible trees,
+ * so that image container accessible update doesn't update the tree
+ * of whitespace container.
+ *
+ * Before:
+ * DOM: whitespace emptylink whitespace linkwithimg whitespace
+ * a11y: emptylink linkwithimg
+ * After:
+ * DOM: whitespace linkwithimg whitespace linkwithimg whitespace
+ * a11y: linkwithimg whitespace linkwithimg
+ */
+ function insertImg()
+ {
+ this.containerNode = getNode("container2");
+ this.topNode = this.containerNode.parentNode;
+ this.textNode = this.containerNode.nextSibling;
+ this.imgNode = document.createElement("img");
+ this.imgNode.setAttribute("src", "../moz.png");
+
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.textNode),
+ new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.imgNode),
+ new orderChecker(),
+ new invokerChecker(EVENT_REORDER, this.topNode)
+ ];
+
+ this.invoke = function insertImg_invoke()
+ {
+ var tree =
+ { SECTION: [
+ { LINK: [] },
+ { LINK: [
+ { GRAPHIC: [] }
+ ] }
+ ] };
+
+ testAccessibleTree(this.topNode, tree);
+
+ this.containerNode.appendChild(this.imgNode);
+ }
+
+ this.finalCheck = function insertImg_finalCheck()
+ {
+ var tree =
+ { SECTION: [
+ { LINK: [
+ { GRAPHIC: [ ] }
+ ] },
+ { TEXT_LEAF: [ ] },
+ { LINK: [
+ { GRAPHIC: [ ] }
+ ] }
+ ] };
+
+ testAccessibleTree(this.topNode, tree);
+ }
+
+ this.getID = function appendImg_getID()
+ {
+ return "insert img into internal container";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new removeImg());
+ gQueue.push(new insertImg());
+
+ gQueue.invoke(); // SimpleTest.finish() will be called in the end
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+
+ <a target="_blank"
+ title="Make sure accessible tree is correct when rendered text is changed"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652">
+ Mozilla Bug 625652
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container1"> <img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"> </div>
+ <div> <a id="container2"></a> <a><img src="../moz.png"></a> </div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/treeview.css b/accessible/tests/mochitest/treeview.css
new file mode 100644
index 000000000..1bffb1798
--- /dev/null
+++ b/accessible/tests/mochitest/treeview.css
@@ -0,0 +1,15 @@
+treechildren::-moz-tree-checkbox(checked) {
+ list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+treechildren::-moz-tree-image(cyclerState1) {
+ list-style-image: url("chrome://global/skin/console/bullet-question.png");
+}
+
+treechildren::-moz-tree-image(cyclerState2) {
+ list-style-image: url("chrome://global/skin/console/bullet-warning.png");
+}
+
+treechildren::-moz-tree-image(cyclerState3) {
+ list-style-image: url("chrome://global/skin/console/bullet-error.png");
+}
diff --git a/accessible/tests/mochitest/treeview.js b/accessible/tests/mochitest/treeview.js
new file mode 100644
index 000000000..869471c85
--- /dev/null
+++ b/accessible/tests/mochitest/treeview.js
@@ -0,0 +1,289 @@
+/**
+ * Helper method to start a single XUL tree test.
+ */
+function loadXULTreeAndDoTest(aDoTestFunc, aTreeID, aTreeView)
+{
+ var doTestFunc = aDoTestFunc ? aDoTestFunc : gXULTreeLoadContext.doTestFunc;
+ var treeID = aTreeID ? aTreeID : gXULTreeLoadContext.treeID;
+ var treeView = aTreeView ? aTreeView : gXULTreeLoadContext.treeView;
+
+ function loadXULTree(aTreeID, aTreeView)
+ {
+ this.treeNode = getNode(aTreeID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.treeNode)
+ ];
+
+ this.invoke = function loadXULTree_invoke()
+ {
+ this.treeNode.view = aTreeView;
+ }
+
+ this.getID = function loadXULTree_getID()
+ {
+ return "Load XUL tree " + prettyName(aTreeID);
+ }
+ }
+
+ gXULTreeLoadContext.queue = new eventQueue();
+ gXULTreeLoadContext.queue.push(new loadXULTree(treeID, treeView));
+ gXULTreeLoadContext.queue.onFinish = function()
+ {
+ SimpleTest.executeSoon(doTestFunc);
+ return DO_NOT_FINISH_TEST;
+ }
+ gXULTreeLoadContext.queue.invoke();
+}
+
+/**
+ * Analogy of addA11yLoadEvent, nice helper to load XUL tree and start the test.
+ */
+function addA11yXULTreeLoadEvent(aDoTestFunc, aTreeID, aTreeView)
+{
+ gXULTreeLoadContext.doTestFunc = aDoTestFunc;
+ gXULTreeLoadContext.treeID = aTreeID;
+ gXULTreeLoadContext.treeView = aTreeView;
+
+ addA11yLoadEvent(loadXULTreeAndDoTest);
+}
+
+
+function nsTableTreeView(aRowCount)
+{
+ this.__proto__ = new nsTreeView();
+
+ for (var idx = 0; idx < aRowCount; idx++)
+ this.mData.push(new treeItem("row" + String(idx) + "_"));
+}
+
+function nsTreeTreeView()
+{
+ this.__proto__ = new nsTreeView();
+
+ this.mData = [
+ new treeItem("row1"),
+ new treeItem("row2_", true, [new treeItem("row2.1_"), new treeItem("row2.2_")]),
+ new treeItem("row3_", false, [new treeItem("row3.1_"), new treeItem("row3.2_")]),
+ new treeItem("row4")
+ ];
+}
+
+function nsTreeView()
+{
+ this.mTree = null;
+ this.mData = [];
+}
+
+nsTreeView.prototype =
+{
+ //////////////////////////////////////////////////////////////////////////////
+ // nsITreeView implementation
+
+ get rowCount()
+ {
+ return this.getRowCountIntl(this.mData);
+ },
+ setTree: function setTree(aTree)
+ {
+ this.mTree = aTree;
+ },
+ getCellText: function getCellText(aRow, aCol)
+ {
+ var data = this.getDataForIndex(aRow);
+ if (aCol.id in data.colsText)
+ return data.colsText[aCol.id];
+
+ return data.text + aCol.id;
+ },
+ getCellValue: function getCellValue(aRow, aCol)
+ {
+ var data = this.getDataForIndex(aRow);
+ return data.value;
+ },
+ getRowProperties: function getRowProperties(aIndex) { return ""; },
+ getCellProperties: function getCellProperties(aIndex, aCol)
+ {
+ if (!aCol.cycler)
+ return "";
+
+ var data = this.getDataForIndex(aIndex);
+ return this.mCyclerStates[data.cyclerState];
+ },
+ getColumnProperties: function getColumnProperties(aCol) { return ""; },
+ getParentIndex: function getParentIndex(aRowIndex)
+ {
+ var info = this.getInfoByIndex(aRowIndex);
+ return info.parentIndex;
+ },
+ hasNextSibling: function hasNextSibling(aRowIndex, aAfterIndex) { },
+ getLevel: function getLevel(aIndex)
+ {
+ var info = this.getInfoByIndex(aIndex);
+ return info.level;
+ },
+ getImageSrc: function getImageSrc(aRow, aCol) {},
+ getProgressMode: function getProgressMode(aRow, aCol) {},
+ isContainer: function isContainer(aIndex)
+ {
+ var data = this.getDataForIndex(aIndex);
+ return data.open != undefined;
+ },
+ isContainerOpen: function isContainerOpen(aIndex)
+ {
+ var data = this.getDataForIndex(aIndex);
+ return data.open;
+ },
+ isContainerEmpty: function isContainerEmpty(aIndex)
+ {
+ var data = this.getDataForIndex(aIndex);
+ return data.open == undefined;
+ },
+ isSeparator: function isSeparator(aIndex) {},
+ isSorted: function isSorted() {},
+ toggleOpenState: function toggleOpenState(aIndex)
+ {
+ var data = this.getDataForIndex(aIndex);
+
+ data.open = !data.open;
+ var rowCount = this.getRowCountIntl(data.children);
+
+ if (data.open)
+ this.mTree.rowCountChanged(aIndex + 1, rowCount);
+ else
+ this.mTree.rowCountChanged(aIndex + 1, -rowCount);
+ },
+ selectionChanged: function selectionChanged() {},
+ cycleHeader: function cycleHeader(aCol) {},
+ cycleCell: function cycleCell(aRow, aCol)
+ {
+ var data = this.getDataForIndex(aRow);
+ data.cyclerState = (data.cyclerState + 1) % 3;
+
+ this.mTree.invalidateCell(aRow, aCol);
+ },
+ isEditable: function isEditable(aRow, aCol)
+ {
+ return true;
+ },
+ isSelectable: function isSelectable(aRow, aCol) {},
+ setCellText: function setCellText(aRow, aCol, aValue)
+ {
+ var data = this.getDataForIndex(aRow);
+ data.colsText[aCol.id] = aValue;
+ },
+ setCellValue: function setCellValue(aRow, aCol, aValue)
+ {
+ var data = this.getDataForIndex(aRow);
+ data.value = aValue;
+
+ this.mTree.invalidateCell(aRow, aCol);
+ },
+ performAction: function performAction(aAction) {},
+ performActionOnRow: function performActionOnRow(aAction, aRow) {},
+ performActionOnCell: function performActionOnCell(aAction, aRow, aCol) {},
+
+ //////////////////////////////////////////////////////////////////////////////
+ // public implementation
+
+ appendItem: function appendItem(aText)
+ {
+ this.mData.push(new treeItem(aText));
+ },
+
+ //////////////////////////////////////////////////////////////////////////////
+ // private implementation
+
+ getDataForIndex: function getDataForIndex(aRowIndex)
+ {
+ return this.getInfoByIndex(aRowIndex).data;
+ },
+
+ getInfoByIndex: function getInfoByIndex(aRowIndex)
+ {
+ var info = {
+ data: null,
+ parentIndex: -1,
+ level: 0,
+ index: -1
+ };
+
+ this.getInfoByIndexIntl(aRowIndex, info, this.mData, 0);
+ return info;
+ },
+
+ getRowCountIntl: function getRowCountIntl(aChildren)
+ {
+ var rowCount = 0;
+ for (var childIdx = 0; childIdx < aChildren.length; childIdx++) {
+ rowCount++;
+
+ var data = aChildren[childIdx];
+ if (data.open)
+ rowCount += this.getRowCountIntl(data.children);
+ }
+
+ return rowCount;
+ },
+
+ getInfoByIndexIntl: function getInfoByIndexIntl(aRowIdx, aInfo,
+ aChildren, aLevel)
+ {
+ var rowIdx = aRowIdx;
+ for (var childIdx = 0; childIdx < aChildren.length; childIdx++) {
+ var data = aChildren[childIdx];
+
+ aInfo.index++;
+
+ if (rowIdx == 0) {
+ aInfo.data = data;
+ aInfo.level = aLevel;
+ return -1;
+ }
+
+ if (data.open) {
+ var parentIdx = aInfo.index;
+ rowIdx = this.getInfoByIndexIntl(rowIdx - 1, aInfo, data.children,
+ aLevel + 1);
+
+ if (rowIdx == -1) {
+ if (aInfo.parentIndex == -1)
+ aInfo.parentIndex = parentIdx;
+ return 0;
+ }
+ } else {
+ rowIdx--;
+ }
+ }
+
+ return rowIdx;
+ },
+
+ mCyclerStates: [
+ "cyclerState1",
+ "cyclerState2",
+ "cyclerState3"
+ ]
+};
+
+function treeItem(aText, aOpen, aChildren)
+{
+ this.text = aText;
+ this.colsText = {};
+ this.open = aOpen;
+ this.value = "true";
+ this.cyclerState = 0;
+ if (aChildren)
+ this.children = aChildren;
+}
+
+/**
+ * Used in conjunction with loadXULTreeAndDoTest and addA11yXULTreeLoadEvent.
+ */
+var gXULTreeLoadContext =
+{
+ doTestFunc: null,
+ treeID: null,
+ treeView: null,
+ queue: null
+};
diff --git a/accessible/tests/mochitest/value.js b/accessible/tests/mochitest/value.js
new file mode 100644
index 000000000..cc9dffe00
--- /dev/null
+++ b/accessible/tests/mochitest/value.js
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////
+// Public methods
+
+/**
+ * Tests nsIAccessibleValue interface.
+ *
+ * @param aAccOrElmOrId [in] identifier of accessible
+ * @param aValue [in] accessible value (nsIAccessible::value)
+ * @param aCurrValue [in] current value (nsIAccessibleValue::currentValue)
+ * @param aMinValue [in] minimum value (nsIAccessibleValue::minimumValue)
+ * @param aMaxValue [in] maximumn value (nsIAccessibleValue::maximumValue)
+ * @param aMinIncr [in] minimum increment value
+ * (nsIAccessibleValue::minimumIncrement)
+ */
+function testValue(aAccOrElmOrId, aValue, aCurrValue,
+ aMinValue, aMaxValue, aMinIncr)
+{
+ var acc = getAccessible(aAccOrElmOrId, [nsIAccessibleValue]);
+ if (!acc)
+ return;
+
+ is(acc.value, aValue, "Wrong value of " + prettyName(aAccOrElmOrId));
+
+ is(acc.currentValue, aCurrValue,
+ "Wrong current value of " + prettyName(aAccOrElmOrId));
+ is(acc.minimumValue, aMinValue,
+ "Wrong minimum value of " + prettyName(aAccOrElmOrId));
+ is(acc.maximumValue, aMaxValue,
+ "Wrong maximum value of " + prettyName(aAccOrElmOrId));
+ is(acc.minimumIncrement, aMinIncr,
+ "Wrong minimum increment value of " + prettyName(aAccOrElmOrId));
+}
diff --git a/accessible/tests/mochitest/value/a11y.ini b/accessible/tests/mochitest/value/a11y.ini
new file mode 100644
index 000000000..b93d9b180
--- /dev/null
+++ b/accessible/tests/mochitest/value/a11y.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/mochitest/*.js
+
+[test_general.html]
+[test_number.html]
+[test_progress.html]
+[test_progress.xul]
+[test_range.html]
diff --git a/accessible/tests/mochitest/value/test_general.html b/accessible/tests/mochitest/value/test_general.html
new file mode 100644
index 000000000..12e718ba1
--- /dev/null
+++ b/accessible/tests/mochitest/value/test_general.html
@@ -0,0 +1,159 @@
+<html>
+
+<head>
+ <title>nsIAccessible value testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ .offscreen {
+ position: absolute;
+ left: -5000px;
+ top: -5000px;
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ function testValue(aID, aValue)
+ {
+ var acc = getAccessible(aID);
+ if (!acc)
+ return;
+ is(acc.value, aValue, "Wrong value for " + aID + "!");
+ }
+
+ var rootDir = getRootDirectory(window.location.href);
+ var href = getRootDirectory(window.location.href) + "foo";
+
+ // roles that can't live as HTMLLinkAccessibles
+ testValue("aria_menuitem_link", "");
+ testValue("aria_button_link", "");
+ testValue("aria_checkbox_link", "");
+ testValue("aria_application_link", "");
+
+ // roles that can live as HTMLLinkAccessibles
+ testValue("aria_link_link", href);
+ testValue("aria_main_link", href);
+ testValue("aria_navigation_link", href);
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA textboxes
+
+ testValue("aria_textbox1", "helo");
+
+ //////////////////////////////////////////////////////////////////////////
+ // ARIA comboboxes
+
+ // aria-activedescendant defines a current item the value is computed from
+ testValue("aria_combobox1", kDiscBulletText + "Zoom");
+
+ // aria-selected defines a selected item the value is computed from,
+ // list control is pointed by aria-owns relation.
+ testValue("aria_combobox2", kDiscBulletText + "Zoom");
+
+ // aria-selected defines a selected item the value is computed from,
+ // list control is a child of combobox.
+ testValue("aria_combobox3", kDiscBulletText + "2");
+
+ //////////////////////////////////////////////////////////////////////////
+ // HTML controls
+ testValue("combobox1", "item1");
+ testValue("combobox2", "item2");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=494807"
+ title="Do not expose a11y info specific to hyperlinks when role is overridden using ARIA">
+ Bug 494807
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=819273"
+ title="ARIA combobox should have accessible value">
+ Bug 819273
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=887250"
+ title="ARIA textbox role doesn't expose value">
+ Bug 887250
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+
+ <a id="aria_menuitem_link" role="menuitem" href="foo">menuitem</a>
+ <a id="aria_button_link" role="button" href="foo">button</a>
+ <a id="aria_checkbox_link" role="checkbox" href="foo">checkbox</a>
+
+ <!-- landmark links -->
+ <a id="aria_application_link" role="application" href="foo">app</a>
+ <a id="aria_main_link" role="main" href="foo">main</a>
+ <a id="aria_navigation_link" role="navigation" href="foo">nav</a>
+
+ <!-- strange edge case: please don't do this in the wild -->
+ <a id="aria_link_link" role="link" href="foo">link</a>
+
+ <div id="aria_textbox1" role="textbox">helo</div>
+
+ <div id="aria_combobox1" role="combobox"
+ aria-owns="aria_combobox1_owned_listbox"
+ aria-activedescendant="aria_combobox1_selected_option">
+ </div>
+ <ul role="listbox" id="aria_combobox1_owned_listbox">
+ <li role="option">Zebra</li>
+ <li role="option" id="aria_combobox1_selected_option">Zoom</li>
+ </ul>
+
+ <div id="aria_combobox2" role="combobox"
+ aria-owns="aria_combobox2_owned_listbox">
+ </div>
+ <ul role="listbox" id="aria_combobox2_owned_listbox">
+ <li role="option">Zebra</li>
+ <li role="option" aria-selected="true">Zoom</li>
+ </ul>
+
+ <div id="aria_combobox3" role="combobox">
+ <div role="textbox"></div>
+ <ul role="listbox">
+ <li role="option">1</li>
+ <li role="option" aria-selected="true">2</li>
+ <li role="option">3</li>
+ </ul>
+ </div>
+
+ <select id="combobox1">
+ <option id="cb1_item1">item1</option>
+ <option id="cb1_item2">item2</option>
+ </select>
+ <select id="combobox2">
+ <option id="cb2_item1">item1</option>
+ <option id="cb2_item2" selected="true">item2</option>
+ </select>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/value/test_number.html b/accessible/tests/mochitest/value/test_number.html
new file mode 100644
index 000000000..68825b445
--- /dev/null
+++ b/accessible/tests/mochitest/value/test_number.html
@@ -0,0 +1,59 @@
+<html>
+
+<head>
+ <title>nsIAccessible value testing for input@type=range element</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../value.js"></script>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // HTML5 number element tests
+ testValue("number", "", 0, 0, 0, 1);
+ testValue("number_value", "1", 1, 0, 0, 1);
+ testValue("number_step", "", 0, 0, 0, 1);
+ testValue("number_min42", "", 0, 42, 0, 1);
+ testValue("number_max42", "", 0, 0, 42, 1);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559761"
+ title="make HTML5 input@type=number element accessible">
+ Bug 559761
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+
+ <!-- HTML5 input@type=number element -->
+ <input type="number" id="number">
+ <input type="number" id="number_value" value="1">
+ <input type="number" id="number_step" step="1">
+ <input type="number" id="number_min42" min="42">
+ <input type="number" id="number_max42" max="42">
+</body>
+</html>
diff --git a/accessible/tests/mochitest/value/test_progress.html b/accessible/tests/mochitest/value/test_progress.html
new file mode 100644
index 000000000..b99cce14c
--- /dev/null
+++ b/accessible/tests/mochitest/value/test_progress.html
@@ -0,0 +1,61 @@
+<html>
+
+<head>
+ <title>nsIAccessible value testing for progress element</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../value.js"></script>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // HTML5 progress element tests
+ testValue("pr_indeterminate", "", 0, 0, 1, 0);
+ testValue("pr_zero", "0%", 0, 0, 1, 0);
+ testValue("pr_zeropointfive", "50%", 0.5, 0, 1, 0);
+ testValue("pr_one", "100%", 1, 0, 1, 0);
+ testValue("pr_42", "100%", 42, 0, 1, 0);
+ testValue("pr_21", "50%", 21, 0, 42, 0);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559773"
+ title="make HTML5 progress element accessible">
+ Mozilla Bug 559773
+ </a><br />
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+
+ <!-- HTML5 progress element -->
+ <progress id="pr_indeterminate">this will be read by legacy browsers</progress>
+ <progress id="pr_zero" value="0">this will be read by legacy browsers</progress>
+ <progress id="pr_zeropointfive" value="0.5">this will be read by legacy browsers</progress>
+ <progress id="pr_one" value="1">this will be read by legacy browsers</progress>
+ <progress id="pr_42" value="42">this will be read by legacy browsers</progress>
+ <progress id="pr_21" value="21" max="42">this will be read by legacy browsers</progress>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/value/test_progress.xul b/accessible/tests/mochitest/value/test_progress.xul
new file mode 100644
index 000000000..5ae4a358f
--- /dev/null
+++ b/accessible/tests/mochitest/value/test_progress.xul
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="XUL progressmeter tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../value.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ function doTest()
+ {
+ // progressmeter
+ testValue("pm1", "50%", 50, 0, 100, 0);
+ testValue("pm2", "50%", 500, 0, 1000, 0);
+ testValue("pm3", "", 0, 0, 100, 0);
+
+ // scale
+ testValue("sc1", "500", 500, 0, 1000, 10);
+ testValue("sc2", "", 0, 0, 0, 0);
+
+ // aria progressbar
+ testValue("ariapb1", "500", 500, 0, 1000, 0);
+ testValue("ariapb2", "", 0, 0, 0, 0);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ ]]>
+ </script>
+
+ <hbox flex="1" style="overflow: auto;">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=489551"
+ title="Values of sliders and progress bars in HTML 5 audio and video element's control sets are not percentages">
+ Mozilla Bug 489551
+ </a><br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <!-- progressmeter -->
+ <progressmeter id="pm1" value="50"/>
+ <progressmeter id="pm2" value="500" max="1000"/>
+ <progressmeter id="pm3"/>
+
+ <!-- scale -->
+ <scale id="sc1" value="500" max="1000" increment="10"/>
+ <scale id="sc2"/>
+
+ <!-- aria -->
+ <description id="ariapb1" role="progressbar"
+ aria-valuenow="500" aria-valuemin="0" aria-valuemax="1000"/>
+ <description id="ariapb2" role="progressbar"/>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/value/test_range.html b/accessible/tests/mochitest/value/test_range.html
new file mode 100644
index 000000000..b795f7a79
--- /dev/null
+++ b/accessible/tests/mochitest/value/test_range.html
@@ -0,0 +1,59 @@
+<html>
+
+<head>
+ <title>nsIAccessible value testing for input@type=range element</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../value.js"></script>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <script type="application/javascript">
+ function doTest()
+ {
+ // HTML5 progress element tests
+ testValue("range", "50", 50, 0, 100, 1);
+ testValue("range_value", "1", 1, 0, 100, 1);
+ testValue("range_step", "50", 50, 0, 100, 1);
+ testValue("range_min42", "71", 71, 42, 100, 1);
+ testValue("range_max42", "21", 21, 0, 42, 1);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
+ title="make HTML5 input@type=range element accessible">
+ Bug 559764
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+
+ <!-- HTML5 input@type=range element -->
+ <input type="range" id="range">
+ <input type="range" id="range_value" value="1">
+ <input type="range" id="range_step" step="1">
+ <input type="range" id="range_min42" min="42">
+ <input type="range" id="range_max42" max="42">
+</body>
+</html>