68.14.8 - everything else

This commit is contained in:
Fedor 2025-04-19 19:21:07 +03:00
parent 95cbc1282f
commit 18be06a872
472 changed files with 11291 additions and 15900 deletions

View File

@ -60,7 +60,6 @@ xpcom/reflect/xptcall/md/unix/.*
browser/components/translation/cld2/.*
browser/extensions/mortar/ppapi/.*
devtools/client/shared/sourceeditor/codemirror/.*
devtools/client/shared/sourceeditor/tern/.*
dom/canvas/test/webgl-conf/checkout/closure-library/.*
dom/media/gmp/rlz/.*
dom/media/platforms/ffmpeg/ffmpeg57/.*

View File

@ -125,7 +125,6 @@ devtools/client/shared/source-map/
devtools/client/shared/vendor/
devtools/client/shared/sourceeditor/codemirror/*.js
devtools/client/shared/sourceeditor/codemirror/**/*.js
devtools/client/shared/sourceeditor/tern/
devtools/client/shared/sourceeditor/test/cm_mode_ruby.js
devtools/client/shared/sourceeditor/test/codemirror/
devtools/server/actors/utils/automation-timeline.js
@ -326,9 +325,6 @@ toolkit/components/reader/JSDOMParser.js
# Uses preprocessing
toolkit/components/reader/Readerable.jsm
# Should be going away soon
toolkit/content/widgets/wizard.xml
# Uses preprocessing
toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
toolkit/modules/AppConstants.jsm

View File

@ -135,6 +135,7 @@ module.exports = {
"no-array-constructor": "off",
"no-undef": "off",
"no-unused-vars": "off",
"no-useless-concat": "off",
"no-redeclare": "off",
"no-global-assign": "off",
}
@ -187,6 +188,8 @@ module.exports = {
"mozilla/reject-importGlobalProperties": "off",
"mozilla/no-arbitrary-setTimeout": "off",
"mozilla/no-define-cc-etc": "off",
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/use-chromeutils-generateqi": "off",
"mozilla/use-default-preference-values": "off",
"mozilla/use-includes-instead-of-indexOf": "off",
@ -209,6 +212,7 @@ module.exports = {
"no-restricted-globals": "off",
"no-return-await": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-throw-literal": "off",
"no-useless-concat": "off",
"no-undef": "off",
@ -259,10 +263,10 @@ module.exports = {
"dom/websocket/**",
"dom/workers/**",
"dom/worklet/**",
"dom/xbl/**",
"dom/xml/**",
"dom/xslt/**",
"dom/xul/**",
"dom/ipc/test.xhtml",
],
"rules": {
"consistent-return": "off",
@ -359,5 +363,451 @@ module.exports = {
"rules": {
"no-async-promise-executor": "off",
}
}, {
"files": [
"browser/base/content/test/chrome/test_aboutCrashed.xhtml",
"browser/base/content/test/chrome/test_aboutRestartRequired.xhtml",
"browser/base/content/test/general/browser_tab_dragdrop2_frame1.xhtml",
"browser/components/places/tests/chrome/test_0_bug510634.xhtml",
"browser/components/places/tests/chrome/test_bug1163447_selectItems_through_shortcut.xhtml",
"browser/components/places/tests/chrome/test_0_bug510634.xhtml",
"browser/components/places/tests/chrome/test_bug1163447_selectItems_through_shortcut.xhtml",
"browser/components/places/tests/chrome/test_bug549192.xhtml",
"browser/components/places/tests/chrome/test_bug549491.xhtml",
"browser/components/places/tests/chrome/test_selectItems_on_nested_tree.xhtml",
"browser/components/places/tests/chrome/test_treeview_date.xhtml",
],
"rules": {
"mozilla/no-arbitrary-setTimeout": "off",
"object-shorthand": "off",
"no-undef": "off",
"no-unused-vars": "off",
}
}, {
"files": [
"accessible/tests/mochitest/actions/test_keys_menu.xhtml",
"accessible/tests/mochitest/elm/test_listbox.xhtml",
"accessible/tests/mochitest/events/test_focus_autocomplete.xhtml",
"accessible/tests/mochitest/events/test_focus_contextmenu.xhtml",
"accessible/tests/mochitest/events/test_tree.xhtml",
"accessible/tests/mochitest/hittest/test_zoom_tree.xhtml",
"accessible/tests/mochitest/name/test_general.xhtml",
"accessible/tests/mochitest/name/test_tree.xhtml",
"accessible/tests/mochitest/selectable/test_listbox.xhtml",
"accessible/tests/mochitest/states/test_expandable.xhtml",
"accessible/tests/mochitest/tree/test_button.xhtml",
"accessible/tests/mochitest/tree/test_tree.xhtml",
"accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml",
"accessible/tests/mochitest/treeupdate/test_menu.xhtml",
],
"rules": {
"object-shorthand": "off",
"mozilla/no-compare-against-boolean-literals": "off",
"mozilla/use-cc-etc": "off",
"consistent-return": "off",
"no-redeclare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-unused-vars": "off",
"no-useless-call": "off",
}
}, {
"files": [
"testing/mochitest/browser-harness.xhtml",
"testing/mochitest/chrome/test_chromeGetTestFile.xhtml",
"testing/mochitest/chrome/test_sanityEventUtils.xhtml",
"testing/mochitest/chrome/test_sanityException.xhtml",
"testing/mochitest/chrome/test_sanityException2.xhtml",
"testing/mochitest/harness.xhtml",
],
"rules": {
"dot-notation": "off",
"object-shorthand": "off",
"mozilla/use-services": "off",
"mozilla/no-compare-against-boolean-literals": "off",
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/use-cc-etc": "off",
"consistent-return": "off",
"no-fallthrough": "off",
"no-nested-ternary": "off",
"no-redeclare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-throw-literal": "off",
"no-undef": "off",
"no-unsanitized/property": "off",
"no-unused-vars": "off",
"no-useless-call": "off",
}
}, {
"files": [
"docshell/test/chrome/bug113934_window.xhtml",
"docshell/test/chrome/bug215405_window.xhtml",
"docshell/test/chrome/bug293235_window.xhtml",
"docshell/test/chrome/bug294258_window.xhtml",
"docshell/test/chrome/bug298622_window.xhtml",
"docshell/test/chrome/bug301397_window.xhtml",
"docshell/test/chrome/bug303267_window.xhtml",
"docshell/test/chrome/bug311007_window.xhtml",
"docshell/test/chrome/bug321671_window.xhtml",
"docshell/test/chrome/bug360511_window.xhtml",
"docshell/test/chrome/bug396519_window.xhtml",
"docshell/test/chrome/bug396649_window.xhtml",
"docshell/test/chrome/bug449778_window.xhtml",
"docshell/test/chrome/bug449780_window.xhtml",
"docshell/test/chrome/bug582176_window.xhtml",
"docshell/test/chrome/bug662200_window.xhtml",
"docshell/test/chrome/bug690056_window.xhtml",
"docshell/test/chrome/bug89419_window.xhtml",
"docshell/test/chrome/mozFrameType_window.xhtml",
"docshell/test/chrome/test_bug453650.xhtml",
"docshell/test/chrome/test_bug454235.xhtml",
"docshell/test/chrome/test_bug565388.xhtml",
"docshell/test/chrome/test_bug608669.xhtml",
"docshell/test/chrome/test_bug789773.xhtml",
"docshell/test/chrome/test_bug846906.xhtml",
"docshell/test/chrome/test_docRedirect.xhtml",
"docshell/test/chrome/test_principalInherit.xhtml",
"docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml",
],
"rules": {
"dot-notation": "off",
"no-global-assign": "off",
"no-octal": "off",
"object-shorthand": "off",
"mozilla/consistent-if-bracing": "off",
"mozilla/no-compare-against-boolean-literals": "off",
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/use-cc-etc": "off",
"mozilla/use-services": "off",
"mozilla/use-chromeutils-generateqi": "off",
"consistent-return": "off",
"no-delete-var": "off",
"no-redeclare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-undef": "off",
"no-unused-vars": "off",
"no-useless-call": "off",
}
}, {
"files": [
"editor/composer/test/test_bug434998.xhtml",
"editor/libeditor/tests/test_bug607584.xhtml",
"editor/libeditor/tests/test_bug616590.xhtml",
"editor/libeditor/tests/test_bug780908.xhtml",
],
"rules": {
"object-shorthand": "off",
"no-undef": "off",
}
}, {
"files": [
"widget/tests/native_menus_window.xhtml",
"widget/tests/native_mouse_mac_window.xhtml",
"widget/tests/standalone_native_menu_window.xhtml",
"widget/tests/system_font_changes.xhtml",
"widget/tests/taskbar_previews.xhtml",
"widget/tests/test_bug1123480.xhtml",
"widget/tests/test_bug343416.xhtml",
"widget/tests/test_bug428405.xhtml",
"widget/tests/test_bug429954.xhtml",
"widget/tests/test_bug466599.xhtml",
"widget/tests/test_bug485118.xhtml",
"widget/tests/test_bug517396.xhtml",
"widget/tests/test_bug538242.xhtml",
"widget/tests/test_bug596600.xhtml",
"widget/tests/test_bug673301.xhtml",
"widget/tests/test_bug760802.xhtml",
"widget/tests/test_chrome_context_menus_win.xhtml",
"widget/tests/test_clipboard.xhtml",
"widget/tests/test_input_events_on_deactive_window.xhtml",
"widget/tests/test_key_event_counts.xhtml",
"widget/tests/test_keycodes.xhtml",
"widget/tests/test_panel_mouse_coords.xhtml",
"widget/tests/test_position_on_resize.xhtml",
"widget/tests/test_sizemode_events.xhtml",
"widget/tests/test_taskbar_progress.xhtml",
"widget/tests/test_transferable_overflow.xhtml",
"widget/tests/window_bug429954.xhtml",
"widget/tests/window_bug478536.xhtml",
"widget/tests/window_composition_text_querycontent.xhtml",
"widget/tests/window_state_windows.xhtml",
"widget/tests/window_wheeltransaction.xhtml",
],
"rules": {
"complexity": "off",
"consistent-return": "off",
"dot-notation": "off",
"mozilla/prefer-boolean-length-check": "off",
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/use-cc-etc": "off",
"mozilla/use-chromeutils-generateqi": "off",
"mozilla/use-services": "off",
"object-shorthand": "off",
"no-caller": "off",
"no-delete-var": "off",
"no-nested-ternary": "off",
"no-new-object": "off",
"no-redeclare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-undef": "off",
"no-unsafe-finally": "off",
"no-unsanitized/property": "off",
"no-unused-vars": "off",
"no-useless-return": "off",
}
}, {
"files": [
"dom/base/test/chrome/file_bug1139964.xhtml",
"dom/base/test/chrome/file_bug549682.xhtml",
"dom/base/test/chrome/file_bug616841.xhtml",
"dom/base/test/chrome/file_bug990812-1.xhtml",
"dom/base/test/chrome/file_bug990812-2.xhtml",
"dom/base/test/chrome/file_bug990812-3.xhtml",
"dom/base/test/chrome/file_bug990812-4.xhtml",
"dom/base/test/chrome/file_bug990812-5.xhtml",
"dom/base/test/chrome/file_bug990812.xhtml",
"dom/base/test/chrome/test_bug1098074_throw_from_ReceiveMessage.xhtml",
"dom/base/test/chrome/test_bug339494.xhtml",
"dom/base/test/chrome/test_bug429785.xhtml",
"dom/base/test/chrome/test_bug467123.xhtml",
"dom/base/test/chrome/test_bug683852.xhtml",
"dom/base/test/chrome/test_bug780529.xhtml",
"dom/base/test/chrome/test_bug800386.xhtml",
"dom/base/test/chrome/test_bug814638.xhtml",
"dom/base/test/chrome/test_bug884693.xhtml",
"dom/base/test/chrome/test_document-element-inserted.xhtml",
"dom/base/test/chrome/test_domparsing.xhtml",
"dom/base/test/chrome/test_fileconstructor.xhtml",
"dom/base/test/chrome/title_window.xhtml",
"dom/base/test/chrome/window_nsITextInputProcessor.xhtml",
"dom/base/test/chrome/window_swapFrameLoaders.xhtml",
"dom/base/test/test_domrequesthelper.xhtml",
"dom/bindings/test/test_bug1123516_maplikesetlikechrome.xhtml",
"dom/console/tests/test_jsm.xhtml",
"dom/events/test/test_bug1412775.xhtml",
"dom/events/test/test_bug336682_2.xhtml",
"dom/events/test/test_bug415498.xhtml",
"dom/events/test/test_bug602962.xhtml",
"dom/events/test/test_bug617528.xhtml",
"dom/events/test/test_bug679494.xhtml",
"dom/indexedDB/test/test_globalObjects_chrome.xhtml",
"dom/indexedDB/test/test_wrappedArray.xhtml",
"dom/ipc/test.xhtml",
"dom/ipc/tests/test_process_error.xhtml",
"dom/notification/test/chrome/test_notification_system_principal.xhtml",
"dom/plugins/test/mochitest/test_busy_hang.xhtml",
"dom/plugins/test/mochitest/test_convertpoint.xhtml",
"dom/plugins/test/mochitest/test_crash_notify.xhtml",
"dom/plugins/test/mochitest/test_crash_notify_no_report.xhtml",
"dom/plugins/test/mochitest/test_crash_submit.xhtml",
"dom/plugins/test/mochitest/test_hang_submit.xhtml",
"dom/plugins/test/mochitest/test_hangui.xhtml",
"dom/plugins/test/mochitest/test_idle_hang.xhtml",
"dom/plugins/test/mochitest/test_xulbrowser_plugin_visibility.xhtml",
"dom/plugins/test/mochitest/xulbrowser_plugin_visibility.xhtml",
"dom/security/test/general/test_bug1277803.xhtml",
"dom/serviceworkers/test/test_serviceworkerinfo.xhtml",
"dom/serviceworkers/test/test_serviceworkermanager.xhtml",
"dom/system/tests/test_constants.xhtml",
"dom/tests/mochitest/chrome/DOMWindowCreated_chrome.xhtml",
"dom/tests/mochitest/chrome/MozDomFullscreen_chrome.xhtml",
"dom/tests/mochitest/chrome/sizemode_attribute.xhtml",
"dom/tests/mochitest/chrome/test_cyclecollector.xhtml",
"dom/tests/mochitest/chrome/test_docshell_swap.xhtml",
"dom/tests/mochitest/chrome/window_focus.xhtml",
"dom/url/tests/test_bug883784.xhtml",
"dom/workers/test/test_WorkerDebugger.xhtml",
"dom/workers/test/test_WorkerDebugger_console.xhtml",
"dom/workers/test/test_fileReadSlice.xhtml",
"dom/workers/test/test_fileReaderSync.xhtml",
"dom/workers/test/test_fileSlice.xhtml",
],
"rules": {
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/use-chromeutils-generateqi": "off",
"mozilla/use-services": "off",
"complexity": "off",
"no-array-constructor": "off",
"no-caller": "off",
"no-empty": "off",
"no-eval": "off",
"no-lone-blocks": "off",
"no-octal": "off",
"no-redeclare": "off",
"no-shadow": "off",
"no-throw-literal": "off",
"no-undef": "off",
"no-unsanitized/method": "off",
"no-unused-vars": "off",
"no-useless-return": "off",
"object-shorthand": "off",
}
}, {
"files": [
"toolkit/components/aboutmemory/tests/test_aboutmemory.xhtml",
"toolkit/components/aboutmemory/tests/test_aboutmemory2.xhtml",
"toolkit/components/aboutmemory/tests/test_aboutmemory3.xhtml",
"toolkit/components/aboutmemory/tests/test_aboutmemory4.xhtml",
"toolkit/components/aboutmemory/tests/test_aboutmemory5.xhtml",
"toolkit/components/aboutmemory/tests/test_aboutmemory7.xhtml",
"toolkit/components/aboutmemory/tests/test_dumpGCAndCCLogsToFile.xhtml",
"toolkit/components/aboutmemory/tests/test_memoryReporters.xhtml",
"toolkit/components/aboutmemory/tests/test_memoryReporters2.xhtml",
"toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xhtml",
"toolkit/components/ctypes/tests/chrome/test_ctypes.xhtml",
"toolkit/components/osfile/tests/mochi/test_osfile_back.xhtml",
"toolkit/components/osfile/tests/mochi/test_osfile_comms.xhtml",
"toolkit/components/osfile/tests/mochi/test_osfile_front.xhtml",
"toolkit/components/places/tests/chrome/browser_disableglobalhistory.xhtml",
"toolkit/components/places/tests/chrome/test_browser_disableglobalhistory.xhtml",
"toolkit/components/places/tests/chrome/test_favicon_annotations.xhtml",
"toolkit/components/workerloader/tests/test_loading.xhtml",
"toolkit/content/tests/chrome/bug263683_window.xhtml",
"toolkit/content/tests/chrome/bug304188_window.xhtml",
"toolkit/content/tests/chrome/bug331215_window.xhtml",
"toolkit/content/tests/chrome/bug360437_window.xhtml",
"toolkit/content/tests/chrome/bug366992_window.xhtml",
"toolkit/content/tests/chrome/bug409624_window.xhtml",
"toolkit/content/tests/chrome/bug429723_window.xhtml",
"toolkit/content/tests/chrome/bug451540_window.xhtml",
"toolkit/content/tests/chrome/dialog_dialogfocus.xhtml",
"toolkit/content/tests/chrome/findbar_entireword_window.xhtml",
"toolkit/content/tests/chrome/findbar_events_window.xhtml",
"toolkit/content/tests/chrome/findbar_window.xhtml",
"toolkit/content/tests/chrome/frame_popup_anchor.xhtml",
"toolkit/content/tests/chrome/frame_subframe_origin_subframe1.xhtml",
"toolkit/content/tests/chrome/frame_subframe_origin_subframe2.xhtml",
"toolkit/content/tests/chrome/test_arrowpanel.xhtml",
"toolkit/content/tests/chrome/test_autocomplete2.xhtml",
"toolkit/content/tests/chrome/test_autocomplete3.xhtml",
"toolkit/content/tests/chrome/test_autocomplete4.xhtml",
"toolkit/content/tests/chrome/test_autocomplete5.xhtml",
"toolkit/content/tests/chrome/test_autocomplete_emphasis.xhtml",
"toolkit/content/tests/chrome/test_autocomplete_mac_caret.xhtml",
"toolkit/content/tests/chrome/test_autocomplete_placehold_last_complete.xhtml",
"toolkit/content/tests/chrome/test_browser_drop.xhtml",
"toolkit/content/tests/chrome/test_bug1048178.xhtml",
"toolkit/content/tests/chrome/test_bug382990.xhtml",
"toolkit/content/tests/chrome/test_bug437844.xhtml",
"toolkit/content/tests/chrome/test_bug624329.xhtml",
"toolkit/content/tests/chrome/test_bug792324.xhtml",
"toolkit/content/tests/chrome/test_contextmenu_list.xhtml",
"toolkit/content/tests/chrome/test_cursorsnap.xhtml",
"toolkit/content/tests/chrome/test_dialogfocus.xhtml",
"toolkit/content/tests/chrome/test_hiddenitems.xhtml",
"toolkit/content/tests/chrome/test_hiddenpaging.xhtml",
"toolkit/content/tests/chrome/test_maximized_persist.xhtml",
"toolkit/content/tests/chrome/test_menu.xhtml",
"toolkit/content/tests/chrome/test_menuitem_blink.xhtml",
"toolkit/content/tests/chrome/test_menulist.xhtml",
"toolkit/content/tests/chrome/test_menulist_keynav.xhtml",
"toolkit/content/tests/chrome/test_mousescroll.xhtml",
"toolkit/content/tests/chrome/test_mozinputbox_dictionary.xhtml",
"toolkit/content/tests/chrome/test_notificationbox.xhtml",
"toolkit/content/tests/chrome/test_panel_focus.xhtml",
"toolkit/content/tests/chrome/test_popup_keys.xhtml",
"toolkit/content/tests/chrome/test_popup_scaled.xhtml",
"toolkit/content/tests/chrome/test_popupincontent.xhtml",
"toolkit/content/tests/chrome/test_popupremoving.xhtml",
"toolkit/content/tests/chrome/test_popupremoving_frame.xhtml",
"toolkit/content/tests/chrome/test_position.xhtml",
"toolkit/content/tests/chrome/test_preferences.xhtml",
"toolkit/content/tests/chrome/test_richlistbox.xhtml",
"toolkit/content/tests/chrome/test_righttoleft.xhtml",
"toolkit/content/tests/chrome/test_screenPersistence.xhtml",
"toolkit/content/tests/chrome/test_scrollbar.xhtml",
"toolkit/content/tests/chrome/test_showcaret.xhtml",
"toolkit/content/tests/chrome/test_tabbox.xhtml",
"toolkit/content/tests/chrome/test_textbox_search.xhtml",
"toolkit/content/tests/chrome/test_tree_view.xhtml",
"toolkit/content/tests/chrome/window_browser_drop.xhtml",
"toolkit/content/tests/chrome/window_cursorsnap_dialog.xhtml",
"toolkit/content/tests/chrome/window_cursorsnap_wizard.xhtml",
"toolkit/content/tests/chrome/window_keys.xhtml",
"toolkit/content/tests/chrome/window_largemenu.xhtml",
"toolkit/content/tests/chrome/window_panel.xhtml",
"toolkit/content/tests/chrome/window_panel_anchoradjust.xhtml",
"toolkit/content/tests/chrome/window_popup_preventdefault_chrome.xhtml",
"toolkit/content/tests/chrome/window_preferences.xhtml",
"toolkit/content/tests/chrome/window_preferences3.xhtml",
"toolkit/content/tests/chrome/window_preferences_beforeaccept.xhtml",
"toolkit/content/tests/chrome/window_preferences_commandretarget.xhtml",
"toolkit/content/tests/chrome/window_preferences_onsyncfrompreference.xhtml",
"toolkit/content/tests/chrome/window_subframe_origin.xhtml",
"toolkit/content/tests/chrome/window_titlebar.xhtml",
"toolkit/content/tests/chrome/window_tooltip.xhtml",
"toolkit/content/tests/widgets/test_contextmenu_menugroup.xhtml",
"toolkit/content/tests/widgets/test_contextmenu_nested.xhtml",
"toolkit/content/tests/widgets/test_editor_currentURI.xhtml",
"toolkit/content/tests/widgets/test_popupanchor.xhtml",
"toolkit/content/tests/widgets/test_popupreflows.xhtml",
"toolkit/content/tests/widgets/window_menubar.xhtml",
"toolkit/modules/tests/chrome/test_bug544442_checkCert.xhtml",
"toolkit/profile/test/test_create_profile.xhtml",
],
"rules": {
"object-shorthand": "off",
"consistent-return": "off",
"mozilla/consistent-if-bracing": "off",
"mozilla/no-compare-against-boolean-literals": "off",
"mozilla/no-useless-parameters": "off",
"mozilla/no-useless-removeEventListener": "off",
"mozilla/prefer-boolean-length-check": "off",
"mozilla/use-cc-etc": "off",
"mozilla/use-chromeutils-generateqi": "off",
"mozilla/use-chromeutils-import": "off",
"mozilla/use-default-preference-values": "off",
"mozilla/use-services": "off",
"no-caller": "off",
"no-else-return": "off",
"no-eval": "off",
"no-fallthrough": "off",
"no-irregular-whitespace": "off",
"no-lonely-if": "off",
"no-nested-ternary": "off",
"no-redeclare": "off",
"no-sequences": "off",
"no-shadow": "off",
"no-throw-literal": "off",
"no-undef": "off",
"no-unneeded-ternary": "off",
"no-unused-vars": "off",
"no-useless-concat": "off",
"no-useless-return": "off",
}
}, {
"files": [
"accessible/**",
"devtools/**",
"dom/**",
"docshell/**",
"editor/libeditor/tests/**",
"editor/spellchecker/tests/test_bug338427.html",
"gfx/**",
"image/test/browser/browser_image.js",
"js/src/builtin/**",
"layout/**",
"mobile/android/**",
"modules/**",
"netwerk/**",
"remote/**",
"security/manager/**",
"services/**",
"storage/test/unit/test_vacuum.js",
"taskcluster/docker/periodic-updates/scripts/**",
"testing/**",
"tools/**",
"widget/tests/test_assign_event_data.html",
],
"rules": {
"mozilla/prefer-boolean-length-check": "off",
}
}]
};

4
Cargo.lock generated
View File

@ -796,9 +796,9 @@ dependencies = [
[[package]]
name = "dogear"
version = "0.2.5"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b7583e1427e296c852f3217eaab3890e698f742b8d7349beb1f40c4e946fc9"
checksum = "c01a457f8d6689260111be60774bfb68e558b41bc89b866ebc3bbed60ba255cb"
dependencies = [
"log",
"smallbitvec",

View File

@ -7,7 +7,7 @@ A browser for Windows XP based on Firefox 68.
## KNOWN PROBLEMS
- 1.5gb memory limit on winxp, if the browser reaches the limit it crashes inevitably, singleporess mode crashes all,
- 1.5gb memory limit on winxp, if the browser reaches the limit it crashes inevitably, singleprocess mode crashes all,
multiprocess only this which reaches 1.5gb, so it better here.
I think this is OS limitation, nothing to do with this.
@ -17,8 +17,9 @@ A browser for Windows XP based on Firefox 68.
- This browser does not run well on winxp SP2 and lower. If you do not want to install SP3, be ready for crashes and blue screens.
If you are on SP2 and lower, there is no need to post a screenshot from blue_screen_view. And i suggest to apply postready updates of 2019.
Some say that it runs fine on sp2 but it is no accurate data what to do.
Some say that it runs fine on sp2 but it is no accurate data what to do, [there about win2000](https://mrqash.blogspot.com/2022/04/mypal-68-firefox-68121-on-windows-2000.html).
## [UPDATING AND POSSIBLE PROBLEMS](https://github.com/Feodor2/Mypal68/wiki/Updating-to-a-new-version)
## IF YOU GOT A BLUE SCREEN
@ -33,15 +34,15 @@ Do not post pictures of the crash, this is useless and a waste.
Also put your pc specs: cpu, ram and graphics card
If I don't reproduce the crash myself then you may to submit drwatson.log.
Drwatson log is usually inside All Users\Application Data\Microsoft\Dr Watson.
Minidump also may be usefull. Do not post any irrelevent logs.
An issue without details cosider as invalid.
Minidump also may be usefull. Do not post any irrelevant logs.
An issue without details considered as invalid.
## IF YOU WANT REPORT A SITE
Report [there](https://github.com/Feodor2/Mypal68/issues/228).
Put the actual link to the site
Notice that I never would look to the site which requires the login, including any goolag sites.
Please try to find what feature is missig by yourself by checking on newer firefoxes and find first version where it works.
Please try to find what feature is missing by yourself by checking on newer firefoxes and find first version where it works (mozregression day).
Actually I do not look every site, no time for this.
## YOU MAY DONATE

View File

@ -5,17 +5,22 @@
#include "AccessibleWrap.h"
#include "Accessible-inl.h"
#include "AccEvent.h"
#include "AndroidInputType.h"
#include "DocAccessibleWrap.h"
#include "IDSet.h"
#include "JavaBuiltins.h"
#include "SessionAccessibility.h"
#include "TraversalRule.h"
#include "Pivot.h"
#include "nsAccessibilityService.h"
#include "nsEventShell.h"
#include "nsPersistentProperties.h"
#include "nsIAccessibleAnnouncementEvent.h"
#include "nsIStringBundle.h"
#include "nsAccUtils.h"
#include "nsTextEquivUtils.h"
#include "RootAccessible.h"
#include "mozilla/a11y/PDocAccessibleChild.h"
#include "mozilla/jni/GeckoBundleUtils.h"
@ -78,6 +83,27 @@ nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
}
break;
}
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
if (accessible != aEvent->Document() && !aEvent->IsFromUserInput()) {
AccCaretMoveEvent* caretEvent = downcast_accEvent(aEvent);
if (IsHyperText()) {
DOMPoint point =
AsHyperText()->OffsetToDOMPoint(caretEvent->GetCaretOffset());
if (Accessible* newPos =
doc->GetAccessibleOrContainer(point.node)) {
static_cast<AccessibleWrap*>(newPos)->Pivot(
java::SessionAccessibility::HTML_GRANULARITY_DEFAULT, true,
true);
}
}
}
break;
}
case nsIAccessibleEvent::EVENT_SCROLLING_START: {
accessible->Pivot(java::SessionAccessibility::HTML_GRANULARITY_DEFAULT,
true, true);
break;
}
default:
break;
}
@ -122,15 +148,11 @@ nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) {
RefPtr<AccessibleWrap> newPosition =
static_cast<AccessibleWrap*>(vcEvent->NewAccessible());
auto oldPosition = static_cast<AccessibleWrap*>(vcEvent->OldAccessible());
if (sessionAcc && newPosition) {
if (oldPosition != newPosition) {
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(newPosition);
} else {
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
}
if (vcEvent->Reason() == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(newPosition);
} else {
sessionAcc->SendAccessibilityFocusedEvent(newPosition);
}
if (vcEvent->BoundaryType() != nsIAccessiblePivot::NO_BOUNDARY) {
@ -247,6 +269,155 @@ bool AccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
return false;
}
void AccessibleWrap::Pivot(int32_t aGranularity, bool aForward,
bool aInclusive) {
a11y::Pivot pivot(RootAccessible());
TraversalRule rule(aGranularity);
Accessible* result = aForward ? pivot.Next(this, rule, aInclusive)
: pivot.Prev(this, rule, aInclusive);
if (result && (result != this || aInclusive)) {
PivotMoveReason reason = aForward ? nsIAccessiblePivot::REASON_NEXT
: nsIAccessiblePivot::REASON_PREV;
RefPtr<AccEvent> event = new AccVCChangeEvent(
result->Document(), this, -1, -1, result, -1, -1, reason,
nsIAccessiblePivot::NO_BOUNDARY, eFromUserInput);
nsEventShell::FireEvent(event);
}
}
void AccessibleWrap::ExploreByTouch(float aX, float aY) {
a11y::Pivot pivot(RootAccessible());
TraversalRule rule;
Accessible* result = pivot.AtPoint(aX, aY, rule);
if (result && result != this) {
RefPtr<AccEvent> event =
new AccVCChangeEvent(result->Document(), this, -1, -1, result, -1, -1,
nsIAccessiblePivot::REASON_POINT,
nsIAccessiblePivot::NO_BOUNDARY, eFromUserInput);
nsEventShell::FireEvent(event);
}
}
void AccessibleWrap::NavigateText(int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward,
bool aSelect) {
a11y::Pivot pivot(RootAccessible());
HyperTextAccessible* editable =
(State() & states::EDITABLE) != 0 ? AsHyperText() : nullptr;
int32_t start = aStartOffset, end = aEndOffset;
// If the accessible is an editable, set the virtual cursor position
// to its caret offset. Otherwise use the document's virtual cursor
// position as a starting offset.
if (editable) {
start = end = editable->CaretOffset();
}
uint16_t pivotGranularity = nsIAccessiblePivot::LINE_BOUNDARY;
switch (aGranularity) {
case 1: // MOVEMENT_GRANULARITY_CHARACTER
pivotGranularity = nsIAccessiblePivot::CHAR_BOUNDARY;
break;
case 2: // MOVEMENT_GRANULARITY_WORD
pivotGranularity = nsIAccessiblePivot::WORD_BOUNDARY;
break;
default:
break;
}
int32_t newOffset;
Accessible* newAnchor = nullptr;
if (aForward) {
newAnchor = pivot.NextText(this, &start, &end, pivotGranularity);
newOffset = end;
} else {
newAnchor = pivot.PrevText(this, &start, &end, pivotGranularity);
newOffset = start;
}
if (newAnchor && (start != aStartOffset || end != aEndOffset)) {
RefPtr<AccEvent> event = new AccVCChangeEvent(
newAnchor->Document(), this, aStartOffset, aEndOffset, newAnchor, start,
end, nsIAccessiblePivot::REASON_NONE, pivotGranularity, eFromUserInput);
nsEventShell::FireEvent(event);
}
// If we are in an editable, move the caret to the new virtual cursor
// offset.
if (editable) {
if (aSelect) {
int32_t anchor = editable->CaretOffset();
if (editable->SelectionCount()) {
int32_t startSel, endSel;
GetSelectionOrCaret(&startSel, &endSel);
anchor = startSel == anchor ? endSel : startSel;
}
editable->SetSelectionBoundsAt(0, anchor, newOffset);
} else {
editable->SetCaretOffset(newOffset);
}
}
}
void AccessibleWrap::SetSelection(int32_t aStart, int32_t aEnd) {
if (HyperTextAccessible* textAcc = AsHyperText()) {
if (aStart == aEnd) {
textAcc->SetCaretOffset(aStart);
} else {
textAcc->SetSelectionBoundsAt(0, aStart, aEnd);
}
}
}
void AccessibleWrap::Cut() {
if ((State() & states::EDITABLE) == 0) {
return;
}
if (HyperTextAccessible* textAcc = AsHyperText()) {
int32_t startSel, endSel;
GetSelectionOrCaret(&startSel, &endSel);
textAcc->CutText(startSel, endSel);
}
}
void AccessibleWrap::Copy() {
if (HyperTextAccessible* textAcc = AsHyperText()) {
int32_t startSel, endSel;
GetSelectionOrCaret(&startSel, &endSel);
textAcc->CopyText(startSel, endSel);
}
}
void AccessibleWrap::Paste() {
if ((State() & states::EDITABLE) == 0) {
return;
}
if (IsHyperText()) {
RefPtr<HyperTextAccessible> textAcc = AsHyperText();
int32_t startSel, endSel;
GetSelectionOrCaret(&startSel, &endSel);
if (startSel != endSel) {
textAcc->DeleteText(startSel, endSel);
}
textAcc->PasteText(startSel);
}
}
void AccessibleWrap::GetSelectionOrCaret(int32_t* aStartOffset,
int32_t* aEndOffset) {
*aStartOffset = *aEndOffset = -1;
if (HyperTextAccessible* textAcc = AsHyperText()) {
if (!textAcc->SelectionBoundsAt(0, aStartOffset, aEndOffset)) {
*aStartOffset = *aEndOffset = textAcc->CaretOffset();
}
}
}
uint32_t AccessibleWrap::GetFlags(role aRole, uint64_t aState,
uint8_t aActionCount) {
uint32_t flags = 0;

View File

@ -34,6 +34,22 @@ class AccessibleWrap : public Accessible {
virtual bool GetSelectionBounds(int32_t* aStartOffset, int32_t* aEndOffset);
virtual void Pivot(int32_t aGranularity, bool aForward, bool aInclusive);
virtual void ExploreByTouch(float aX, float aY);
virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward, bool aSelect);
virtual void SetSelection(int32_t aStart, int32_t aEnd);
virtual void Cut();
virtual void Copy();
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual void Paste();
mozilla::java::GeckoBundle::LocalRef ToBundle(bool aSmall = false);
mozilla::java::GeckoBundle::LocalRef ToBundle(
@ -84,6 +100,8 @@ class AccessibleWrap : public Accessible {
bool HandleLiveRegionEvent(AccEvent* aEvent);
void GetSelectionOrCaret(int32_t* aStartOffset, int32_t* aEndOffset);
static void GetRoleDescription(role aRole,
nsIPersistentProperties* aAttributes,
nsAString& aGeckoRole,

View File

@ -128,13 +128,11 @@ void a11y::ProxyVirtualCursorChangeEvent(
return;
}
if (aOldPosition != aNewPosition) {
if (aReason == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(WrapperFor(aNewPosition));
} else {
RefPtr<AccessibleWrap> wrapperForNewPosition = WrapperFor(aNewPosition);
sessionAcc->SendAccessibilityFocusedEvent(wrapperForNewPosition);
}
if (aReason == nsIAccessiblePivot::REASON_POINT) {
sessionAcc->SendHoverEnterEvent(WrapperFor(aNewPosition));
} else {
RefPtr<AccessibleWrap> wrapperForNewPosition = WrapperFor(aNewPosition);
sessionAcc->SendAccessibilityFocusedEvent(wrapperForNewPosition);
}
if (aBoundaryType != nsIAccessiblePivot::NO_BOUNDARY) {

View File

@ -3,8 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ProxyAccessibleWrap.h"
#include "nsPersistentProperties.h"
#include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
using namespace mozilla::a11y;
ProxyAccessibleWrap::ProxyAccessibleWrap(ProxyAccessible* aProxy)
@ -105,6 +108,43 @@ bool ProxyAccessibleWrap::GetSelectionBounds(int32_t* aStartOffset,
return Proxy()->SelectionBoundsAt(0, unused, aStartOffset, aEndOffset);
}
void ProxyAccessibleWrap::Pivot(int32_t aGranularity, bool aForward,
bool aInclusive) {
Unused << Proxy()->Document()->GetPlatformExtension()->SendPivot(
Proxy()->ID(), aGranularity, aForward, aInclusive);
}
void ProxyAccessibleWrap::ExploreByTouch(float aX, float aY) {
Unused << Proxy()->Document()->GetPlatformExtension()->SendExploreByTouch(
Proxy()->ID(), aX, aY);
}
void ProxyAccessibleWrap::NavigateText(int32_t aGranularity,
int32_t aStartOffset, int32_t aEndOffset,
bool aForward, bool aSelect) {
Unused << Proxy()->Document()->GetPlatformExtension()->SendNavigateText(
Proxy()->ID(), aGranularity, aStartOffset, aEndOffset, aForward, aSelect);
}
void ProxyAccessibleWrap::SetSelection(int32_t aStart, int32_t aEnd) {
Unused << Proxy()->Document()->GetPlatformExtension()->SendSetSelection(
Proxy()->ID(), aStart, aEnd);
}
void ProxyAccessibleWrap::Cut() {
Unused << Proxy()->Document()->GetPlatformExtension()->SendCut(Proxy()->ID());
}
void ProxyAccessibleWrap::Copy() {
Unused << Proxy()->Document()->GetPlatformExtension()->SendCopy(
Proxy()->ID());
}
void ProxyAccessibleWrap::Paste() {
Unused << Proxy()->Document()->GetPlatformExtension()->SendPaste(
Proxy()->ID());
}
role ProxyAccessibleWrap::WrapperRole() { return Proxy()->Role(); }
AccessibleWrap* ProxyAccessibleWrap::WrapperParent() {

View File

@ -57,6 +57,23 @@ class ProxyAccessibleWrap : public AccessibleWrap {
virtual bool GetSelectionBounds(int32_t* aStartOffset,
int32_t* aEndOffset) override;
virtual void Pivot(int32_t aGranularity, bool aForward,
bool aInclusive) override;
virtual void NavigateText(int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward,
bool aSelect) override;
virtual void SetSelection(int32_t aStart, int32_t aEnd) override;
virtual void Cut() override;
virtual void Copy() override;
virtual void Paste() override;
virtual void ExploreByTouch(float aX, float aY) override;
virtual void WrapperDOMNodeID(nsString& aDOMNodeID) override;
private:

View File

@ -30,6 +30,16 @@
} while (0)
#endif
#define FORWARD_ACTION_TO_ACCESSIBLE(funcname, ...) \
if (RootAccessibleWrap* rootAcc = GetRoot()) { \
AccessibleWrap* acc = rootAcc->FindAccessibleById(aID); \
if (!acc) { \
return; \
} \
\
acc->funcname(__VA_ARGS__); \
}
template <>
const char nsWindow::NativePtr<mozilla::a11y::SessionAccessibility>::sName[] =
"SessionAccessibility";
@ -97,25 +107,45 @@ RootAccessibleWrap* SessionAccessibility::GetRoot() {
}
void SessionAccessibility::SetText(int32_t aID, jni::String::Param aText) {
if (RootAccessibleWrap* rootAcc = GetRoot()) {
AccessibleWrap* acc = rootAcc->FindAccessibleById(aID);
if (!acc) {
return;
}
acc->SetTextContents(aText->ToString());
}
FORWARD_ACTION_TO_ACCESSIBLE(SetTextContents, aText->ToString());
}
void SessionAccessibility::Click(int32_t aID) {
if (RootAccessibleWrap* rootAcc = GetRoot()) {
AccessibleWrap* acc = rootAcc->FindAccessibleById(aID);
if (!acc) {
return;
}
FORWARD_ACTION_TO_ACCESSIBLE(DoAction, 0);
}
acc->DoAction(0);
}
void SessionAccessibility::Pivot(int32_t aID, int32_t aGranularity,
bool aForward, bool aInclusive) {
FORWARD_ACTION_TO_ACCESSIBLE(Pivot, aGranularity, aForward, aInclusive);
}
void SessionAccessibility::ExploreByTouch(int32_t aID, float aX, float aY) {
FORWARD_ACTION_TO_ACCESSIBLE(ExploreByTouch, aX, aY);
}
void SessionAccessibility::NavigateText(int32_t aID, int32_t aGranularity,
int32_t aStartOffset,
int32_t aEndOffset, bool aForward,
bool aSelect) {
FORWARD_ACTION_TO_ACCESSIBLE(NavigateText, aGranularity, aStartOffset,
aEndOffset, aForward, aSelect);
}
void SessionAccessibility::SetSelection(int32_t aID, int32_t aStart,
int32_t aEnd) {
FORWARD_ACTION_TO_ACCESSIBLE(SetSelection, aStart, aEnd);
}
void SessionAccessibility::Cut(int32_t aID) {
FORWARD_ACTION_TO_ACCESSIBLE(Cut);
}
void SessionAccessibility::Copy(int32_t aID) {
FORWARD_ACTION_TO_ACCESSIBLE(Copy);
}
void SessionAccessibility::Paste(int32_t aID) {
FORWARD_ACTION_TO_ACCESSIBLE(Paste);
}
SessionAccessibility* SessionAccessibility::GetInstanceFor(
@ -417,3 +447,5 @@ void SessionAccessibility::UpdateCachedBounds(
mSessionAccessibility->UpdateCachedBounds(infos);
SendWindowContentChangedEvent();
}
#undef FORWARD_ACTION_TO_ACCESSIBLE

View File

@ -53,6 +53,14 @@ class SessionAccessibility final
jni::Object::LocalRef GetNodeInfo(int32_t aID);
void SetText(int32_t aID, jni::String::Param aText);
void Click(int32_t aID);
void Pivot(int32_t aID, int32_t aGranularity, bool aForward, bool aInclusive);
void ExploreByTouch(int32_t aID, float aX, float aY);
void NavigateText(int32_t aID, int32_t aGranularity, int32_t aStartOffset,
int32_t aEndOffset, bool aForward, bool aSelect);
void SetSelection(int32_t aID, int32_t aStart, int32_t aEnd);
void Cut(int32_t aID);
void Copy(int32_t aID);
void Paste(int32_t aID);
void StartNativeAccessibility();
// Event methods

View File

@ -0,0 +1,251 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TraversalRule.h"
#include "mozilla/ArrayUtils.h"
#include "Role.h"
#include "Accessible.h"
#include "HTMLListAccessible.h"
#include "SessionAccessibility.h"
#include "nsAccUtils.h"
#include "nsIAccessiblePivot.h"
using namespace mozilla;
using namespace mozilla::a11y;
TraversalRule::TraversalRule()
: TraversalRule(java::SessionAccessibility::HTML_GRANULARITY_DEFAULT) {}
TraversalRule::TraversalRule(int32_t aGranularity)
: mGranularity(aGranularity) {}
uint16_t TraversalRule::Match(Accessible* aAccessible) {
uint16_t result = nsIAccessibleTraversalRule::FILTER_IGNORE;
if (nsAccUtils::MustPrune(aAccessible)) {
result |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
uint64_t state = aAccessible->State();
if ((state & states::INVISIBLE) != 0) {
return result;
}
if ((state & states::OPAQUE1) == 0) {
nsIFrame* frame = aAccessible->GetFrame();
if (frame->StyleEffects()->mOpacity == 0.0f) {
return result | nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
}
switch (mGranularity) {
case java::SessionAccessibility::HTML_GRANULARITY_LINK:
result |= LinkMatch(aAccessible);
break;
case java::SessionAccessibility::HTML_GRANULARITY_CONTROL:
result |= ControlMatch(aAccessible);
break;
case java::SessionAccessibility::HTML_GRANULARITY_SECTION:
result |= SectionMatch(aAccessible);
break;
case java::SessionAccessibility::HTML_GRANULARITY_HEADING:
result |= HeadingMatch(aAccessible);
break;
default:
result |= DefaultMatch(aAccessible);
break;
}
return result;
}
bool TraversalRule::IsSingleLineage(Accessible* aAccessible) {
Accessible* child = aAccessible;
while (child) {
switch (child->ChildCount()) {
case 0:
return true;
case 1:
child = child->FirstChild();
break;
case 2:
if (Accessible* bullet =
child->Parent()->IsHTMLListItem()
? child->Parent()->AsHTMLListItem()->Bullet()
: nullptr) {
child = bullet->NextSibling();
} else {
return false;
}
break;
default:
return false;
}
}
return true;
}
bool TraversalRule::IsListItemBullet(const Accessible* aAccessible) {
Accessible* parent = aAccessible->Parent();
return parent && parent->IsHTMLListItem() &&
parent->AsHTMLListItem()->Bullet() == aAccessible;
}
bool TraversalRule::IsFlatSubtree(const Accessible* aAccessible) {
for (auto child = aAccessible->FirstChild(); child;
child = child->NextSibling()) {
roles::Role role = child->Role();
if (role == roles::TEXT_LEAF || role == roles::STATICTEXT) {
continue;
}
if (child->ChildCount() > 0 || child->ActionCount() > 0) {
return false;
}
}
return true;
}
bool TraversalRule::HasName(const Accessible* aAccessible) {
nsAutoString name;
aAccessible->Name(name);
name.CompressWhitespace();
return !name.IsEmpty();
}
uint16_t TraversalRule::LinkMatch(Accessible* aAccessible) {
if (aAccessible->Role() == roles::LINK &&
(aAccessible->State() & states::LINKED) != 0) {
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
uint16_t TraversalRule::HeadingMatch(Accessible* aAccessible) {
if (aAccessible->Role() == roles::HEADING && aAccessible->ChildCount()) {
return nsIAccessibleTraversalRule::FILTER_MATCH;
}
return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
uint16_t TraversalRule::SectionMatch(Accessible* aAccessible) {
roles::Role role = aAccessible->Role();
if (role == roles::HEADING || role == roles::LANDMARK ||
aAccessible->LandmarkRole()) {
return nsIAccessibleTraversalRule::FILTER_MATCH;
}
return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
uint16_t TraversalRule::ControlMatch(Accessible* aAccessible) {
switch (aAccessible->Role()) {
case roles::PUSHBUTTON:
case roles::SPINBUTTON:
case roles::TOGGLE_BUTTON:
case roles::BUTTONDROPDOWN:
case roles::BUTTONDROPDOWNGRID:
case roles::COMBOBOX:
case roles::LISTBOX:
case roles::ENTRY:
case roles::PASSWORD_TEXT:
case roles::PAGETAB:
case roles::RADIOBUTTON:
case roles::RADIO_MENU_ITEM:
case roles::SLIDER:
case roles::CHECKBUTTON:
case roles::CHECK_MENU_ITEM:
case roles::SWITCH:
case roles::MENUITEM:
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
case roles::LINK:
return LinkMatch(aAccessible);
default:
break;
}
return nsIAccessibleTraversalRule::FILTER_IGNORE;
}
uint16_t TraversalRule::DefaultMatch(Accessible* aAccessible) {
switch (aAccessible->Role()) {
case roles::COMBOBOX:
// We don't want to ignore the subtree because this is often
// where the list box hangs out.
return nsIAccessibleTraversalRule::FILTER_MATCH;
case roles::TEXT_LEAF:
case roles::GRAPHIC:
// Nameless text leaves are boring, skip them.
if (HasName(aAccessible)) {
return nsIAccessibleTraversalRule::FILTER_MATCH;
}
break;
case roles::STATICTEXT:
// Ignore list bullets
if (!IsListItemBullet(aAccessible)) {
return nsIAccessibleTraversalRule::FILTER_MATCH;
}
break;
case roles::HEADER:
case roles::HEADING:
case roles::COLUMNHEADER:
case roles::ROWHEADER:
case roles::STATUSBAR:
if ((aAccessible->ChildCount() > 0 || HasName(aAccessible)) &&
(IsSingleLineage(aAccessible) || IsFlatSubtree(aAccessible))) {
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
break;
case roles::GRID_CELL:
if (IsSingleLineage(aAccessible) || IsFlatSubtree(aAccessible)) {
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
break;
case roles::LISTITEM:
if (IsFlatSubtree(aAccessible) || IsSingleLineage(aAccessible)) {
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
}
break;
case roles::MENUITEM:
case roles::LINK:
case roles::PAGETAB:
case roles::PUSHBUTTON:
case roles::CHECKBUTTON:
case roles::RADIOBUTTON:
case roles::PROGRESSBAR:
case roles::BUTTONDROPDOWN:
case roles::BUTTONMENU:
case roles::CHECK_MENU_ITEM:
case roles::PASSWORD_TEXT:
case roles::RADIO_MENU_ITEM:
case roles::TOGGLE_BUTTON:
case roles::ENTRY:
case roles::KEY:
case roles::SLIDER:
case roles::SPINBUTTON:
case roles::OPTION:
case roles::SWITCH:
case roles::MATHML_MATH:
// Ignore the subtree, if there is one. So that we don't land on
// the same content that was already presented by its parent.
return nsIAccessibleTraversalRule::FILTER_MATCH |
nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
default:
break;
}
return nsIAccessibleTraversalRule::FILTER_IGNORE;
}

View File

@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _TraversalRule_H_
#define _TraversalRule_H_
#include "Pivot.h"
namespace mozilla {
namespace a11y {
class Accessible;
/**
* Class represents a simple traversal rule.
*/
class TraversalRule final : public PivotRule {
public:
TraversalRule();
explicit TraversalRule(int32_t aGranularity);
~TraversalRule() = default;
virtual uint16_t Match(Accessible* aAccessible) override;
private:
bool IsSingleLineage(Accessible* aAccessible);
bool IsFlatSubtree(const Accessible* aAccessible);
bool IsListItemBullet(const Accessible* aAccessible);
bool HasName(const Accessible* aAccessible);
uint16_t DefaultMatch(Accessible* aAccessible);
uint16_t LinkMatch(Accessible* aAccessible);
uint16_t HeadingMatch(Accessible* aAccessible);
uint16_t ControlMatch(Accessible* aAccessible);
uint16_t SectionMatch(Accessible* aAccessible);
int32_t mGranularity;
};
} // namespace a11y
} // namespace mozilla
#endif

View File

@ -5,6 +5,7 @@
EXPORTS.mozilla.a11y += ['AccessibleWrap.h',
'HyperTextAccessibleWrap.h',
'SessionAccessibility.h',
'TraversalRule.h',
]
SOURCES += [
@ -14,6 +15,7 @@ SOURCES += [
'ProxyAccessibleWrap.cpp',
'RootAccessibleWrap.cpp',
'SessionAccessibility.cpp',
'TraversalRule.cpp',
]
LOCAL_INCLUDES += [
@ -22,6 +24,7 @@ LOCAL_INCLUDES += [
'/accessible/html',
'/accessible/ipc',
'/accessible/ipc/other',
'/accessible/xpcom',
'/accessible/xul',
'/dom/base',
'/widget',

View File

@ -62,7 +62,7 @@ class AccessibleWrap : public Accessible {
static const char* ReturnString(nsAString& aString) {
static nsCString returnedString;
returnedString = NS_ConvertUTF16toUTF8(aString);
CopyUTF16toUTF8(aString, returnedString);
return returnedString.get();
}

View File

@ -72,16 +72,10 @@ RelatedAccIterator::RelatedAccIterator(DocAccessible* aDocument,
: mDocument(aDocument),
mRelAttr(aRelAttr),
mProviders(nullptr),
mBindingParent(nullptr),
mIndex(0) {
mBindingParent = aDependentContent->IsInAnonymousSubtree()
? aDependentContent->GetBindingParent()
: nullptr;
nsAtom* IDAttr = mBindingParent ? nsGkAtoms::anonid : nsGkAtoms::id;
nsAutoString id;
if (aDependentContent->IsElement() &&
aDependentContent->AsElement()->GetAttr(kNameSpaceID_None, IDAttr, id)) {
aDependentContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id)) {
mProviders = mDocument->GetRelProviders(aDependentContent->AsElement(), id);
}
}
@ -92,22 +86,17 @@ Accessible* RelatedAccIterator::Next() {
while (mIndex < mProviders->Length()) {
DocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++];
// Return related accessible for the given attribute and if the provider
// content is in the same binding in the case of XBL usage.
// Return related accessible for the given attribute.
if (provider->mRelAttr == mRelAttr) {
nsIContent* bindingParent = provider->mContent->IsInAnonymousSubtree()
? provider->mContent->GetBindingParent()
: nullptr;
bool inScope = mBindingParent == bindingParent ||
mBindingParent == provider->mContent;
Accessible* related = mDocument->GetAccessible(provider->mContent);
if (related) {
return related;
}
if (inScope) {
Accessible* related = mDocument->GetAccessible(provider->mContent);
if (related) return related;
// If the document content is pointed by relation then return the
// document itself.
if (provider->mContent == mDocument->GetContent()) return mDocument;
// If the document content is pointed by relation then return the
// document itself.
if (provider->mContent == mDocument->GetContent()) {
return mDocument;
}
}
}
@ -255,40 +244,27 @@ nsIContent* IDRefsIterator::NextElem() {
return nullptr;
}
nsIContent* IDRefsIterator::GetElem(const nsDependentSubstring& aID) {
dom::Element* IDRefsIterator::GetElem(nsIContent* aContent,
const nsAString& aID) {
// Get elements in DOM tree by ID attribute if this is an explicit content.
// In case of bound element check its anonymous subtree.
if (!mContent->IsInAnonymousSubtree()) {
if (!aContent->IsInAnonymousSubtree()) {
dom::DocumentOrShadowRoot* docOrShadowRoot =
mContent->GetUncomposedDocOrConnectedShadowRoot();
aContent->GetUncomposedDocOrConnectedShadowRoot();
if (docOrShadowRoot) {
dom::Element* refElm = docOrShadowRoot->GetElementById(aID);
if (refElm || !mContent->GetXBLBinding()) return refElm;
if (refElm) {
return refElm;
}
}
}
// If content is in anonymous subtree or an element having anonymous subtree
// then use "anonid" attribute to get elements in anonymous subtree.
// Check inside the binding the element is contained in.
nsIContent* bindingParent = mContent->GetBindingParent();
if (bindingParent) {
nsIContent* refElm =
bindingParent->OwnerDoc()->GetAnonymousElementByAttribute(
bindingParent, nsGkAtoms::anonid, aID);
if (refElm) return refElm;
}
// Check inside the binding of the element.
if (mContent->GetXBLBinding()) {
return mContent->OwnerDoc()->GetAnonymousElementByAttribute(
mContent, nsGkAtoms::anonid, aID);
}
return nullptr;
}
dom::Element* IDRefsIterator::GetElem(const nsDependentSubstring& aID) {
return GetElem(mContent, aID);
}
Accessible* IDRefsIterator::Next() {
nsIContent* nextEl = nullptr;
while ((nextEl = NextElem())) {

View File

@ -95,7 +95,6 @@ class RelatedAccIterator : public AccIterable {
DocAccessible* mDocument;
nsAtom* mRelAttr;
DocAccessible::AttrRelProviders* mProviders;
nsIContent* mBindingParent;
uint32_t mIndex;
};
@ -217,7 +216,8 @@ class IDRefsIterator : public AccIterable {
/**
* Return the element with the given ID.
*/
nsIContent* GetElem(const nsDependentSubstring& aID);
static dom::Element* GetElem(nsIContent* aContent, const nsAString& aID);
dom::Element* GetElem(const nsDependentSubstring& aID);
// AccIterable
virtual Accessible* Next() override;

View File

@ -412,35 +412,9 @@ void NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument) {
}
void NotificationController::ScheduleContentInsertion(
nsIContent* aStartChildNode, nsIContent* aEndChildNode) {
// The frame constructor guarantees that only ranges with the same parent
// arrive here in presence of dynamic changes to the page, see
// nsCSSFrameConstructor::IssueSingleInsertNotifications' callers.
nsINode* parent = aStartChildNode->GetFlattenedTreeParentNode();
if (!parent) {
return;
}
Accessible* container = mDocument->AccessibleOrTrueContainer(parent);
if (!container) {
return;
}
AutoTArray<nsCOMPtr<nsIContent>, 10> list;
for (nsIContent* node = aStartChildNode; node != aEndChildNode;
node = node->GetNextSibling()) {
MOZ_ASSERT(parent == node->GetFlattenedTreeParentNode());
// Notification triggers for content insertion even if no content was
// actually inserted (like if the content is display: none). Try to catch
// this case early.
if (node->GetPrimaryFrame() ||
(node->IsElement() && node->AsElement()->IsDisplayContents())) {
list.AppendElement(node);
}
}
if (!list.IsEmpty()) {
mContentInsertions.LookupOrAdd(container)->AppendElements(list);
Accessible* aContainer, nsTArray<nsCOMPtr<nsIContent>>& aInsertions) {
if (!aInsertions.IsEmpty()) {
mContentInsertions.LookupOrAdd(aContainer)->AppendElements(aInsertions);
ScheduleProcessing();
}
}
@ -827,18 +801,8 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
}
}
// Process only currently queued generic notifications.
nsTArray<RefPtr<Notification>> notifications;
notifications.SwapElements(mNotifications);
uint32_t notificationCount = notifications.Length();
for (uint32_t idx = 0; idx < notificationCount; idx++) {
notifications[idx]->Process();
if (!mDocument) return;
}
// Process invalidation list of the document after all accessible tree
// modification are done.
// mutation is done.
mDocument->ProcessInvalidationList();
// Process relocation list.
@ -852,6 +816,20 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
}
mRelocations.Clear();
// Process only currently queued generic notifications.
// These are used for processing aria-activedescendant, DOMMenuItemActive,
// etc. Therefore, they must be processed after relocations, since relocated
// subtrees might not have been created before relocation processing and the
// target might be inside a relocated subtree.
nsTArray<RefPtr<Notification>> notifications;
notifications.SwapElements(mNotifications);
uint32_t notificationCount = notifications.Length();
for (uint32_t idx = 0; idx < notificationCount; idx++) {
notifications[idx]->Process();
if (!mDocument) return;
}
// If a generic notification occurs after this point then we may be allowed to
// process it synchronously. However we do not want to reenter if fireing
// events causes script to run.
@ -924,6 +902,7 @@ void NotificationController::WillRefresh(mozilla::TimeStamp aTime) {
if (browserChild) {
static_cast<BrowserChild*>(browserChild.get())
->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
ipcDoc->SendPDocAccessiblePlatformExtConstructor();
}
#endif
}

View File

@ -190,8 +190,8 @@ class NotificationController final : public EventQueue,
/**
* Pend accessible tree update for content insertion.
*/
void ScheduleContentInsertion(nsIContent* aStartChildNode,
nsIContent* aEndChildNode);
void ScheduleContentInsertion(Accessible* aContainer,
nsTArray<nsCOMPtr<nsIContent>>& aInsertions);
/**
* Pend an accessible subtree relocation.

View File

@ -27,8 +27,7 @@ TreeWalker::TreeWalker(Accessible* aContext)
mChildFilter(nsIContent::eSkipPlaceholderContent),
mFlags(0),
mPhase(eAtStart) {
mChildFilter |=
mContext->NoXBLKids() ? nsIContent::eAllButXBL : nsIContent::eAllChildren;
mChildFilter |= nsIContent::eAllChildren;
mAnchorNode = mContext->IsDoc() ? mDoc->DocumentNode()->GetRootElement()
: mContext->GetContent();
@ -49,8 +48,7 @@ TreeWalker::TreeWalker(Accessible* aContext, nsIContent* aAnchorNode,
"This constructor cannot be used for tree creation");
MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
mChildFilter |=
mContext->NoXBLKids() ? nsIContent::eAllButXBL : nsIContent::eAllChildren;
mChildFilter |= nsIContent::eAllChildren;
MOZ_COUNT_CTOR(TreeWalker);
}
@ -100,10 +98,16 @@ bool TreeWalker::Seek(nsIContent* aChildNode) {
nsINode* parentNode = aChildNode;
do {
childNode = parentNode->AsContent();
parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
(mChildFilter & nsIContent::eAllButXBL)
? childNode->GetParentNode()
: childNode->GetFlattenedTreeParent();
parentNode = childNode->GetFlattenedTreeParent();
// Handle the special case of XBL binding child under a shadow root.
if (parentNode && parentNode->IsShadowRoot()) {
parentNode = childNode->GetFlattenedTreeParent();
if (parentNode == mAnchorNode) {
return true;
}
continue;
}
if (!parentNode || !parentNode->IsElement()) {
return false;

View File

@ -82,15 +82,6 @@ XULMAP(popup, [](Element* aElement, Accessible* aContext) {
return CreateMenupopupAccessible(aElement, aContext);
})
XULMAP(textbox, [](Element* aElement, Accessible* aContext) -> Accessible* {
if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::autocomplete, eIgnoreCase)) {
return new XULComboboxAccessible(aElement, aContext->Document());
}
return new EnumRoleAccessible<roles::SECTION>(aElement, aContext->Document());
})
XULMAP(tree, [](Element* aElement, Accessible* aContext) -> Accessible* {
nsIContent* child =
nsTreeUtils::GetDescendantChild(aElement, nsGkAtoms::treechildren);

View File

@ -79,7 +79,6 @@ LOCAL_INCLUDES += [
'/accessible/xpcom',
'/accessible/xul',
'/dom/base',
'/dom/xbl',
'/ipc/chromium/src',
'/layout/generic',
'/layout/style',

View File

@ -61,8 +61,6 @@
#include "nsTreeBodyFrame.h"
#include "nsTreeColumns.h"
#include "nsTreeUtils.h"
#include "nsXBLPrototypeBinding.h"
#include "nsXBLBinding.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/EventTarget.h"
@ -935,7 +933,7 @@ Accessible* nsAccessibilityService::CreateAccessible(nsINode* aNode,
if (!frame || !frame->StyleVisibility()->IsVisible()) {
// display:contents element doesn't have a frame, but retains the semantics.
// All its children are unaffected.
if (content->IsElement() && content->AsElement()->IsDisplayContents()) {
if (nsCoreUtils::IsDisplayContents(content)) {
const HTMLMarkupMapInfo* markupMap =
mHTMLMarkupMap.Get(content->NodeInfo()->NameAtom());
if (markupMap && markupMap->new_func) {
@ -1092,7 +1090,7 @@ Accessible* nsAccessibilityService::CreateAccessible(nsINode* aNode,
}
}
// Accessible XBL types and deck stuff are used in XUL only currently.
// XUL accessibles.
if (!newAcc && content->IsXULElement()) {
// No accessible for not selected deck panel and its children.
if (!aContext->IsXULTabpanels()) {
@ -1133,6 +1131,8 @@ Accessible* nsAccessibilityService::CreateAccessible(nsINode* aNode,
// polyline and image. A 'use' and 'text' graphic elements require
// special support.
newAcc = new EnumRoleAccessible<roles::GRAPHIC>(content, document);
} else if (content->IsSVGElement(nsGkAtoms::text)) {
newAcc = new HyperTextAccessibleWrap(content->AsElement(), document);
} else if (content->IsSVGElement(nsGkAtoms::svg)) {
newAcc = new EnumRoleAccessible<roles::DIAGRAM>(content, document);
}
@ -1528,10 +1528,13 @@ void nsAccessibilityService::RemoveNativeRootAccessible(
bool nsAccessibilityService::HasAccessible(nsINode* aDOMNode) {
if (!aDOMNode) return false;
DocAccessible* document = GetDocAccessible(aDOMNode->OwnerDoc());
Document* document = aDOMNode->OwnerDoc();
if (!document) return false;
return document->HasAccessible(aDOMNode);
DocAccessible* docAcc = GetExistingDocAccessible(aDOMNode->OwnerDoc());
if (!docAcc) return false;
return docAcc->HasAccessible(aDOMNode);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -544,3 +544,8 @@ void nsCoreUtils::DispatchAccEvent(RefPtr<nsIAccessibleEvent> event) {
obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
}
bool nsCoreUtils::IsDisplayContents(nsIContent* aContent) {
return aContent && aContent->IsElement() &&
aContent->AsElement()->IsDisplayContents();
}

View File

@ -322,6 +322,8 @@ class nsCoreUtils {
* Notify accessible event observers of an event.
*/
static void DispatchAccEvent(RefPtr<nsIAccessibleEvent> aEvent);
static bool IsDisplayContents(nsIContent* aContent);
};
#endif

View File

@ -4,8 +4,6 @@
#include "Accessible-inl.h"
#include "nsIXBLAccessible.h"
#include "EmbeddedObjCollector.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
@ -28,7 +26,6 @@
#include "TableAccessible.h"
#include "TableCellAccessible.h"
#include "TreeWalker.h"
#include "XULDocument.h"
#include "nsIDOMXULButtonElement.h"
#include "nsIDOMXULSelectCntrlEl.h"
@ -131,12 +128,6 @@ ENameValueFlag Accessible::Name(nsString& aName) const {
ARIAName(aName);
if (!aName.IsEmpty()) return eNameOK;
nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
if (xblAccessible) {
xblAccessible->GetAccessibleName(aName);
if (!aName.IsEmpty()) return eNameOK;
}
ENameValueFlag nameFlag = NativeName(aName);
if (!aName.IsEmpty()) return nameFlag;
@ -229,8 +220,8 @@ KeyBinding Accessible::AccessKey() const {
HTMLLabelIterator iter(Document(), this,
HTMLLabelIterator::eSkipAncestorLabel);
label = iter.Next();
} else if (mContent->IsXULElement()) {
}
if (!label) {
XULLabelIterator iter(Document(), mContent);
label = iter.Next();
}
@ -303,7 +294,7 @@ uint64_t Accessible::VisibilityState() const {
if (!frame) {
// Element having display:contents is considered visible semantically,
// despite it doesn't have a visually visible box.
if (mContent->IsElement() && mContent->AsElement()->IsDisplayContents()) {
if (nsCoreUtils::IsDisplayContents(mContent)) {
return states::OFFSCREEN;
}
return states::INVISIBLE;
@ -726,6 +717,22 @@ void Accessible::TakeFocus() const {
}
}
void Accessible::NameFromAssociatedXULLabel(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName) {
Accessible* label = nullptr;
XULLabelIterator iter(aDocument, aElm);
while ((label = iter.Next())) {
// Check if label's value attribute is used
label->Elm()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
if (aName.IsEmpty()) {
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
nsTextEquivUtils::AppendTextEquivFromContent(label, label->Elm(), &aName);
}
}
aName.CompressWhitespace();
}
void Accessible::XULElmName(DocAccessible* aDocument, nsIContent* aElm,
nsString& aName) {
/**
@ -759,47 +766,10 @@ void Accessible::XULElmName(DocAccessible* aDocument, nsIContent* aElm,
// CASES #2 and #3 ------ label as a child or <label control="id" ... >
// </label>
if (aName.IsEmpty()) {
Accessible* label = nullptr;
XULLabelIterator iter(aDocument, aElm);
while ((label = iter.Next())) {
// Check if label's value attribute is used
label->Elm()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
if (aName.IsEmpty()) {
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
nsTextEquivUtils::AppendTextEquivFromContent(label, label->Elm(),
&aName);
}
}
NameFromAssociatedXULLabel(aDocument, aElm, aName);
}
aName.CompressWhitespace();
if (!aName.IsEmpty()) return;
// Can get text from title of <toolbaritem> if we're a child of a
// <toolbaritem>
nsIContent* bindingParent = aElm->GetBindingParent();
nsIContent* parent =
bindingParent ? bindingParent->GetParent() : aElm->GetParent();
nsAutoString ancestorTitle;
while (parent) {
if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
parent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::title,
ancestorTitle)) {
// Before returning this, check if the element itself has a tooltip:
if (aElm->IsElement() &&
aElm->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext,
aName)) {
aName.CompressWhitespace();
return;
}
aName.Assign(ancestorTitle);
aName.CompressWhitespace();
return;
}
parent = parent->GetParent();
}
}
nsresult Accessible::HandleAccEvent(AccEvent* aEvent) {
@ -1591,9 +1561,8 @@ Relation Accessible::RelationByType(RelationType aType) const {
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_labelledby));
if (mContent->IsHTMLElement()) {
rel.AppendIter(new HTMLLabelIterator(Document(), this));
} else if (mContent->IsXULElement()) {
rel.AppendIter(new XULLabelIterator(Document(), mContent));
}
rel.AppendIter(new XULLabelIterator(Document(), mContent));
return rel;
}
@ -1718,11 +1687,10 @@ Relation Accessible::RelationByType(RelationType aType) const {
// In XUL, use first <button default="true" .../> in the document
dom::Document* doc = mContent->OwnerDoc();
nsIContent* buttonEl = nullptr;
if (doc->IsXULDocument()) {
dom::XULDocument* xulDoc = doc->AsXULDocument();
if (doc->AllowXULXBL()) {
nsCOMPtr<nsIHTMLCollection> possibleDefaultButtons =
xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
NS_LITERAL_STRING("true"));
doc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
NS_LITERAL_STRING("true"));
if (possibleDefaultButtons) {
uint32_t length = possibleDefaultButtons->Length();
// Check for button in list of default="true" elements
@ -1736,21 +1704,6 @@ Relation Accessible::RelationByType(RelationType aType) const {
}
}
}
if (!buttonEl) { // Check for anonymous accept button in <dialog>
dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
if (rootElm) {
nsIContent* possibleButtonEl =
rootElm->OwnerDoc()->GetAnonymousElementByAttribute(
rootElm, nsGkAtoms::_default, NS_LITERAL_STRING("true"));
if (possibleButtonEl && possibleButtonEl->IsElement()) {
RefPtr<nsIDOMXULButtonElement> button =
possibleButtonEl->AsElement()->AsXULButton();
if (button) {
buttonEl = possibleButtonEl;
}
}
}
}
return Relation(mDoc, buttonEl);
}
}
@ -1895,7 +1848,7 @@ void Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
nsIFrame* frame = GetFrame();
if (!frame) {
if (mContent->IsElement() && mContent->AsElement()->IsDisplayContents()) {
if (nsCoreUtils::IsDisplayContents(mContent)) {
aText += kEmbeddedObjectChar;
}
return;
@ -1966,6 +1919,11 @@ ENameValueFlag Accessible::NativeName(nsString& aName) const {
if (!aName.IsEmpty()) return eNameOK;
NameFromAssociatedXULLabel(mDoc, mContent, aName);
if (!aName.IsEmpty()) {
return eNameOK;
}
nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
}
@ -2430,8 +2388,7 @@ Accessible* Accessible::CurrentItem() const {
if (HasOwnContent() && mContent->IsElement() &&
mContent->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::aria_activedescendant, id)) {
dom::Document* DOMDoc = mContent->OwnerDoc();
dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
dom::Element* activeDescendantElm = IDRefsIterator::GetElem(mContent, id);
if (activeDescendantElm) {
if (mContent->IsInclusiveDescendantOf(activeDescendantElm)) {
// Don't want a cyclical descendant relationship. That would be bad.

View File

@ -917,12 +917,6 @@ class Accessible : public nsISupports {
mStateFlags &= ~eRelocated;
}
/**
* Return true if the accessible doesn't allow accessible children from XBL
* anonymous subtree.
*/
bool NoXBLKids() const { return mStateFlags & eNoXBLKids; }
/**
* Return true if the accessible allows accessible children from subtree of
* a DOM element of this accessible.
@ -1028,11 +1022,10 @@ class Accessible : public nsISupports {
eKidsMutating = 1 << 6, // subtree is being mutated
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
eRelocated = 1 << 8, // accessible was moved in tree
eNoXBLKids = 1 << 9, // accessible don't allows XBL children
eNoKidsFromDOM = 1 << 10, // accessible doesn't allow children from DOM
eHasTextKids = 1 << 11, // accessible have a text leaf in children
eNoKidsFromDOM = 1 << 9, // accessible doesn't allow children from DOM
eHasTextKids = 1 << 10, // accessible have a text leaf in children
eLastStateFlag = eNoKidsFromDOM
eLastStateFlag = eHasTextKids
};
/**
@ -1063,6 +1056,13 @@ class Accessible : public nsISupports {
*/
void ARIAName(nsString& aName) const;
/**
* Returns the accessible name specified for this control using XUL
* <label control="id" ...>.
*/
static void NameFromAssociatedXULLabel(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName);
/**
* Return the name for XUL element.
*/
@ -1136,7 +1136,7 @@ class Accessible : public nsISupports {
nsTArray<Accessible*> mChildren;
int32_t mIndexInParent;
static const uint8_t kStateFlagsBits = 12;
static const uint8_t kStateFlagsBits = 11;
static const uint8_t kContextFlagsBits = 2;
static const uint8_t kTypeBits = 6;
static const uint8_t kGenericTypesBits = 16;

View File

@ -93,10 +93,6 @@ DocAccessible::DocAccessible(dom::Document* aDocument,
MOZ_ASSERT(mPresShell, "should have been given a pres shell");
mPresShell->SetDocAccessible(this);
// If this is a XUL Document, it should not implement nsHyperText
if (mDocumentNode && mDocumentNode->IsXULDocument())
mGenericTypes &= ~eHyperText;
}
DocAccessible::~DocAccessible() {
@ -198,10 +194,6 @@ role DocAccessible::NativeRole() const {
return roles::CHROME_WINDOW;
if (itemType == nsIDocShellTreeItem::typeContent) {
#ifdef MOZ_XUL
if (mDocumentNode && mDocumentNode->IsXULDocument())
return roles::APPLICATION;
#endif
return roles::DOCUMENT;
}
} else if (itemType == nsIDocShellTreeItem::typeContent) {
@ -343,13 +335,6 @@ void DocAccessible::URL(nsAString& aURL) const {
}
void DocAccessible::DocType(nsAString& aType) const {
#ifdef MOZ_XUL
if (mDocumentNode->IsXULDocument()) {
aType.AssignLiteral("window"); // doctype not implemented for XUL at time
// of writing - causes assertion
return;
}
#endif
dom::DocumentType* docType = mDocumentNode->GetDoctype();
if (docType) docType->GetPublicId(aType);
}
@ -686,7 +671,19 @@ void DocAccessible::AttributeWillChange(dom::Element* aElement,
}
void DocAccessible::NativeAnonymousChildListChange(nsIContent* aContent,
bool aIsRemove) {}
bool aIsRemove) {
if (aIsRemove) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin("TREE", "Anonymous content removed; doc: %p", this);
logging::Node("node", aContent);
logging::MsgEnd();
}
#endif
ContentRemoved(aContent);
}
}
void DocAccessible::AttributeChanged(dom::Element* aElement,
int32_t aNameSpaceID, nsAtom* aAttribute,
@ -994,7 +991,7 @@ void DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible) {
nsAutoString id;
if (elm->AsElement()->GetAttr(kNameSpaceID_None,
nsGkAtoms::aria_activedescendant, id)) {
dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
dom::Element* activeDescendantElm = IDRefsIterator::GetElem(elm, id);
if (activeDescendantElm) {
Accessible* activeDescendant = GetAccessible(activeDescendantElm);
if (activeDescendant) {
@ -1267,12 +1264,148 @@ void DocAccessible::ContentInserted(nsIContent* aStartChildNode,
nsIContent* aEndChildNode) {
// Ignore content insertions until we constructed accessible tree. Otherwise
// schedule tree update on content insertion after layout.
if (mNotificationController && HasLoadState(eTreeConstructed)) {
// Update the whole tree of this document accessible when the container is
// null (document element is inserted or removed).
mNotificationController->ScheduleContentInsertion(aStartChildNode,
aEndChildNode);
if (!mNotificationController || !HasLoadState(eTreeConstructed)) {
return;
}
// The frame constructor guarantees that only ranges with the same parent
// arrive here in presence of dynamic changes to the page, see
// nsCSSFrameConstructor::IssueSingleInsertNotifications' callers.
nsINode* parent = aStartChildNode->GetFlattenedTreeParentNode();
if (!parent) {
return;
}
Accessible* container = AccessibleOrTrueContainer(parent);
if (!container) {
return;
}
AutoTArray<nsCOMPtr<nsIContent>, 10> list;
for (nsIContent* node = aStartChildNode; node != aEndChildNode;
node = node->GetNextSibling()) {
MOZ_ASSERT(parent == node->GetFlattenedTreeParentNode());
if (PruneOrInsertSubtree(node)) {
list.AppendElement(node);
}
}
mNotificationController->ScheduleContentInsertion(container, list);
}
bool DocAccessible::PruneOrInsertSubtree(nsIContent* aRoot) {
bool insert = false;
// In the case that we are, or are in, a shadow host, we need to assure
// some accessibles are removed if they are not rendered anymore.
nsIContent* shadowHost =
aRoot->GetShadowRoot() ? aRoot : aRoot->GetContainingShadowHost();
if (shadowHost) {
dom::ExplicitChildIterator iter(shadowHost);
// Check all explicit children in the host, if they are not slotted
// then remove their accessibles and subtrees.
while (nsIContent* childNode = iter.GetNextChild()) {
if (!childNode->GetPrimaryFrame() &&
!nsCoreUtils::IsDisplayContents(childNode)) {
ContentRemoved(childNode);
}
}
// If this is a slot, check to see if its fallback content is rendered,
// if not - remove it.
if (aRoot->IsHTMLElement(nsGkAtoms::slot)) {
for (nsIContent* childNode = aRoot->GetFirstChild(); childNode;
childNode = childNode->GetNextSibling()) {
if (!childNode->GetPrimaryFrame() &&
!nsCoreUtils::IsDisplayContents(childNode)) {
ContentRemoved(childNode);
}
}
}
}
// If we already have an accessible, check if we need to remove it, recreate
// it, or keep it in place.
Accessible* acc = GetAccessible(aRoot);
if (acc) {
MOZ_ASSERT(aRoot == acc->GetContent(), "Accessible has differing content!");
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin(
"TREE", "inserted content already has accessible; doc: %p", this);
logging::Node("content node", aRoot);
logging::AccessibleInfo("accessible node", acc);
logging::MsgEnd();
}
#endif
nsIFrame* frame = acc->GetFrame();
// Accessible has no frame and it's not display:contents. Remove it.
// As well as removing the a11y subtree, we must also remove Accessibles
// for DOM descendants, since some of these might be relocated Accessibles
// and their DOM nodes are now hidden as well.
if (!frame && !nsCoreUtils::IsDisplayContents(aRoot)) {
ContentRemoved(aRoot);
return false;
}
// If it's a XULLabel it was probably reframed because a `value` attribute
// was added. The accessible creates its text leaf upon construction, so we
// need to recreate. Remove it, and schedule for reconstruction.
if (acc->IsXULLabel()) {
ContentRemoved(acc);
return true;
}
// It is a broken image that is being reframed because it either got
// or lost an `alt` tag that would rerender this node as text.
if (frame && (acc->IsImage() != (frame->AccessibleType() == eImageType))) {
ContentRemoved(aRoot);
return true;
}
// The accessible can be reparented or reordered in its parent.
// We schedule it for reinsertion. For example, a slotted element
// can change its slot attribute to a different slot.
insert = true;
} else {
// If there is no current accessible, and the node has a frame, or is
// display:contents, schedule it for insertion.
if (aRoot->GetPrimaryFrame() || nsCoreUtils::IsDisplayContents(aRoot)) {
// This may be a new subtree, the insertion process will recurse through
// its descendants.
if (!GetAccessibleOrDescendant(aRoot)) {
return true;
}
// Content is not an accessible, but has accessible descendants.
// We schedule this container for insertion strictly for the case where it
// itself now needs an accessible. We will still need to recurse into the
// descendant content to prune accessibles, and in all likelyness to
// insert accessibles since accessible insertions will likeley get missed
// in an existing subtree.
insert = true;
}
}
if (Accessible* container = AccessibleOrTrueContainer(aRoot)) {
AutoTArray<nsCOMPtr<nsIContent>, 10> list;
dom::AllChildrenIterator iter =
dom::AllChildrenIterator(aRoot, nsIContent::eAllChildren, true);
while (nsIContent* childNode = iter.GetNextChild()) {
if (PruneOrInsertSubtree(childNode)) {
list.AppendElement(childNode);
}
}
if (!list.IsEmpty()) {
mNotificationController->ScheduleContentInsertion(container, list);
}
}
return insert;
}
void DocAccessible::RecreateAccessible(nsIContent* aContent) {
@ -1394,6 +1527,9 @@ void DocAccessible::DoInitialUpdate() {
#endif
browserChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
childID, holder);
#if !defined(XP_WIN)
ipcDoc->SendPDocAccessiblePlatformExtConstructor();
#endif
}
if (IsRoot()) {
@ -1603,6 +1739,12 @@ bool DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return true;
}
if (aAttribute == nsGkAtoms::type) {
// If the input[type] changes, we should recreate the accessible.
RecreateAccessible(aElement);
return true;
}
return false;
}
@ -1659,6 +1801,7 @@ class InsertIterator final {
TreeWalker mWalker;
const nsTArray<nsCOMPtr<nsIContent> >* mNodes;
nsTHashtable<nsPtrHashKey<const nsIContent>> mProcessedNodes;
uint32_t mNodesIdx;
};
@ -1684,7 +1827,15 @@ bool InsertIterator::Next() {
// what means there's no container. Ignore the insertion too.
nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1);
nsIContent* node = mNodes->ElementAt(mNodesIdx++);
Accessible* container = Document()->AccessibleOrTrueContainer(node, true);
// Check to see if we already processed this node with this iterator.
// this can happen if we get two redundant insertions in the case of a
// text and frame insertion.
if (!mProcessedNodes.EnsureInserted(node)) {
continue;
}
Accessible* container =
Document()->AccessibleOrTrueContainer(node->GetParentNode(), true);
if (container != Context()) {
continue;
}
@ -1753,20 +1904,17 @@ void DocAccessible::ProcessContentInserted(
do {
Accessible* parent = iter.Child()->Parent();
if (parent) {
if (parent != aContainer) {
Accessible* previousSibling = iter.ChildBefore();
if (parent != aContainer ||
iter.Child()->PrevSibling() != previousSibling) {
#ifdef A11Y_LOG
logging::TreeInfo("stealing accessible", 0, "old parent", parent,
logging::TreeInfo("relocating accessible", 0, "old parent", parent,
"new parent", aContainer, "child", iter.Child(),
nullptr);
#endif
MOZ_ASSERT_UNREACHABLE("stealing accessible");
continue;
MoveChild(iter.Child(), aContainer,
previousSibling ? previousSibling->IndexInParent() + 1 : 0);
}
#ifdef A11Y_LOG
logging::TreeInfo("binding to same parent", logging::eVerbose, "parent",
aContainer, "child", iter.Child(), nullptr);
#endif
continue;
}
@ -2101,7 +2249,7 @@ void DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
}
}
aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
aChildren->RemoveLastElements(aChildren->Length() - aStartIdx);
}
bool DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,

View File

@ -325,10 +325,6 @@ class DocAccessible : public HyperTextAccessibleWrap,
/**
* Return true if the given ID is referred by relation attribute.
*
* @note Different elements may share the same ID if they are hosted inside
* XBL bindings. Be careful the result of this method may be senseless
* while it's called for XUL elements (where XBL is used widely).
*/
bool IsDependentID(dom::Element* aElement, const nsAString& aID) const {
return GetRelProviders(aElement, aID);
@ -595,6 +591,19 @@ class DocAccessible : public HyperTextAccessibleWrap,
*/
void ARIAActiveDescendantIDMaybeMoved(dom::Element* aElm);
/**
* Traverse content subtree and for each node do one of 3 things:
* 1. Check if content node has an accessible that should be removed and
* remove it.
* 2. Check if content node has an accessible that needs to be recreated.
* Remove it and schedule it for reinsertion.
* 3. Check if content node has no accessible but needs one. Schedule one for
* insertion.
*
* Returns true if the root node should be reinserted.
*/
bool PruneOrInsertSubtree(nsIContent* aRoot);
protected:
/**
* State and property flags, kept by mDocFlags.

View File

@ -1792,18 +1792,20 @@ void HyperTextAccessible::Shutdown() {
}
bool HyperTextAccessible::RemoveChild(Accessible* aAccessible) {
int32_t childIndex = aAccessible->IndexInParent();
int32_t count = mOffsets.Length() - childIndex;
if (count > 0) mOffsets.RemoveElementsAt(childIndex, count);
const int32_t childIndex = aAccessible->IndexInParent();
if (childIndex < static_cast<int64_t>(mOffsets.Length())) {
mOffsets.RemoveLastElements(mOffsets.Length() -
aAccessible->IndexInParent());
}
return AccessibleWrap::RemoveChild(aAccessible);
}
bool HyperTextAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild) {
int32_t count = mOffsets.Length() - aIndex;
if (count > 0) {
mOffsets.RemoveElementsAt(aIndex, count);
if (aIndex < mOffsets.Length()) {
mOffsets.RemoveLastElements(mOffsets.Length() - aIndex);
}
return AccessibleWrap::InsertChildAt(aIndex, aChild);
}

View File

@ -41,7 +41,7 @@
#include "nsGlobalWindow.h"
#ifdef MOZ_XUL
# include "nsIXULWindow.h"
# include "nsIAppWindow.h"
#endif
using namespace mozilla;
@ -78,33 +78,23 @@ ENameValueFlag RootAccessible::Name(nsString& aName) const {
return eNameOK;
}
role RootAccessible::NativeRole() const {
// If it's a <dialog> or <wizard>, use roles::DIALOG instead
dom::Element* rootElm = mDocumentNode->GetRootElement();
if (rootElm &&
rootElm->IsAnyOfXULElements(nsGkAtoms::dialog, nsGkAtoms::wizard))
return roles::DIALOG;
return DocAccessibleWrap::NativeRole();
}
// RootAccessible protected member
#ifdef MOZ_XUL
uint32_t RootAccessible::GetChromeFlags() const {
// Return the flag set for the top level window as defined
// by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
// Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
// Not simple: nsIAppWindow is not just a QI from nsIDOMWindow
nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
NS_ENSURE_TRUE(docShell, 0);
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
NS_ENSURE_TRUE(treeOwner, 0);
nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
if (!xulWin) {
nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwner));
if (!appWin) {
return 0;
}
uint32_t chromeFlags;
xulWin->GetChromeFlags(&chromeFlags);
appWin->GetChromeFlags(&chromeFlags);
return chromeFlags;
}
#endif

View File

@ -29,7 +29,6 @@ class RootAccessible : public DocAccessibleWrap, public nsIDOMEventListener {
virtual void Shutdown() override;
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
virtual Relation RelationByType(RelationType aType) const override;
virtual mozilla::a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;
// RootAccessible

View File

@ -163,11 +163,11 @@ role HTMLHeaderOrFooterAccessible::NativeRole() const {
// If other sectioning or sectioning root elements, they become sections.
nsIContent* parent = mContent->GetParent();
while (parent) {
if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside,
nsGkAtoms::nav, nsGkAtoms::section,
nsGkAtoms::blockquote, nsGkAtoms::details,
nsGkAtoms::dialog, nsGkAtoms::fieldset,
nsGkAtoms::figure, nsGkAtoms::td)) {
if (parent->IsAnyOfHTMLElements(
nsGkAtoms::article, nsGkAtoms::aside, nsGkAtoms::nav,
nsGkAtoms::section, nsGkAtoms::main, nsGkAtoms::blockquote,
nsGkAtoms::details, nsGkAtoms::dialog, nsGkAtoms::fieldset,
nsGkAtoms::figure, nsGkAtoms::td)) {
break;
}
parent = parent->GetParent();

View File

@ -98,17 +98,16 @@ class HTMLTextFieldAccessible : public HyperTextAccessibleWrap {
virtual ENameValueFlag NativeName(nsString& aName) const override;
/**
* Return a widget element this input is part of, for example, XUL:textbox or
* HTML:input@type="number".
* Return a widget element this input is part of, for example, search-textbox.
*
* FIXME: This should probably be renamed.
*/
nsIContent* BindingOrWidgetParent() const {
nsIContent* el = mContent->GetBindingParent();
if (el) {
if (auto* el = mContent->GetClosestNativeAnonymousSubtreeRootParent()) {
return el;
}
// XUL textboxes custom elements implementation.
ErrorResult rv;
return Elm()->Closest(NS_LITERAL_STRING("textbox"), rv);
// XUL search-textbox custom element
return Elm()->Closest(NS_LITERAL_STRING("search-textbox"), IgnoreErrors());
}
};

View File

@ -34,7 +34,6 @@ XPIDL_SOURCES += [
'nsIAccessibleTypes.idl',
'nsIAccessibleValue.idl',
'nsIAccessibleVirtualCursorChangeEvent.idl',
'nsIXBLAccessible.idl',
]
XPIDL_MODULE = 'accessibility'

View File

@ -1,18 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
/**
* XBL controls can implement this interface to provide own implementation of
* accessible properties.
*/
[scriptable, builtinclass, uuid(3716eb86-166b-445b-a94a-9b522fee96e6)]
interface nsIXBLAccessible : nsISupports
{
/**
* Return accessible name.
*/
readonly attribute AString accessibleName;
};

View File

@ -17,6 +17,8 @@
# include "mozilla/mscom/Ptr.h"
# include "nsWinUtils.h"
# include "RootAccessible.h"
#else
# include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
#endif
namespace mozilla {
@ -818,6 +820,23 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvBatch(
# endif // defined(XP_WIN)
return IPC_OK();
}
bool DocAccessibleParent::DeallocPDocAccessiblePlatformExtParent(
PDocAccessiblePlatformExtParent* aActor) {
delete aActor;
return true;
}
PDocAccessiblePlatformExtParent*
DocAccessibleParent::AllocPDocAccessiblePlatformExtParent() {
return new DocAccessiblePlatformExtParent();
}
DocAccessiblePlatformExtParent* DocAccessibleParent::GetPlatformExtension() {
return static_cast<DocAccessiblePlatformExtParent*>(
SingleManagedOrNull(ManagedPDocAccessiblePlatformExtParent()));
}
#endif // !defined(XP_WIN)
} // namespace a11y

View File

@ -17,6 +17,10 @@ namespace a11y {
class xpcAccessibleGeneric;
#if !defined(XP_WIN)
class DocAccessiblePlatformExtParent;
#endif
/*
* These objects live in the main process and comunicate with and represent
* an accessible document in a content process.
@ -225,6 +229,14 @@ class DocAccessibleParent : public ProxyAccessible,
#if !defined(XP_WIN)
virtual mozilla::ipc::IPCResult RecvBatch(
const uint64_t& aBatchType, nsTArray<BatchData>&& aData) override;
virtual bool DeallocPDocAccessiblePlatformExtParent(
PDocAccessiblePlatformExtParent* aActor) override;
virtual PDocAccessiblePlatformExtParent*
AllocPDocAccessiblePlatformExtParent() override;
DocAccessiblePlatformExtParent* GetPlatformExtension();
#endif
private:

View File

@ -0,0 +1,82 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocAccessiblePlatformExtChild.h"
#include "DocAccessibleChild.h"
#include "AccessibleWrap.h"
namespace mozilla {
namespace a11y {
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvPivot(
uint64_t aID, int32_t aGranularity, bool aForward, bool aInclusive) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->Pivot(aGranularity, aForward, aInclusive);
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvNavigateText(
uint64_t aID, int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset,
bool aForward, bool aSelect) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->NavigateText(aGranularity, aStartOffset, aEndOffset, aForward,
aSelect);
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvSetSelection(
uint64_t aID, int32_t aStart, int32_t aEnd) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->SetSelection(aStart, aEnd);
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvCut(uint64_t aID) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->Cut();
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvCopy(uint64_t aID) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->Copy();
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvPaste(uint64_t aID) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->Paste();
}
return IPC_OK();
}
mozilla::ipc::IPCResult DocAccessiblePlatformExtChild::RecvExploreByTouch(
uint64_t aID, float aX, float aY) {
if (auto acc = IdToAccessibleWrap(aID)) {
acc->ExploreByTouch(aX, aY);
}
return IPC_OK();
}
AccessibleWrap* DocAccessiblePlatformExtChild::IdToAccessibleWrap(
const uint64_t& aID) const {
return static_cast<AccessibleWrap*>(
static_cast<DocAccessibleChild*>(Manager())->IdToAccessible(aID));
}
} // namespace a11y
} // namespace mozilla

View File

@ -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/. */
#ifndef mozilla_a11y_DocAccessiblePlatformExtChild_h
#define mozilla_a11y_DocAccessiblePlatformExtChild_h
#include "mozilla/a11y/PDocAccessiblePlatformExtChild.h"
namespace mozilla {
namespace a11y {
class AccessibleWrap;
class DocAccessibleChild;
class DocAccessiblePlatformExtChild : public PDocAccessiblePlatformExtChild {
public:
mozilla::ipc::IPCResult RecvPivot(uint64_t aID, int32_t aGranularity,
bool aForward, bool aInclusive);
mozilla::ipc::IPCResult RecvNavigateText(uint64_t aID, int32_t aGranularity,
int32_t aStartOffset,
int32_t aEndOffset, bool aForward,
bool aSelect);
mozilla::ipc::IPCResult RecvSetSelection(uint64_t aID, int32_t aStart,
int32_t aEnd);
mozilla::ipc::IPCResult RecvCut(uint64_t aID);
mozilla::ipc::IPCResult RecvCopy(uint64_t aID);
mozilla::ipc::IPCResult RecvPaste(uint64_t aID);
mozilla::ipc::IPCResult RecvExploreByTouch(uint64_t aID, float aX, float aY);
private:
AccessibleWrap* IdToAccessibleWrap(const uint64_t& aID) const;
};
} // namespace a11y
} // namespace mozilla
#endif

View File

@ -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/. */
#ifndef mozilla_a11y_DocAccessiblePlatformExtParent_h
#define mozilla_a11y_DocAccessiblePlatformExtParent_h
#include "mozilla/a11y/PDocAccessiblePlatformExtParent.h"
namespace mozilla {
namespace a11y {
class DocAccessiblePlatformExtParent : public PDocAccessiblePlatformExtParent {
};
} // namespace a11y
} // namespace mozilla
#endif

View File

@ -0,0 +1,31 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PDocAccessible;
namespace mozilla {
namespace a11y {
protocol PDocAccessiblePlatformExt {
manager PDocAccessible;
child:
async __delete__();
async Pivot(uint64_t aID, int32_t aGranularity, bool aForward, bool aInclusive);
async NavigateText(uint64_t aID, int32_t aGranularity, int32_t aStartOffset, int32_t aEndOffset, bool aForward, bool aSelect);
async SetSelection(uint64_t aID, int32_t aStart, int32_t aEnd);
async Cut(uint64_t aID);
async Copy(uint64_t aID);
async Paste(uint64_t aID);
async ExploreByTouch(uint64_t aID, float aX, float aY);
};
}
}

View File

@ -0,0 +1,27 @@
# 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/.
# With --disable-accessibility, we need to compile PDocAccessiblePlatformExt.ipdl, but
# not the C++.
IPDL_SOURCES += ['PDocAccessiblePlatformExt.ipdl']
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'DocAccessiblePlatformExtChild.h',
'DocAccessiblePlatformExtParent.h',
]
SOURCES += [
'DocAccessiblePlatformExtChild.cpp',
]
LOCAL_INCLUDES += [
'/accessible/android',
'/accessible/ipc/other',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -2,5 +2,7 @@
# 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/.
toolkit.jar:
content/global/accessibility/content-script.js (content-script.js)
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
DIRS += ['android']
else:
DIRS += ['other']

View File

@ -0,0 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_DocAccessiblePlatformExtChild_h
#define mozilla_a11y_DocAccessiblePlatformExtChild_h
#include "mozilla/a11y/PDocAccessiblePlatformExtChild.h"
namespace mozilla {
namespace a11y {
class DocAccessibleChild;
class DocAccessiblePlatformExtChild : public PDocAccessiblePlatformExtChild {};
} // namespace a11y
} // namespace mozilla
#endif

View File

@ -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/. */
#ifndef mozilla_a11y_DocAccessiblePlatformExtParent_h
#define mozilla_a11y_DocAccessiblePlatformExtParent_h
#include "mozilla/a11y/PDocAccessiblePlatformExtParent.h"
namespace mozilla {
namespace a11y {
class DocAccessiblePlatformExtParent : public PDocAccessiblePlatformExtParent {
};
} // namespace a11y
} // namespace mozilla
#endif

View File

@ -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/. */
include protocol PDocAccessible;
namespace mozilla {
namespace a11y {
protocol PDocAccessiblePlatformExt {
manager PDocAccessible;
child:
async __delete__();
};
}}

View File

@ -0,0 +1,18 @@
# 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/.
# With --disable-accessibility, we need to compile PDocAccessiblePlatformExt.ipdl, but
# not the C++.
IPDL_SOURCES += ['PDocAccessiblePlatformExt.ipdl']
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'DocAccessiblePlatformExtChild.h',
'DocAccessiblePlatformExtParent.h',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -10,7 +10,7 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'/accessible/windows/msaa',
]
else:
DIRS += ['other']
DIRS += ['other', 'extension']
LOCAL_INCLUDES += [
'/accessible/ipc/other',
]

View File

@ -19,6 +19,7 @@
# include "AccessibleWrap.h"
#endif
#include "mozilla/PresShell.h"
#include "mozilla/a11y/DocAccessiblePlatformExtChild.h"
namespace mozilla {
namespace a11y {
@ -1677,5 +1678,16 @@ mozilla::ipc::IPCResult DocAccessibleChild::RecvRestoreFocus() {
return IPC_OK();
}
bool DocAccessibleChild::DeallocPDocAccessiblePlatformExtChild(
PDocAccessiblePlatformExtChild* aActor) {
delete aActor;
return true;
}
PDocAccessiblePlatformExtChild*
DocAccessibleChild::AllocPDocAccessiblePlatformExtChild() {
return new DocAccessiblePlatformExtChild();
}
} // namespace a11y
} // namespace mozilla

View File

@ -11,6 +11,7 @@ namespace mozilla {
namespace a11y {
class Accessible;
class DocAccessiblePlatformExtChild;
class HyperTextAccessible;
class TextLeafAccessible;
class ImageAccessible;
@ -22,6 +23,8 @@ class TableCellAccessible;
* and their lifetime is the same as the document they represent.
*/
class DocAccessibleChild : public DocAccessibleChildBase {
friend DocAccessiblePlatformExtChild;
public:
DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
: DocAccessibleChildBase(aDoc) {
@ -469,6 +472,12 @@ class DocAccessibleChild : public DocAccessibleChildBase {
virtual mozilla::ipc::IPCResult RecvDOMNodeID(const uint64_t& aID,
nsString* aDOMNodeID) override;
virtual bool DeallocPDocAccessiblePlatformExtChild(
PDocAccessiblePlatformExtChild* aActor) override;
virtual PDocAccessiblePlatformExtChild* AllocPDocAccessiblePlatformExtChild()
override;
private:
Accessible* IdToAccessible(const uint64_t& aID) const;
Accessible* IdToAccessibleLink(const uint64_t& aID) const;

View File

@ -4,6 +4,7 @@
include protocol PFileDescriptorSet;
include protocol PBrowser;
include protocol PDocAccessiblePlatformExt;
include "mozilla/GfxMessageUtils.h";
@ -69,8 +70,10 @@ struct RelationTargets
nested(upto inside_sync) sync protocol PDocAccessible
{
manager PBrowser;
manages PDocAccessiblePlatformExt;
parent:
async PDocAccessiblePlatformExt();
async Shutdown();
/*

View File

@ -1,355 +0,0 @@
/* 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";
var EXPORTED_SYMBOLS = ["AccessFu"];
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Logger, Utils } = ChromeUtils.import(
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Rect",
"resource://gre/modules/Geometry.jsm"
);
const GECKOVIEW_MESSAGE = {
ACTIVATE: "GeckoView:AccessibilityActivate",
BY_GRANULARITY: "GeckoView:AccessibilityByGranularity",
CLIPBOARD: "GeckoView:AccessibilityClipboard",
CURSOR_TO_FOCUSED: "GeckoView:AccessibilityCursorToFocused",
EXPLORE_BY_TOUCH: "GeckoView:AccessibilityExploreByTouch",
LONG_PRESS: "GeckoView:AccessibilityLongPress",
NEXT: "GeckoView:AccessibilityNext",
PREVIOUS: "GeckoView:AccessibilityPrevious",
SCROLL_BACKWARD: "GeckoView:AccessibilityScrollBackward",
SCROLL_FORWARD: "GeckoView:AccessibilityScrollForward",
SET_SELECTION: "GeckoView:AccessibilitySetSelection",
VIEW_FOCUSED: "GeckoView:AccessibilityViewFocused",
CLEAR_CURSOR: "GeckoView:AccessibilityClearCursor",
};
const ACCESSFU_MESSAGE = {
DOSCROLL: "AccessFu:DoScroll",
};
const FRAME_SCRIPT = "chrome://global/content/accessibility/content-script.js";
var AccessFu = {
/**
* A lazy getter for event handler that binds the scope to AccessFu object.
*/
get handleEvent() {
delete this.handleEvent;
this.handleEvent = this._handleEvent.bind(this);
return this.handleEvent;
},
/**
* Start AccessFu mode.
*/
enable: function enable() {
if (this._enabled) {
return;
}
this._enabled = true;
Services.obs.addObserver(this, "remote-browser-shown");
Services.obs.addObserver(this, "inprocess-browser-shown");
Services.ww.registerNotification(this);
for (let win of Services.wm.getEnumerator(null)) {
this._attachWindow(win);
}
Logger.info("AccessFu:Enabled");
},
/**
* Disable AccessFu and return to default interaction mode.
*/
disable: function disable() {
if (!this._enabled) {
return;
}
this._enabled = false;
Services.obs.removeObserver(this, "remote-browser-shown");
Services.obs.removeObserver(this, "inprocess-browser-shown");
Services.ww.unregisterNotification(this);
for (let win of Services.wm.getEnumerator(null)) {
this._detachWindow(win);
}
if (this.doneCallback) {
this.doneCallback();
delete this.doneCallback;
}
Logger.info("AccessFu:Disabled");
},
receiveMessage: function receiveMessage(aMessage) {
Logger.debug(() => {
return ["Recieved", aMessage.name, JSON.stringify(aMessage.json)];
});
switch (aMessage.name) {
case ACCESSFU_MESSAGE.DOSCROLL:
this.Input.doScroll(aMessage.json, aMessage.target);
break;
}
},
_attachWindow: function _attachWindow(win) {
let wtype = win.document.documentElement.getAttribute("windowtype");
if (wtype != "navigator:browser" && wtype != "navigator:geckoview") {
// Don't attach to non-browser or geckoview windows.
return;
}
// Set up frame script
let mm = win.messageManager;
for (let messageName of Object.values(ACCESSFU_MESSAGE)) {
mm.addMessageListener(messageName, this);
}
mm.loadFrameScript(FRAME_SCRIPT, true);
win.addEventListener("TabSelect", this);
if (win.WindowEventDispatcher && !this._eventDispatcherListeners.has(win)) {
const listener = (event, data, callback) => {
this.onEvent(event, data, callback, win);
};
this._eventDispatcherListeners.set(win, listener);
// desktop mochitests don't have this.
win.WindowEventDispatcher.registerListener(
listener,
Object.values(GECKOVIEW_MESSAGE)
);
}
},
_detachWindow: function _detachWindow(win) {
let mm = win.messageManager;
mm.broadcastAsyncMessage("AccessFu:Stop");
mm.removeDelayedFrameScript(FRAME_SCRIPT);
for (let messageName of Object.values(ACCESSFU_MESSAGE)) {
mm.removeMessageListener(messageName, this);
}
win.removeEventListener("TabSelect", this);
if (win.WindowEventDispatcher && this._eventDispatcherListeners.has(win)) {
// desktop mochitests don't have this.
win.WindowEventDispatcher.unregisterListener(
this._eventDispatcherListeners.get(win),
Object.values(GECKOVIEW_MESSAGE)
);
this._eventDispatcherListeners.delete(win);
}
},
onEvent(event, data, callback, win) {
switch (event) {
case GECKOVIEW_MESSAGE.SETTINGS:
if (data.enabled) {
this._enable();
} else {
this._disable();
}
break;
case GECKOVIEW_MESSAGE.NEXT:
case GECKOVIEW_MESSAGE.PREVIOUS: {
let rule = "Simple";
if (data && data.rule && data.rule.length) {
rule =
data.rule.substr(0, 1).toUpperCase() +
data.rule.substr(1).toLowerCase();
}
let method = event.replace(/GeckoView:Accessibility(\w+)/, "move$1");
this.Input.moveCursor(method, rule, "gesture", win);
break;
}
case GECKOVIEW_MESSAGE.ACTIVATE:
this.Input.activateCurrent(data, win);
break;
case GECKOVIEW_MESSAGE.LONG_PRESS:
// XXX: Advertize long press on supported objects and implement action
break;
case GECKOVIEW_MESSAGE.SCROLL_FORWARD:
this.Input.androidScroll("forward", win);
break;
case GECKOVIEW_MESSAGE.SCROLL_BACKWARD:
this.Input.androidScroll("backward", win);
break;
case GECKOVIEW_MESSAGE.CURSOR_TO_FOCUSED:
this.autoMove({ moveToFocused: true }, win);
break;
case GECKOVIEW_MESSAGE.BY_GRANULARITY:
this.Input.moveByGranularity(data, win);
break;
case GECKOVIEW_MESSAGE.EXPLORE_BY_TOUCH:
this.Input.moveToPoint("Simple", ...data.coordinates, win);
break;
case GECKOVIEW_MESSAGE.SET_SELECTION:
this.Input.setSelection(data, win);
break;
case GECKOVIEW_MESSAGE.CLIPBOARD:
this.Input.clipboard(data, win);
break;
case GECKOVIEW_MESSAGE.CLEAR_CURSOR:
this.Input.clearCursor(win);
}
},
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "domwindowopened": {
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener(
"load",
() => {
this._attachWindow(win);
},
{ once: true }
);
break;
}
}
},
_handleEvent: function _handleEvent(aEvent) {
switch (aEvent.type) {
case "TabSelect": {
if (this._focused) {
// We delay this for half a second so the awesomebar could close,
// and we could use the current coordinates for the content item.
// XXX TODO figure out how to avoid magic wait here.
this.autoMove({
delay: 500,
forcePresent: true,
noOpIfOnScreen: true,
moveMethod: "moveFirst",
});
}
break;
}
default:
break;
}
},
autoMove: function autoMove(aOptions, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:AutoMove", aOptions);
},
// So we don't enable/disable twice
_enabled: false,
// Layerview is focused
_focused: false,
_eventDispatcherListeners: new WeakMap(),
/**
* Adjusts the given bounds that are defined in device display pixels
* to client-relative CSS pixels of the chrome window.
* @param {Rect} aJsonBounds the bounds to adjust
* @param {Window} aWindow the window containing the item
*/
screenToClientBounds(aJsonBounds, aWindow) {
let bounds = new Rect(
aJsonBounds.left,
aJsonBounds.top,
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top
);
let { devicePixelRatio, mozInnerScreenX, mozInnerScreenY } = aWindow;
bounds = bounds.scale(1 / devicePixelRatio, 1 / devicePixelRatio);
bounds = bounds.translate(-mozInnerScreenX, -mozInnerScreenY);
return bounds.expandToIntegers();
},
};
var Input = {
moveToPoint: function moveToPoint(aRule, aX, aY, aWindow) {
Logger.debug("moveToPoint", aX, aY);
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:MoveToPoint", {
rule: aRule,
x: aX,
y: aY,
origin: "top",
});
},
moveCursor: function moveCursor(aAction, aRule, aInputType, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:MoveCursor", {
action: aAction,
rule: aRule,
origin: "top",
inputType: aInputType,
});
},
androidScroll: function androidScroll(aDirection, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:AndroidScroll", {
direction: aDirection,
origin: "top",
});
},
moveByGranularity: function moveByGranularity(aDetails, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:MoveByGranularity", aDetails);
},
setSelection: function setSelection(aDetails, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:SetSelection", aDetails);
},
clipboard: function clipboard(aDetails, aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:Clipboard", aDetails);
},
activateCurrent: function activateCurrent(aData, aWindow) {
let mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:Activate", { offset: 0 });
},
doScroll: function doScroll(aDetails, aBrowser) {
let horizontal = aDetails.horizontal;
let page = aDetails.page;
let win = aBrowser.ownerGlobal;
let winUtils = win.windowUtils;
let p = AccessFu.screenToClientBounds(aDetails.bounds, win).center();
winUtils.sendWheelEvent(
p.x,
p.y,
horizontal ? page : 0,
horizontal ? 0 : page,
0,
win.WheelEvent.DOM_DELTA_PAGE,
0,
0,
0,
0
);
},
clearCursor: function clearCursor(aWindow) {
const mm = Utils.getCurrentMessageManager(aWindow);
mm.sendAsyncMessage("AccessFu:ClearCursor");
},
};
AccessFu.Input = Input;

View File

@ -1,73 +0,0 @@
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const AndroidEvents = {
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,
WINDOW_CONTENT_CHANGED: 0x800,
VIEW_SCROLLED: 0x1000,
VIEW_TEXT_SELECTION_CHANGED: 0x2000,
ANNOUNCEMENT: 0x4000,
VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000,
};
function ConstantsMap(aObject, aPrefix, aMap = {}, aModifier = null) {
let offset = aPrefix.length;
for (var name in aObject) {
if (name.indexOf(aPrefix) === 0) {
aMap[name.slice(offset)] = aModifier
? aModifier(aObject[name])
: aObject[name];
}
}
return aMap;
}
XPCOMUtils.defineLazyGetter(this, "Roles", function() {
return ConstantsMap(Ci.nsIAccessibleRole, "ROLE_");
});
XPCOMUtils.defineLazyGetter(this, "Events", function() {
return ConstantsMap(Ci.nsIAccessibleEvent, "EVENT_");
});
XPCOMUtils.defineLazyGetter(this, "Relations", function() {
return ConstantsMap(Ci.nsIAccessibleRelation, "RELATION_");
});
XPCOMUtils.defineLazyGetter(this, "Prefilters", function() {
return ConstantsMap(Ci.nsIAccessibleTraversalRule, "PREFILTER_");
});
XPCOMUtils.defineLazyGetter(this, "Filters", function() {
return ConstantsMap(Ci.nsIAccessibleTraversalRule, "FILTER_");
});
XPCOMUtils.defineLazyGetter(this, "States", function() {
let statesMap = ConstantsMap(Ci.nsIAccessibleStates, "STATE_", {}, val => {
return { base: val, extended: 0 };
});
ConstantsMap(Ci.nsIAccessibleStates, "EXT_STATE_", statesMap, val => {
return { base: 0, extended: val };
});
return statesMap;
});
var EXPORTED_SYMBOLS = [
"Roles",
"Events",
"Relations",
"Filters",
"States",
"Prefilters",
"AndroidEvents",
];

View File

@ -1,574 +0,0 @@
/* 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/. */
ChromeUtils.defineModuleGetter(
this,
"Utils",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Logger",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Roles",
"resource://gre/modules/accessibility/Constants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"States",
"resource://gre/modules/accessibility/Constants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"TraversalRules",
"resource://gre/modules/accessibility/Traversal.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"TraversalHelper",
"resource://gre/modules/accessibility/Traversal.jsm"
);
var EXPORTED_SYMBOLS = ["ContentControl"];
const MOVEMENT_GRANULARITY_CHARACTER = 1;
const MOVEMENT_GRANULARITY_WORD = 2;
const MOVEMENT_GRANULARITY_LINE = 4;
const CLIPBOARD_COPY = 0x4000;
const CLIPBOARD_PASTE = 0x8000;
const CLIPBOARD_CUT = 0x10000;
function ContentControl(aContentScope) {
this._contentScope = Cu.getWeakReference(aContentScope);
this._childMessageSenders = new WeakMap();
}
this.ContentControl.prototype = {
messagesOfInterest: [
"AccessFu:Activate",
"AccessFu:AndroidScroll",
"AccessFu:AutoMove",
"AccessFu:ClearCursor",
"AccessFu:Clipboard",
"AccessFu:MoveByGranularity",
"AccessFu:MoveCursor",
"AccessFu:MoveToPoint",
"AccessFu:SetSelection",
],
start: function cc_start() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.addMessageListener(message, this);
}
},
stop: function cc_stop() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.removeMessageListener(message, this);
}
},
get document() {
return this._contentScope.get().content.document;
},
get window() {
return this._contentScope.get().content;
},
get vc() {
return Utils.getVirtualCursor(this.document);
},
receiveMessage: function cc_receiveMessage(aMessage) {
Logger.debug(() => {
return [
"ContentControl.receiveMessage",
aMessage.name,
JSON.stringify(aMessage.json),
];
});
// If we get an explicit message, we should immediately cancel any autoMove
this.cancelAutoMove();
try {
let func = this["handle" + aMessage.name.slice(9)]; // 'AccessFu:'.length
if (func) {
func.bind(this)(aMessage);
} else {
Logger.warning("ContentControl: Unhandled message:", aMessage.name);
}
} catch (x) {
Logger.logException(
x,
"Error handling message: " + JSON.stringify(aMessage.json)
);
}
},
handleAndroidScroll: function cc_handleAndroidScroll(aMessage) {
let vc = this.vc;
let position = vc.position;
if (aMessage.json.origin != "child" && this.sendToChild(vc, aMessage)) {
// Forwarded succesfully to child cursor.
return;
}
// Counter-intuitive, but scrolling backward (ie. up), actually should
// increase range values.
if (this.adjustRange(position, aMessage.json.direction === "backward")) {
return;
}
this._contentScope.get().sendAsyncMessage("AccessFu:DoScroll", {
bounds: Utils.getBounds(position),
page: aMessage.json.direction === "forward" ? 1 : -1,
horizontal: false,
});
},
handleMoveCursor: function cc_handleMoveCursor(aMessage) {
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let adjustRange = aMessage.json.adjustRange;
let vc = this.vc;
if (origin != "child" && this.sendToChild(vc, aMessage)) {
// Forwarded succesfully to child cursor.
return;
}
if (adjustRange && this.adjustRange(vc.position, action === "moveNext")) {
return;
}
let moved = TraversalHelper.move(vc, action, aMessage.json.rule);
if (moved) {
if (origin === "child") {
// We just stepped out of a child, clear child cursor.
Utils.getMessageManagerForFrame(aMessage.target).sendAsyncMessage(
"AccessFu:ClearCursor",
{}
);
} else {
// We potentially landed on a new child cursor. If so, we want to
// either be on the first or last item in the child doc.
let childAction = action;
if (action === "moveNext") {
childAction = "moveFirst";
} else if (action === "movePrevious") {
childAction = "moveLast";
}
// Attempt to forward move to a potential child cursor in our
// new position.
this.sendToChild(vc, aMessage, { action: childAction }, true);
}
} else if (
!this._childMessageSenders.has(aMessage.target) &&
origin !== "top"
) {
// We failed to move, and the message is not from a parent, so forward
// to it.
this.sendToParent(aMessage);
}
},
handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
let [x, y] = [aMessage.json.x, aMessage.json.y];
let rule = TraversalRules[aMessage.json.rule];
this.vc.moveToPoint(rule, x, y, true);
},
handleClearCursor: function cc_handleClearCursor(aMessage) {
let forwarded = this.sendToChild(this.vc, aMessage);
this.vc.position = null;
if (!forwarded) {
this._contentScope.get().sendAsyncMessage("AccessFu:CursorCleared");
}
},
handleAutoMove: function cc_handleAutoMove(aMessage) {
this.autoMove(null, aMessage.json);
},
handleActivate: function cc_handleActivate(aMessage) {
let activateAccessible = aAccessible => {
Logger.debug(() => {
return ["activateAccessible", Logger.accessibleToString(aAccessible)];
});
if (aAccessible.actionCount > 0) {
aAccessible.doAction(0);
} else {
let control = Utils.getEmbeddedControl(aAccessible);
if (control && control.actionCount > 0) {
control.doAction(0);
}
// XXX Some mobile widget sets do not expose actions properly
// (via ARIA roles, etc.), so we need to generate a click.
// Could possibly be made simpler in the future. Maybe core
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
let docAcc = Utils.AccService.getAccessibleFor(this.document);
let docX = {},
docY = {},
docW = {},
docH = {};
docAcc.getBounds(docX, docY, docW, docH);
let objX = {},
objY = {},
objW = {},
objH = {};
aAccessible.getBounds(objX, objY, objW, objH);
let x = Math.round(objX.value - docX.value + objW.value / 2);
let y = Math.round(objY.value - docY.value + objH.value / 2);
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
for (let eventType of ["mousedown", "mouseup"]) {
let evt = this.document.createEvent("MouseEvents");
evt.initMouseEvent(
eventType,
true,
true,
this.window,
x,
y,
0,
0,
0,
false,
false,
false,
false,
0,
null
);
node.dispatchEvent(evt);
}
}
};
let focusedAcc = Utils.AccService.getAccessibleFor(
this.document.activeElement
);
if (
focusedAcc &&
this.vc.position === focusedAcc &&
focusedAcc.role === Roles.ENTRY
) {
let accText = focusedAcc.QueryInterface(Ci.nsIAccessibleText);
let newOffset = aMessage.json.offset;
if (newOffset >= 0 && newOffset <= accText.characterCount) {
accText.caretOffset = newOffset;
}
return;
}
// recursively find a descendant that is activatable.
let getActivatableDescendant = aAccessible => {
if (aAccessible.actionCount > 0) {
return aAccessible;
}
for (let acc = aAccessible.firstChild; acc; acc = acc.nextSibling) {
let activatable = getActivatableDescendant(acc);
if (activatable) {
return activatable;
}
}
return null;
};
let vc = this.vc;
if (!this.sendToChild(vc, aMessage, null, true)) {
let position = vc.position;
activateAccessible(getActivatableDescendant(position) || position);
}
},
adjustRange: function cc_adjustRange(aAccessible, aStepUp) {
let acc = Utils.getEmbeddedControl(aAccessible) || aAccessible;
try {
acc.QueryInterface(Ci.nsIAccessibleValue);
} catch (x) {
// This is not an adjustable, return false.
return false;
}
let elem = acc.DOMNode;
if (!elem) {
return false;
}
if (elem.tagName === "INPUT" && elem.type === "range") {
elem[aStepUp ? "stepDown" : "stepUp"]();
let evt = this.document.createEvent("UIEvent");
evt.initEvent("change", true, true);
elem.dispatchEvent(evt);
} else {
let evt = this.document.createEvent("KeyboardEvent");
let keycode = aStepUp ? evt.DOM_VK_DOWN : evt.DOM_VK_UP;
evt.initKeyEvent(
"keypress",
false,
true,
null,
false,
false,
false,
false,
keycode,
0
);
elem.dispatchEvent(evt);
}
return true;
},
handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
const { direction, granularity, select } = aMessage.json;
const focusedAcc = Utils.AccService.getAccessibleFor(
this.document.activeElement
);
const editable =
focusedAcc && Utils.getState(focusedAcc).contains(States.EDITABLE)
? focusedAcc.QueryInterface(Ci.nsIAccessibleText)
: null;
if (editable) {
const caretOffset = editable.caretOffset;
this.vc.setTextRange(editable, caretOffset, caretOffset, false);
}
let pivotGranularity;
switch (granularity) {
case MOVEMENT_GRANULARITY_CHARACTER:
pivotGranularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
break;
case MOVEMENT_GRANULARITY_WORD:
pivotGranularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
break;
case MOVEMENT_GRANULARITY_LINE:
pivotGranularity = Ci.nsIAccessiblePivot.LINE_BOUNDARY;
break;
default:
return;
}
if (direction === "Previous") {
this.vc.movePreviousByText(pivotGranularity);
} else if (direction === "Next") {
this.vc.moveNextByText(pivotGranularity);
}
if (editable) {
const newOffset =
direction === "Next" ? this.vc.endOffset : this.vc.startOffset;
if (select) {
let anchor = editable.caretOffset;
if (editable.selectionCount) {
const [startSel, endSel] = Utils.getTextSelection(editable);
anchor = startSel == anchor ? endSel : startSel;
}
editable.setSelectionBounds(0, anchor, newOffset);
} else {
editable.caretOffset = newOffset;
}
}
},
handleSetSelection: function cc_handleSetSelection(aMessage) {
const { start, end } = aMessage.json;
const focusedAcc = Utils.AccService.getAccessibleFor(
this.document.activeElement
);
if (focusedAcc) {
const accText = focusedAcc.QueryInterface(Ci.nsIAccessibleText);
if (start == end) {
accText.caretOffset = start;
} else {
accText.setSelectionBounds(0, start, end);
}
}
},
handleClipboard: function cc_handleClipboard(aMessage) {
const { action } = aMessage.json;
const focusedAcc = Utils.AccService.getAccessibleFor(
this.document.activeElement
);
if (focusedAcc) {
const [startSel, endSel] = Utils.getTextSelection(focusedAcc);
const editText = focusedAcc.QueryInterface(Ci.nsIAccessibleEditableText);
switch (action) {
case CLIPBOARD_COPY:
if (startSel != endSel) {
editText.copyText(startSel, endSel);
}
break;
case CLIPBOARD_PASTE:
if (startSel != endSel) {
editText.deleteText(startSel, endSel);
}
editText.pasteText(startSel);
break;
case CLIPBOARD_CUT:
if (startSel != endSel) {
editText.cutText(startSel, endSel);
}
break;
}
}
},
getChildCursor: function cc_getChildCursor(aAccessible) {
let acc = aAccessible || this.vc.position;
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
let domNode = acc.DOMNode;
let mm = this._childMessageSenders.get(domNode, null);
if (!mm) {
mm = Utils.getMessageManagerForFrame(domNode);
mm.addWeakMessageListener("AccessFu:MoveCursor", this);
this._childMessageSenders.set(domNode, mm);
}
return mm;
}
return null;
},
sendToChild: function cc_sendToChild(
aVirtualCursor,
aMessage,
aReplacer,
aFocus
) {
let position = aVirtualCursor.position;
let mm = this.getChildCursor(position);
if (!mm) {
return false;
}
if (aFocus) {
position.takeFocus();
}
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = "parent";
for (let attr in aReplacer) {
newJSON[attr] = aReplacer[attr];
}
mm.sendAsyncMessage(aMessage.name, newJSON);
return true;
},
sendToParent: function cc_sendToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = "child";
aMessage.target.sendAsyncMessage(aMessage.name, newJSON);
},
/**
* Move cursor.
* aOptions could have any of these fields:
* - delay: in ms, before actual move is performed. Another autoMove call
* would cancel it. Useful if we want to wait for a possible trailing
* focus move. Default 0.
* - noOpIfOnScreen: if accessible is alive and visible, don't do anything.
* - moveToFocused: if there is a focused accessible move to that. This takes
* precedence over given anchor.
* - moveMethod: pivot move method to use, default is 'moveNext',
*/
autoMove: function cc_autoMove(aAnchor, aOptions = {}) {
this.cancelAutoMove();
let moveFunc = () => {
let vc = this.vc;
let acc = aAnchor;
let rule = aOptions.onScreenOnly
? TraversalRules.SimpleOnScreen
: TraversalRules.Simple;
if (
aOptions.noOpIfOnScreen &&
Utils.isAliveAndVisible(vc.position, true)
) {
return;
}
if (aOptions.moveToFocused) {
acc =
Utils.AccService.getAccessibleFor(this.document.activeElement) || acc;
}
let moved = false;
let moveMethod = aOptions.moveMethod || "moveNext"; // default is moveNext
let moveFirstOrLast = moveMethod in ["moveFirst", "moveLast"];
if (!moveFirstOrLast || acc) {
// We either need next/previous or there is an anchor we need to use.
moved = vc[moveFirstOrLast ? "moveNext" : moveMethod](
rule,
acc,
true,
true
);
}
if (moveFirstOrLast && !moved) {
// We move to first/last after no anchor move happened or succeeded.
moved = vc[moveMethod](rule, true);
}
this.sendToChild(
vc,
{
name: "AccessFu:AutoMove",
json: {
moveMethod: aOptions.moveMethod,
moveToFocused: aOptions.moveToFocused,
noOpIfOnScreen: true,
},
},
null,
true
);
};
if (aOptions.delay) {
this._autoMove = this.window.setTimeout(moveFunc, aOptions.delay);
} else {
moveFunc();
}
},
cancelAutoMove: function cc_cancelAutoMove() {
this.window.clearTimeout(this._autoMove);
this._autoMove = 0;
},
QueryInterface: ChromeUtils.generateQI([
Ci.nsISupportsWeakReference,
Ci.nsIMessageListener,
]),
};

View File

@ -1,301 +0,0 @@
/* 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";
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Utils",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Logger",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Events",
"resource://gre/modules/accessibility/Constants.jsm"
);
var EXPORTED_SYMBOLS = ["EventManager"];
function EventManager(aContentScope) {
this.contentScope = aContentScope;
this.addEventListener = this.contentScope.addEventListener.bind(
this.contentScope
);
this.removeEventListener = this.contentScope.removeEventListener.bind(
this.contentScope
);
this.sendMsgFunc = this.contentScope.sendAsyncMessage.bind(this.contentScope);
}
this.EventManager.prototype = {
start: function start() {
try {
if (!this._started) {
Logger.debug("EventManager.start");
this._started = true;
AccessibilityEventObserver.addListener(this);
this._preDialogPosition = new WeakMap();
}
} catch (x) {
Logger.logException(x, "Failed to start EventManager");
}
},
// XXX: Stop is not called when the tab is closed (|TabClose| event is too
// late). It is only called when the AccessFu is disabled explicitly.
stop: function stop() {
if (!this._started) {
return;
}
Logger.debug("EventManager.stop");
AccessibilityEventObserver.removeListener(this);
try {
this._preDialogPosition = new WeakMap();
} catch (x) {
// contentScope is dead.
} finally {
this._started = false;
}
},
get contentControl() {
return this.contentScope._jsat_contentControl;
},
handleAccEvent: function handleAccEvent(aEvent) {
Logger.debug(() => {
return [
"A11yEvent",
Logger.eventToString(aEvent),
Logger.accessibleToString(aEvent.accessible),
];
});
// Don't bother with non-content events in firefox.
if (
Utils.MozBuildApp == "browser" &&
aEvent.eventType != Events.VIRTUALCURSOR_CHANGED &&
// XXX Bug 442005 results in DocAccessible::getDocType returning
// NS_ERROR_FAILURE. Checking for aEvent.accessibleDocument.docType ==
// 'window' does not currently work.
(aEvent.accessibleDocument.DOMDocument.doctype &&
aEvent.accessibleDocument.DOMDocument.doctype.name === "window")
) {
return;
}
switch (aEvent.eventType) {
case Events.TEXT_CARET_MOVED: {
if (
aEvent.accessible != aEvent.accessibleDocument &&
!aEvent.isFromUserInput
) {
// If caret moves in document without direct user
// we are probably stepping through results in find-in-page.
let acc = Utils.getTextLeafForOffset(
aEvent.accessible,
aEvent.QueryInterface(Ci.nsIAccessibleCaretMoveEvent).caretOffset
);
this.contentControl.autoMove(acc);
}
break;
}
case Events.NAME_CHANGE: {
// XXX: Port to Android
break;
}
case Events.SCROLLING_START: {
this.contentControl.autoMove(aEvent.accessible);
break;
}
case Events.SHOW: {
// XXX: Port to Android
break;
}
case Events.HIDE: {
// XXX: Port to Android
break;
}
case Events.VALUE_CHANGE: {
// XXX: Port to Android
break;
}
}
},
QueryInterface: ChromeUtils.generateQI([
Ci.nsISupportsWeakReference,
Ci.nsIObserver,
]),
};
const AccessibilityEventObserver = {
/**
* A WeakMap containing [content, EventManager] pairs.
*/
eventManagers: new WeakMap(),
/**
* A total number of registered eventManagers.
*/
listenerCount: 0,
/**
* An indicator of an active 'accessible-event' observer.
*/
started: false,
/**
* Start an AccessibilityEventObserver.
*/
start: function start() {
if (this.started || this.listenerCount === 0) {
return;
}
Services.obs.addObserver(this, "accessible-event");
this.started = true;
},
/**
* Stop an AccessibilityEventObserver.
*/
stop: function stop() {
if (!this.started) {
return;
}
Services.obs.removeObserver(this, "accessible-event");
// Clean up all registered event managers.
this.eventManagers = new WeakMap();
this.listenerCount = 0;
this.started = false;
},
/**
* Register an EventManager and start listening to the
* 'accessible-event' messages.
*
* @param aEventManager EventManager
* An EventManager object that was loaded into the specific content.
*/
addListener: function addListener(aEventManager) {
let content = aEventManager.contentScope.content;
if (!this.eventManagers.has(content)) {
this.listenerCount++;
}
this.eventManagers.set(content, aEventManager);
// Since at least one EventManager was registered, start listening.
Logger.debug(
"AccessibilityEventObserver.addListener. Total:",
this.listenerCount
);
this.start();
},
/**
* Unregister an EventManager and, optionally, stop listening to the
* 'accessible-event' messages.
*
* @param aEventManager EventManager
* An EventManager object that was stopped in the specific content.
*/
removeListener: function removeListener(aEventManager) {
let content = aEventManager.contentScope.content;
if (!this.eventManagers.delete(content)) {
return;
}
this.listenerCount--;
Logger.debug(
"AccessibilityEventObserver.removeListener. Total:",
this.listenerCount
);
if (this.listenerCount === 0) {
// If there are no EventManagers registered at the moment, stop listening
// to the 'accessible-event' messages.
this.stop();
}
},
/**
* Lookup an EventManager for a specific content. If the EventManager is not
* found, walk up the hierarchy of parent windows.
* @param content Window
* A content Window used to lookup the corresponding EventManager.
*/
getListener: function getListener(content) {
let eventManager = this.eventManagers.get(content);
if (eventManager) {
return eventManager;
}
let parent = content.parent;
if (parent === content) {
// There is no parent or the parent is of a different type.
return null;
}
return this.getListener(parent);
},
/**
* Handle the 'accessible-event' message.
*/
observe: function observe(aSubject, aTopic, aData) {
if (aTopic !== "accessible-event") {
return;
}
let event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
if (!event.accessibleDocument) {
Logger.warning(
"AccessibilityEventObserver.observe: no accessible document:",
Logger.eventToString(event),
"accessible:",
Logger.accessibleToString(event.accessible)
);
return;
}
let content;
try {
content = event.accessibleDocument.window;
} catch (e) {
Logger.warning(
"AccessibilityEventObserver.observe: no window for accessible document:",
Logger.eventToString(event),
"accessible:",
Logger.accessibleToString(event.accessible)
);
return;
}
// Match the content window to its EventManager.
let eventManager = this.getListener(content);
if (!eventManager || !eventManager._started) {
if (Utils.MozBuildApp === "browser" && !content.isChromeWindow) {
Logger.warning(
"AccessibilityEventObserver.observe: ignored event:",
Logger.eventToString(event),
"accessible:",
Logger.accessibleToString(event.accessible),
"document:",
Logger.accessibleToString(event.accessibleDocument)
);
}
return;
}
try {
eventManager.handleAccEvent(event);
} catch (x) {
Logger.logException(x, "Error handing accessible event");
}
},
};

View File

@ -1,433 +0,0 @@
/* 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";
var EXPORTED_SYMBOLS = ["TraversalRules", "TraversalHelper"]; // jshint ignore:line
const { PrefCache, Utils } = ChromeUtils.import(
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Roles", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Filters", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"States", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Prefilters", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm"
);
var gSkipEmptyImages = new PrefCache(
"accessibility.accessfu.skip_empty_images"
);
function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter, aContainerRule) {
this._explicitMatchRoles = new Set(aRoles);
this._matchRoles = aRoles;
if (aRoles.length) {
if (!aRoles.includes(Roles.LABEL)) {
this._matchRoles.push(Roles.LABEL);
}
if (!aRoles.includes(Roles.INTERNAL_FRAME)) {
// Used for traversing in to child OOP frames.
this._matchRoles.push(Roles.INTERNAL_FRAME);
}
}
this._matchFunc =
aMatchFunc ||
function() {
return Filters.MATCH;
};
this.preFilter = aPreFilter || gSimplePreFilter;
this.containerRule = aContainerRule;
}
BaseTraversalRule.prototype = {
getMatchRoles: function BaseTraversalRule_getmatchRoles() {
return this._matchRoles;
},
match: function BaseTraversalRule_match(aAccessible) {
let role = aAccessible.role;
if (role == Roles.INTERNAL_FRAME) {
return Utils.getMessageManagerForFrame(aAccessible.DOMNode)
? Filters.MATCH | Filters.IGNORE_SUBTREE
: Filters.IGNORE;
}
if (this._explicitMatchRoles.has(role) || !this._explicitMatchRoles.size) {
return this._matchFunc(aAccessible);
}
return Filters.IGNORE;
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIAccessibleTraversalRule]),
};
var gSimpleTraversalRoles = [
Roles.MENUITEM,
Roles.LINK,
Roles.PAGETAB,
Roles.GRAPHIC,
Roles.STATICTEXT,
Roles.TEXT_LEAF,
Roles.PUSHBUTTON,
Roles.CHECKBUTTON,
Roles.RADIOBUTTON,
Roles.COMBOBOX,
Roles.PROGRESSBAR,
Roles.BUTTONDROPDOWN,
Roles.BUTTONMENU,
Roles.CHECK_MENU_ITEM,
Roles.PASSWORD_TEXT,
Roles.RADIO_MENU_ITEM,
Roles.TOGGLE_BUTTON,
Roles.ENTRY,
Roles.KEY,
Roles.HEADER,
Roles.HEADING,
Roles.SLIDER,
Roles.SPINBUTTON,
Roles.OPTION,
Roles.LISTITEM,
Roles.GRID_CELL,
Roles.COLUMNHEADER,
Roles.ROWHEADER,
Roles.STATUSBAR,
Roles.SWITCH,
Roles.MATHML_MATH,
];
var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
// An object is simple, if it either has a single child lineage,
// or has a flat subtree.
function isSingleLineage(acc) {
for (let child = acc; child; child = child.firstChild) {
if (Utils.visibleChildCount(child) > 1) {
return false;
}
}
return true;
}
function isFlatSubtree(acc) {
for (let child = acc.firstChild; child; child = child.nextSibling) {
// text leafs inherit the actionCount of any ancestor that has a click
// listener.
if ([Roles.TEXT_LEAF, Roles.STATICTEXT].includes(child.role)) {
continue;
}
if (Utils.visibleChildCount(child) > 0 || child.actionCount > 0) {
return false;
}
}
return true;
}
switch (aAccessible.role) {
case Roles.COMBOBOX:
// We don't want to ignore the subtree because this is often
// where the list box hangs out.
return Filters.MATCH;
case Roles.TEXT_LEAF: {
// Nameless text leaves are boring, skip them.
let name = aAccessible.name;
return name && name.trim() ? Filters.MATCH : Filters.IGNORE;
}
case Roles.STATICTEXT:
// Ignore prefix static text in list items. They are typically bullets or numbers.
return Utils.isListItemDecorator(aAccessible)
? Filters.IGNORE
: Filters.MATCH;
case Roles.GRAPHIC:
return TraversalRules._shouldSkipImage(aAccessible);
case Roles.HEADER:
case Roles.HEADING:
case Roles.COLUMNHEADER:
case Roles.ROWHEADER:
case Roles.STATUSBAR:
if (
(aAccessible.childCount > 0 || aAccessible.name) &&
(isSingleLineage(aAccessible) || isFlatSubtree(aAccessible))
) {
return Filters.MATCH | Filters.IGNORE_SUBTREE;
}
return Filters.IGNORE;
case Roles.GRID_CELL:
return isSingleLineage(aAccessible) || isFlatSubtree(aAccessible)
? Filters.MATCH | Filters.IGNORE_SUBTREE
: Filters.IGNORE;
case Roles.LISTITEM: {
let item =
aAccessible.childCount === 2 &&
aAccessible.firstChild.role === Roles.STATICTEXT
? aAccessible.lastChild
: aAccessible;
return isSingleLineage(item) || isFlatSubtree(item)
? Filters.MATCH | Filters.IGNORE_SUBTREE
: Filters.IGNORE;
}
default:
// Ignore the subtree, if there is one. So that we don't land on
// the same content that was already presented by its parent.
return Filters.MATCH | Filters.IGNORE_SUBTREE;
}
};
var gSimplePreFilter =
Prefilters.DEFUNCT |
Prefilters.INVISIBLE |
Prefilters.TRANSPARENT |
Prefilters.PLATFORM_PRUNED;
var TraversalRules = {
// jshint ignore:line
Simple: new BaseTraversalRule(gSimpleTraversalRoles, gSimpleMatchFunc),
SimpleOnScreen: new BaseTraversalRule(
gSimpleTraversalRoles,
gSimpleMatchFunc,
gSimplePreFilter | Prefilters.OFFSCREEN
),
Anchor: new BaseTraversalRule([Roles.LINK], function Anchor_match(
aAccessible
) {
// We want to ignore links, only focus named anchors.
if (Utils.getState(aAccessible).contains(States.LINKED)) {
return Filters.IGNORE;
}
return Filters.MATCH;
}),
Button: new BaseTraversalRule([
Roles.PUSHBUTTON,
Roles.SPINBUTTON,
Roles.TOGGLE_BUTTON,
Roles.BUTTONDROPDOWN,
Roles.BUTTONDROPDOWNGRID,
]),
Combobox: new BaseTraversalRule([Roles.COMBOBOX, Roles.LISTBOX]),
Landmark: new BaseTraversalRule(
[],
function Landmark_match(aAccessible) {
return Utils.getLandmarkName(aAccessible)
? Filters.MATCH
: Filters.IGNORE;
},
null,
true
),
/* A rule for Android's section navigation, lands on landmarks, regions, and
on headings to aid navigation of traditionally structured documents */
Section: new BaseTraversalRule(
[],
function Section_match(aAccessible) {
if (aAccessible.role === Roles.HEADING) {
return Filters.MATCH;
}
let matchedRole = Utils.matchRoles(aAccessible, [
"banner",
"complementary",
"contentinfo",
"main",
"navigation",
"search",
"region",
]);
return matchedRole ? Filters.MATCH : Filters.IGNORE;
},
null,
true
),
Entry: new BaseTraversalRule([Roles.ENTRY, Roles.PASSWORD_TEXT]),
FormElement: new BaseTraversalRule([
Roles.PUSHBUTTON,
Roles.SPINBUTTON,
Roles.TOGGLE_BUTTON,
Roles.BUTTONDROPDOWN,
Roles.BUTTONDROPDOWNGRID,
Roles.COMBOBOX,
Roles.LISTBOX,
Roles.ENTRY,
Roles.PASSWORD_TEXT,
Roles.PAGETAB,
Roles.RADIOBUTTON,
Roles.RADIO_MENU_ITEM,
Roles.SLIDER,
Roles.CHECKBUTTON,
Roles.CHECK_MENU_ITEM,
Roles.SWITCH,
]),
Graphic: new BaseTraversalRule([Roles.GRAPHIC], function Graphic_match(
aAccessible
) {
return TraversalRules._shouldSkipImage(aAccessible);
}),
Heading: new BaseTraversalRule([Roles.HEADING], function Heading_match(
aAccessible
) {
return aAccessible.childCount > 0 ? Filters.MATCH : Filters.IGNORE;
}),
ListItem: new BaseTraversalRule([Roles.LISTITEM, Roles.TERM]),
Link: new BaseTraversalRule([Roles.LINK], function Link_match(aAccessible) {
// We want to ignore anchors, only focus real links.
if (Utils.getState(aAccessible).contains(States.LINKED)) {
return Filters.MATCH;
}
return Filters.IGNORE;
}),
/* For TalkBack's "Control" granularity. Form conrols and links */
Control: new BaseTraversalRule(
[
Roles.PUSHBUTTON,
Roles.SPINBUTTON,
Roles.TOGGLE_BUTTON,
Roles.BUTTONDROPDOWN,
Roles.BUTTONDROPDOWNGRID,
Roles.COMBOBOX,
Roles.LISTBOX,
Roles.ENTRY,
Roles.PASSWORD_TEXT,
Roles.PAGETAB,
Roles.RADIOBUTTON,
Roles.RADIO_MENU_ITEM,
Roles.SLIDER,
Roles.CHECKBUTTON,
Roles.CHECK_MENU_ITEM,
Roles.SWITCH,
Roles.LINK,
Roles.MENUITEM,
],
function Control_match(aAccessible) {
// We want to ignore anchors, only focus real links.
if (
aAccessible.role == Roles.LINK &&
!Utils.getState(aAccessible).contains(States.LINKED)
) {
return Filters.IGNORE;
}
return Filters.MATCH;
}
),
List: new BaseTraversalRule(
[Roles.LIST, Roles.DEFINITION_LIST],
null,
null,
true
),
PageTab: new BaseTraversalRule([Roles.PAGETAB]),
Paragraph: new BaseTraversalRule(
[Roles.PARAGRAPH, Roles.SECTION],
function Paragraph_match(aAccessible) {
for (
let child = aAccessible.firstChild;
child;
child = child.nextSibling
) {
if (child.role === Roles.TEXT_LEAF) {
return Filters.MATCH | Filters.IGNORE_SUBTREE;
}
}
return Filters.IGNORE;
}
),
RadioButton: new BaseTraversalRule([
Roles.RADIOBUTTON,
Roles.RADIO_MENU_ITEM,
]),
Separator: new BaseTraversalRule([Roles.SEPARATOR]),
Table: new BaseTraversalRule([Roles.TABLE]),
Checkbox: new BaseTraversalRule([
Roles.CHECKBUTTON,
Roles.CHECK_MENU_ITEM,
Roles.SWITCH /* A type of checkbox that represents on/off values */,
]),
_shouldSkipImage: function _shouldSkipImage(aAccessible) {
if (gSkipEmptyImages.value && aAccessible.name === "") {
return Filters.IGNORE;
}
return Filters.MATCH;
},
};
var TraversalHelper = {
_helperPivotCache: null,
get helperPivotCache() {
delete this.helperPivotCache;
this.helperPivotCache = new WeakMap();
return this.helperPivotCache;
},
getHelperPivot: function TraversalHelper_getHelperPivot(aRoot) {
let pivot = this.helperPivotCache.get(aRoot.DOMNode);
if (!pivot) {
pivot = Utils.AccService.createAccessiblePivot(aRoot);
this.helperPivotCache.set(aRoot.DOMNode, pivot);
}
return pivot;
},
move: function TraversalHelper_move(aVirtualCursor, aMethod, aRule) {
let rule = TraversalRules[aRule];
if (rule.containerRule) {
let moved = false;
let helperPivot = this.getHelperPivot(aVirtualCursor.root);
helperPivot.position = aVirtualCursor.position;
// We continue to step through containers until there is one with an
// atomic child (via 'Simple') on which we could land.
while (!moved) {
if (helperPivot[aMethod](rule)) {
aVirtualCursor.modalRoot = helperPivot.position;
moved = aVirtualCursor.moveFirst(TraversalRules.Simple);
aVirtualCursor.modalRoot = null;
} else {
// If we failed to step to another container, break and return false.
break;
}
}
return moved;
}
return aVirtualCursor[aMethod](rule);
},
};

File diff suppressed because it is too large Load Diff

View File

@ -1,69 +0,0 @@
/* 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/. */
/* eslint-env mozilla/frame-script */
ChromeUtils.defineModuleGetter(
this,
"Logger",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Utils",
"resource://gre/modules/accessibility/Utils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"EventManager",
"resource://gre/modules/accessibility/EventManager.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ContentControl",
"resource://gre/modules/accessibility/ContentControl.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"States",
"resource://gre/modules/accessibility/Constants.jsm"
);
Logger.info("content-script.js", content.document.location);
function onStop(m) {
Logger.debug("AccessFu:Stop");
removeMessageListener("AccessFu:Stop", onStop);
this._jsat_eventManager.stop();
this._jsat_contentControl.stop();
}
addMessageListener("AccessFu:Stop", onStop);
if (!this._jsat_contentControl) {
this._jsat_contentControl = new ContentControl(this);
}
this._jsat_contentControl.start();
if (!this._jsat_eventManager) {
this._jsat_eventManager = new EventManager(this);
}
this._jsat_eventManager.start();
function contentStarted() {
let accDoc = Utils.AccService.getAccessibleFor(content.document);
if (accDoc && !Utils.getState(accDoc).contains(States.BUSY)) {
sendAsyncMessage("AccessFu:ContentStarted");
} else {
content.setTimeout(contentStarted, 0);
}
}
if (Utils.inTest) {
// During a test we want to wait for the document to finish loading for
// consistency.
contentStarted();
}

View File

@ -1,14 +0,0 @@
# 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/.
EXTRA_JS_MODULES.accessibility += [
'AccessFu.jsm',
'Constants.jsm',
'ContentControl.jsm',
'EventManager.jsm',
'Traversal.jsm',
'Utils.jsm'
]
JAR_MANIFESTS += ['jar.mn']

View File

@ -21,7 +21,6 @@ DIRS += [ 'aom',
'html',
'interfaces',
'ipc',
'jsat',
'xpcom'
]

View File

@ -1,7 +1,6 @@
[DEFAULT]
support-files =
head.js
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js
!/accessible/tests/mochitest/letters.gif

View File

@ -19,7 +19,7 @@ Services.scriptloader.loadSubScript(
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "layout.js", dir: MOCHITESTS_DIR },
"events.js"
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);
/**

View File

@ -1,7 +1,7 @@
[DEFAULT]
skip-if = (os == 'win' && processor == 'aarch64') # 1534855
support-files =
events.js
!/accessible/tests/mochitest/*.js
head.js
shared-head.js

View File

@ -7,7 +7,6 @@ support-files =
doc_treeupdate_removal.xhtml
doc_treeupdate_visibility.html
doc_treeupdate_whitespace.html
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js
!/accessible/tests/mochitest/letters.gif

View File

@ -46,14 +46,14 @@ const rules = {
],
HTMLInputImage: [
...HTMLControlHeadRule,
{ attr: "alt", recreated: true },
{ attr: "value", recreated: true },
{ attr: "alt" },
{ attr: "value" },
{ attr: "title" },
],
HTMLInputImageNoValidSrc: [
...HTMLControlHeadRule,
{ attr: "alt", recreated: true },
{ attr: "value", recreated: true },
{ attr: "alt" },
{ attr: "value" },
],
HTMLInputReset: [
...HTMLControlHeadRule,

View File

@ -9,15 +9,10 @@ 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>`,
<div id="container"><div id="scrollarea" style="overflow:auto;"><input>`,
async function(browser, accDoc) {
const id1 = "container";
const id2 = "container2";
const container = findAccessibleChildByID(accDoc, id1);
const container2 = findAccessibleChildByID(accDoc, id2);
/* ================= Change scroll range ================================== */
let tree = {
@ -60,26 +55,5 @@ addAccessibleTask(
],
};
testAccessibleTree(container, tree);
/* ================= Change scrollbar styles ============================== */
tree = {
SECTION: [
// container2
{ SECTION: [] }, // scroll area because of its ID
],
};
testAccessibleTree(container2, tree);
onReorder = waitForEvent(EVENT_REORDER, id2);
await invokeSetStyle(browser, "scrollarea2", "overflow", "auto");
await onReorder;
tree = {
SECTION: [
// container
{ SECTION: [] }, // scroll area
],
};
testAccessibleTree(container2, tree);
}
);

View File

@ -70,7 +70,7 @@ addAccessibleTask(
};
testAccessibleTree(container1, tree);
onReorder = waitForEvent(EVENT_REORDER, id2);
onReorder = waitForEvent(EVENT_REORDER, "container2_child");
// Add CSS generated content to an element in container2's subtree
await invokeSetAttribute(browser, "container2_child", "class", "gentext");
await onReorder;

View File

@ -12,5 +12,8 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as events.js.
loadScripts({ name: "common.js", dir: MOCHITESTS_DIR }, "events.js");
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);

View File

@ -1,7 +1,6 @@
[DEFAULT]
support-files =
head.js
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js

View File

@ -32,8 +32,10 @@ async function runTests(browser, accDoc) {
evt = await onFocus;
testStates(evt.accessible, STATE_FOCUSED);
let inputField = browser.ownerDocument.getElementById("urlbar").inputField;
onFocus = waitForEvent(EVENT_FOCUS, getAccessible(inputField));
onFocus = waitForEvent(
EVENT_FOCUS,
event => event.accessible.DOMNode == gURLBar.inputField
);
EventUtils.synthesizeKey("t", { accelKey: true }, browser.ownerGlobal);
evt = await onFocus;
testStates(evt.accessible, STATE_FOCUSED);

View File

@ -24,6 +24,28 @@ function isEventForAutocompleteItem(event) {
return event.accessible.role == ROLE_COMBOBOX_OPTION;
}
function isEventForButton(event) {
return event.accessible.role == ROLE_PUSHBUTTON;
}
function isEventForOneOffEngine(event) {
let parent = event.accessible.parent;
return (
event.accessible.role == ROLE_PUSHBUTTON &&
parent &&
parent.role == ROLE_GROUPING &&
parent.name
);
}
function isEventForMenuPopup(event) {
return event.accessible.role == ROLE_MENUPOPUP;
}
function isEventForMenuItem(event) {
return event.accessible.role == ROLE_MENUITEM;
}
/**
* Wait for an autocomplete search to finish.
* This is necessary to ensure predictable results, as these searches are
@ -134,6 +156,12 @@ async function runTests() {
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring autocomplete focus on arrow up for search settings button");
focused = waitForEvent(EVENT_FOCUS, isEventForButton);
EventUtils.synthesizeKey("KEY_ArrowUp");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring text box focus when text is typed");
focused = waitForEvent(EVENT_FOCUS, textBox);
EventUtils.sendString("z");
@ -154,12 +182,20 @@ async function runTests() {
await focused;
testStates(textBox, STATE_FOCUSED);
info("Ensuring autocomplete focus on arrow down & up");
info("Ensuring autocomplete focus on arrow down (4)");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
// With the quantumbar enabled, we only get one result here, and arrow down
// selects a one-off search button. We arrow back up to re-select the
// autocomplete result.
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring one-off search button focus on arrow down");
focused = waitForEvent(EVENT_FOCUS, isEventForOneOffEngine);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring autocomplete focus on arrow up");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowUp");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
@ -181,6 +217,34 @@ async function runTests() {
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
}
info(
"Ensuring context menu gets menu event on launch, item focus on down, and address bar focus on escape."
);
let menuEvent = waitForEvent(
nsIAccessibleEvent.EVENT_MENUPOPUP_START,
isEventForMenuPopup
);
EventUtils.sendMouseEvent(
{ type: "contextmenu" },
gURLBar.querySelector("moz-input-box")
);
await menuEvent;
focused = waitForEvent(EVENT_FOCUS, isEventForMenuItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
focused = waitForEvent(EVENT_FOCUS, textBox);
let closed = waitForEvent(
nsIAccessibleEvent.EVENT_MENUPOPUP_END,
isEventForMenuPopup
);
EventUtils.synthesizeKey("KEY_Escape");
await closed;
await focused;
testStates(textBox, STATE_FOCUSED);
}
addAccessibleTask(``, runTests);

View File

@ -30,7 +30,7 @@ async function checkURLBarCaretEvents() {
});
info("Loaded " + kURL);
let urlbarInputEl = newWin.document.getElementById("urlbar").inputField;
let urlbarInputEl = newWin.gURLBar.inputField;
let urlbarInput = getAccessible(urlbarInputEl, [nsIAccessibleText]);
let onCaretMove = waitForEvents([

View File

@ -12,5 +12,8 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as events.js.
loadScripts({ name: "common.js", dir: MOCHITESTS_DIR }, "events.js");
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);

View File

@ -21,9 +21,9 @@ add_task(async function testAutocompleteRichResult() {
value: "a",
});
info("Waiting for accessibility to be created for the richlistbox");
info("Waiting for accessibility to be created for the results list");
let resultsView;
resultsView = gURLBar.view.panel.querySelector("#urlbarView-results");
resultsView = gURLBar.view.panel.querySelector(".urlbarView-results");
await BrowserTestUtils.waitForCondition(() =>
accService.getAccessibleFor(resultsView)
);

View File

@ -1,7 +1,6 @@
[DEFAULT]
support-files =
head.js
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js

View File

@ -12,5 +12,8 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as events.js.
loadScripts({ name: "common.js", dir: MOCHITESTS_DIR }, "events.js");
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);

View File

@ -5,13 +5,13 @@
"use strict";
/* import-globals-from ../mochitest/common.js */
/* import-globals-from events.js */
/* import-globals-from ../mochitest/promisified-events.js */
/* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus,
invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName,
addAccessibleTask, findAccessibleChildByID, isDefunct,
CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, snippetToURL,
Cc, Cu, arrayFromChildren, forceGC */
Cc, Cu, arrayFromChildren, forceGC, contentSpawnMutation */
/**
* Current browser test directory path used to load subscripts.
@ -153,7 +153,7 @@ 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.editor || elm.localName == "textbox") {
if (elm.editor) {
elm.selectionStart = elm.selectionEnd = elm.value.length;
}
elm.focus();
@ -389,3 +389,42 @@ function forceGC() {
SpecialPowers.forceShrinkingGC();
SpecialPowers.forceCC();
}
/*
* This function spawns a content task and awaits expected mutation events from
* various content changes. It's good at catching events we did *not* expect. We
* do this advancing the layout refresh to flush the relocations/insertions
* queue.
*/
async function contentSpawnMutation(browser, waitFor, func, args = null) {
let onReorders = waitForEvents({ expected: waitFor.expected || [] });
let unexpectedListener = new UnexpectedEvents(waitFor.unexpected || []);
function tick() {
// 100ms is an arbitrary positive number to advance the clock.
// We don't need to advance the clock for a11y mutations, but other
// tick listeners may depend on an advancing clock with each refresh.
content.windowUtils.advanceTimeAndRefresh(100);
}
// This stops the refreh driver from doing its regular ticks, and leaves
// us in control.
await ContentTask.spawn(browser, null, tick);
// Perform the tree mutation.
await ContentTask.spawn(browser, args, func);
// Do one tick to flush our queue (insertions, relocations, etc.)
await ContentTask.spawn(browser, null, tick);
let events = await onReorders;
unexpectedListener.stop();
// Go back to normal refresh driver ticks.
await ContentTask.spawn(browser, null, function() {
content.windowUtils.restoreNormalRefresh();
});
return events;
}

View File

@ -1,7 +1,6 @@
[DEFAULT]
support-files =
head.js
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js

View File

@ -12,5 +12,8 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as events.js.
loadScripts({ name: "common.js", dir: MOCHITESTS_DIR }, "events.js");
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);

View File

@ -1,9 +1,10 @@
[DEFAULT]
support-files =
head.js
!/accessible/tests/browser/events.js
!/accessible/tests/browser/shared-head.js
!/accessible/tests/mochitest/*.js
[browser_aria_owns.js]
skip-if = true || (verify && !debug && (os == 'linux')) #Bug 1445513
[browser_searchbar.js]
[browser_shadowdom.js]

View File

@ -0,0 +1,52 @@
"use strict";
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
// eslint-disable-next-line camelcase
add_task(async function test_searchbar_a11y_tree() {
await SpecialPowers.pushPrefEnv({
set: [["browser.search.widget.inNavBar", true]],
});
let searchbar = await TestUtils.waitForCondition(
() => document.getElementById("searchbar"),
"wait for search bar to appear"
);
// Make sure the popup has been rendered so it shows up in the a11y tree.
let popup = document.getElementById("PopupSearchAutoComplete");
let promise = BrowserTestUtils.waitForEvent(popup, "popupshown", false);
searchbar.textbox.openPopup();
await promise;
promise = BrowserTestUtils.waitForEvent(popup, "popuphidden", false);
searchbar.textbox.closePopup();
await promise;
const TREE = {
role: ROLE_EDITCOMBOBOX,
children: [
// input element
{
role: ROLE_ENTRY,
children: [],
},
// context menu
{
role: ROLE_COMBOBOX_LIST,
children: [],
},
// result list
{
role: ROLE_GROUPING,
// not testing the structure inside the result list
},
],
};
testAccessibleTree(searchbar, TREE);
});

View File

@ -0,0 +1,38 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const REORDER = { expected: [[EVENT_REORDER, "container"]] };
// Dynamically inserted slotted accessible elements should be in
// the accessible tree.
const snippet = `
<script>
customElements.define("x-el", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.innerHTML =
"<div role='presentation'><slot></slot></div>";
}
});
</script>
<x-el id="container" role="group"><label id="l1">label1</label></x-el>
`;
addAccessibleTask(snippet, async function(browser, accDoc) {
let container = findAccessibleChildByID(accDoc, "container");
testChildrenIds(container, ["l1"]);
await contentSpawnMutation(browser, REORDER, function() {
let labelEl = content.document.createElement("label");
labelEl.id = "l2";
let containerEl = content.document.getElementById("container");
containerEl.appendChild(labelEl);
});
testChildrenIds(container, ["l1", "l2"]);
});

View File

@ -14,8 +14,11 @@ Services.scriptloader.loadSubScript(
);
// Loading and common.js from accessible/tests/mochitest/ for all tests, as
// well as events.js.
loadScripts({ name: "common.js", dir: MOCHITESTS_DIR }, "events.js");
// well as promisified-events.js.
loadScripts(
{ name: "common.js", dir: MOCHITESTS_DIR },
{ name: "promisified-events.js", dir: MOCHITESTS_DIR }
);
/*
* A test function for comparing the IDs of an accessible's children

View File

@ -1,5 +1,5 @@
load 448064.xhtml # This test instantiates a11y, so be careful about adding tests before it
load 471493.xul
load chrome://reftest/content/crashtests/accessible/tests/crashtests/471493.xhtml
asserts-if(!browserIsRemote,2) load 884202.html
load 890760.html
load 893515.html
@ -12,6 +12,6 @@ load 1494707.html
load 1503964.html
load 1655983.html
# last_test_to_unload_testsuite.xul MUST be the last test in the list because it
# last_test_to_unload_testsuite.xhtml MUST be the last test in the list because it
# is responsible for shutting down accessibility service affecting later tests.
skip-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&/^aarch64-msvc/.test(xulRuntime.XPCOMABI)) load last_test_to_unload_testsuite.xul
skip-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&/^aarch64-msvc/.test(xulRuntime.XPCOMABI)) load chrome://reftest/content/crashtests/accessible/tests/crashtests/last_test_to_unload_testsuite.xhtml

View File

@ -35,6 +35,10 @@ const XUL_EVENTS = CLICK_EVENTS | COMMAND_EVENT;
* // used with 'events', if missing then 'ID' is used instead.
* get targetID() {},
*
* // [optional] true to match DOM events bubbled up to the target,
* // false (default) to only match events fired directly on the target.
* get allowBubbling() {},
*
* // [optional] perform checks when 'click' event is handled if 'events'
* // is used.
* checkOnClickEvent: function() {},
@ -182,6 +186,17 @@ function checkerOfActionInvoker(aType, aTarget, aActionObj) {
this.eventTarget = aActionObj.eventTarget;
}
if (aActionObj && aActionObj.allowBubbling) {
// Normally, we add event listeners on the document. To catch bubbled
// events, we need to add the listener on the target itself.
this.eventTarget = "element";
// Normally, we only match an event fired directly on the target. Override
// this to match a bubbled event.
this.match = function(aEvent) {
return aEvent.currentTarget == aTarget;
};
}
this.phase = false;
this.getID = function getID() {

View File

@ -7,11 +7,11 @@ support-files =
[test_aria.html]
[test_controls.html]
[test_general.html]
[test_general.xul]
[test_general.xhtml]
[test_keys.html]
[test_keys_menu.xul]
[test_keys.xhtml]
[test_link.html]
[test_media.html]
[test_select.html]
[test_tree.xul]
[test_treegrid.xul]
[test_tree.xhtml]
[test_treegrid.xhtml]

View File

@ -7,6 +7,7 @@
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="nsIAccessible actions testing">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
@ -17,6 +18,10 @@
src="../events.js" />
<script type="application/javascript"
src="../actions.js" />
<script type="application/javascript"
src="../role.js" />
<script type="application/javascript"
src="../states.js" />
<script type="application/javascript">
<![CDATA[
@ -71,6 +76,22 @@
ID: "labelWithPopup",
actionName: "click",
events: CLICK_EVENTS
},
{
ID: "toolbarbutton_label",
actionName: "click",
targetID: "toolbarbutton",
events: XUL_EVENTS,
allowBubbling: true
},
{
ID: "menulist_label",
actionName: "click",
// focusChecker expects a unique focus event. However, there might
// still be pending focus events not caught by previous tests.
eventSeq: [
new invokerChecker(EVENT_FOCUS, getNode("menulist"))
]
}/*, // XXX: bug 490288
{
ID: "buttonmenu_item",
@ -136,7 +157,14 @@
tabindex="0"/>
<hbox>
<label id="name_entry_label" value="Name" control="name_entry"/>
<textbox id="name_entry"/>
<html:input id="name_entry"/>
</hbox>
<toolbarbutton id="toolbarbutton">
<label id="toolbarbutton_label">toolbarbutton</label>
</toolbarbutton>
<hbox>
<label id="menulist_label" control="menulist">menulist</label>
<menulist id="menulist"/>
</hbox>
</vbox>
</hbox>

View File

@ -4,6 +4,7 @@
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="Accessible XUL access keys and shortcut keys tests">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
@ -17,8 +18,8 @@
<![CDATA[
function openMenu(aMenuID, aMenuitemID)
{
this.menuNode = getNode(aMenuID),
this.menuitemNode = getNode(aMenuitemID),
this.menuNode = getNode(aMenuID);
this.menuitemNode = getNode(aMenuitemID);
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, this.menuNode)
@ -53,6 +54,11 @@
var gQueue = null;
function doTest()
{
// HTML element should get accessKey from associated XUL label.
let input = getAccessible("input");
is(input.accessKey, (MAC ? "⌃⌥i" : "Alt+Shift+i"),
"Wrong accessKey on input");
gQueue = new eventQueue();
gQueue.push(new openMenu("menu", "menuitem"));
gQueue.invoke(); // Will call SimpleTest.finish();
@ -78,6 +84,9 @@
</body>
<vbox flex="1">
<label control="input" accesskey="i">input</label>
<html:input id="input"/>
<keyset>
<key key="l" modifiers="control" id="key1"/>
</keyset>

View File

@ -7,9 +7,9 @@ support-files =
[test_listbox.html]
[test_obj.html]
[test_obj_css.html]
[test_obj_css.xul]
[test_obj_css.xhtml]
[test_obj_group.html]
[test_obj_group.xul]
[test_obj_group_tree.xul]
[test_obj_group.xhtml]
[test_obj_group_tree.xhtml]
[test_tag.html]
[test_xml-roles.html]

View File

@ -12,7 +12,7 @@
<script type="application/javascript"
src="../attributes.js"></script>
<script type="application/javascript"
src="../events.js"></script>
src="../promisified-events.js"></script>
<script type="application/javascript">
async function doTest() {
@ -25,7 +25,7 @@
testGroupAttrs("f", 6, 6);
// Remove c, reducing the set to 5.
let listbox = getAccessible("listbox");
let updated = waitForEventPromise(EVENT_REORDER, listbox);
let updated = waitForEvent(EVENT_REORDER, listbox);
c.remove();
await updated;
testGroupAttrs("a", 1, 5);
@ -34,7 +34,7 @@
testGroupAttrs("e", 4, 5);
testGroupAttrs("f", 5, 5);
// Now, remove the first element.
updated = waitForEventPromise(EVENT_REORDER, listbox);
updated = waitForEvent(EVENT_REORDER, listbox);
a.remove();
await updated;
testGroupAttrs("b", 1, 4);
@ -42,14 +42,14 @@
testGroupAttrs("e", 3, 4);
testGroupAttrs("f", 4, 4);
// Remove the last item.
updated = waitForEventPromise(EVENT_REORDER, listbox);
updated = waitForEvent(EVENT_REORDER, listbox);
f.remove();
await updated;
testGroupAttrs("b", 1, 3);
testGroupAttrs("d", 2, 3);
testGroupAttrs("e", 3, 3);
// Finally, remove the middle item.
updated = waitForEventPromise(EVENT_REORDER, listbox);
updated = waitForEvent(EVENT_REORDER, listbox);
d.remove();
await updated;
testGroupAttrs("b", 1, 2);
@ -76,7 +76,7 @@
<div id="d" role="option">Option d</div>
<div id="e" role="option">Option e</div>
<div id="f" role="option">Option f</div>
</div>
</div>
</body>
</html>

View File

@ -25,7 +25,6 @@
testCSSAttrs("display_mozgrid");
testCSSAttrs("display_mozgridgroup");
testCSSAttrs("display_mozgridline");
testCSSAttrs("display_mozstack");
testCSSAttrs("display_mozdeck");
testCSSAttrs("display_mozpopup");
@ -57,10 +56,8 @@
<vbox id="display_mozgrid" style="display: -moz-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_mozdeck" style="display: -moz-deck;" role="img"/>
<vbox id="display_mozpopup" style="display: -moz-popup;" role="img"/>
</hbox>
</window>

View File

@ -21,9 +21,11 @@
testAttrs("nav", {"xml-roles": "navigation"}, true);
testAttrs("header", {"xml-roles": "banner"}, true);
testAbsentAttrs("article_header", {"xml-roles": "banner"});
testAbsentAttrs("main_header", {"xml-roles": "banner"});
testAbsentAttrs("section_header", {"xml-roles": "banner"});
testAttrs("footer", {"xml-roles": "contentinfo"}, true);
testAbsentAttrs("article_footer", {"xml-roles": "contentinfo"});
testAbsentAttrs("main_footer", {"xml-roles": "contentinfo"});
testAbsentAttrs("section_footer", {"xml-roles": "contentinfo"});
testAttrs("aside", {"xml-roles": "complementary"}, true);
testAbsentAttrs("section", {"xml-roles": "region"}, true);
@ -157,6 +159,10 @@
<header id="article_header">a header within an article</header>
<footer id="article_footer">a footer within an article</footer>
</article>
<main id="main_with_header_and_footer">
<header id="main_header">a header within a main</header>
<footer id="main_footer">a footer within a main</footer>
</main>
<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>

Some files were not shown because too many files have changed in this diff Show More