import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

1
.cargo/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
config

6
.cargo/config.in Normal file
View file

@ -0,0 +1,6 @@
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'vendored-sources'
[source.vendored-sources]
directory = '@top_srcdir@/third_party/rust'

4
.clang-format Normal file
View file

@ -0,0 +1,4 @@
BasedOnStyle: Mozilla
# Ignore all comments because they aren't reflowed properly.
CommentPragmas: "^"

3
.clang-format-ignore Normal file
View file

@ -0,0 +1,3 @@
^mfbt/.*
^js/.*
^media/.*

252
.eslintignore Normal file
View file

@ -0,0 +1,252 @@
# Always ignore node_modules.
**/node_modules/**/*.*
# Exclude expected objdirs.
obj*/**
# We ignore all these directories by default, until we get them enabled.
# If you are enabling a directory, please add directory specific exclusions
# below.
addon-sdk/**
build/**
caps/**
chrome/**
config/**
db/**
docshell/**
dom/**
editor/**
embedding/**
extensions/**
gfx/**
gradle/**
hal/**
image/**
intl/**
ipc/**
js/**
layout/**
media/**
memory/**
mfbt/**
modules/**
mozglue/**
netwerk/**
nsprpub/**
other-licenses/**
parser/**
probes/**
python/**
rdf/**
startupcache/**
testing/**
!tools/lint/eslint/eslint-plugin-mozilla/
tools/lint/eslint/eslint-plugin-mozilla/node_modules/**
tools/**
uriloader/**
view/**
widget/**
xpcom/**
xpfe/**
xulrunner/**
# b2g exclusions (pref files).
b2g/app/b2g.js
b2g/graphene/graphene.js
b2g/locales/en-US/b2g-l10n.js
# browser/ exclusions
browser/app/**
browser/branding/**/firefox-branding.js
browser/base/content/browser-social.js
browser/base/content/nsContextMenu.js
browser/base/content/sanitizeDialog.js
browser/base/content/test/general/file_csp_block_all_mixedcontent.html
browser/base/content/test/urlbar/file_blank_but_not_blank.html
browser/base/content/newtab/**
browser/components/downloads/**
browser/components/privatebrowsing/**
browser/components/sessionstore/**
browser/components/tabview/**
# generated files in cld2
browser/components/translation/cld2/cld-worker.js
browser/extensions/pdfjs/**
# generated or library files in pocket
browser/extensions/pocket/content/panels/js/tmpl.js
browser/extensions/pocket/content/panels/js/vendor/**
browser/locales/**
# devtools/ exclusions
devtools/client/canvasdebugger/**
devtools/client/commandline/**
devtools/client/debugger/**
devtools/client/framework/**
!devtools/client/framework/selection.js
!devtools/client/framework/toolbox.js
devtools/client/jsonview/lib/**
devtools/client/memory/**
devtools/client/netmonitor/test/**
devtools/client/netmonitor/har/test/**
devtools/client/projecteditor/**
devtools/client/promisedebugger/**
devtools/client/responsivedesign/**
devtools/client/scratchpad/**
devtools/client/shadereditor/**
devtools/client/shared/*.jsm
devtools/client/shared/webgl-utils.js
devtools/client/shared/widgets/*.jsm
devtools/client/sourceeditor/test/*.js
devtools/client/webaudioeditor/**
devtools/client/webconsole/**
!devtools/client/webconsole/panel.js
!devtools/client/webconsole/jsterm.js
!devtools/client/webconsole/console-commands.js
devtools/client/webide/**
!devtools/client/webide/components/webideCli.js
devtools/server/*.js
devtools/server/*.jsm
!devtools/server/child.js
!devtools/server/css-logic.js
!devtools/server/main.js
!devtools/server/websocket-server.js
devtools/server/actors/**
!devtools/server/actors/csscoverage.js
!devtools/server/actors/inspector.js
!devtools/server/actors/highlighters/css-grid.js
!devtools/server/actors/highlighters/eye-dropper.js
!devtools/server/actors/layout.js
!devtools/server/actors/string.js
!devtools/server/actors/styles.js
!devtools/server/actors/webbrowser.js
!devtools/server/actors/webextension.js
devtools/server/performance/**
devtools/server/tests/**
devtools/shared/*.js
!devtools/shared/async-storage.js
!devtools/shared/async-utils.js
!devtools/shared/defer.js
!devtools/shared/event-emitter.js
!devtools/shared/indentation.js
!devtools/shared/loader-plugin-raw.jsm
!devtools/shared/task.js
devtools/shared/apps/**
devtools/shared/client/**
devtools/shared/discovery/**
devtools/shared/gcli/**
!devtools/shared/gcli/templater.js
devtools/shared/heapsnapshot/**
devtools/shared/layout/**
devtools/shared/locales/**
devtools/shared/performance/**
!devtools/shared/platform/**
devtools/shared/qrcode/**
devtools/shared/security/**
devtools/shared/shims/**
devtools/shared/tests/**
!devtools/shared/tests/unit/test_csslexer.js
devtools/shared/touch/**
devtools/shared/transport/**
!devtools/shared/transport/transport.js
!devtools/shared/transport/websocket-transport.js
devtools/shared/webconsole/test/**
devtools/shared/worker/**
!devtools/shared/worker/worker.js
# Ignore devtools pre-processed files
devtools/client/framework/toolbox-process-window.js
devtools/client/performance/system.js
devtools/client/webide/webide-prefs.js
devtools/client/preferences/**
# Ignore devtools third-party libs
devtools/shared/jsbeautify/*
devtools/shared/acorn/*
devtools/client/sourceeditor/tern/*
devtools/shared/node-properties/*
devtools/shared/pretty-fast/*
devtools/shared/sourcemap/*
devtools/shared/sprintfjs/*
devtools/shared/qrcode/decoder/*
devtools/shared/qrcode/encoder/*
devtools/client/shared/demangle.js
devtools/client/shared/vendor/*
devtools/client/sourceeditor/codemirror/*.js
devtools/client/sourceeditor/codemirror/**/*.js
devtools/client/sourceeditor/test/codemirror/*
devtools/client/inspector/markup/test/lib_*
devtools/client/jsonview/lib/require.js
devtools/server/actors/utils/automation-timeline.js
# Ignore devtools files testing sourcemaps / code style
devtools/client/debugger/test/mochitest/code_binary_search.js
devtools/client/debugger/test/mochitest/code_math.min.js
devtools/client/debugger/test/mochitest/code_math_bogus_map.js
devtools/client/debugger/test/mochitest/code_ugly*
devtools/client/debugger/test/mochitest/code_worker-source-map.js
devtools/client/framework/test/code_ugly*
devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
devtools/server/tests/unit/setBreakpoint*
# mobile/android/ exclusions
mobile/android/tests/
# Uses `#filter substitution`
mobile/android/b2gdroid/app/b2gdroid.js
mobile/android/app/mobile.js
mobile/android/chrome/content/healthreport-prefs.js
# Uses `#expand`
mobile/android/chrome/content/about.js
# Not much JS to lint and non-standard at that
mobile/android/installer/
mobile/android/locales/
# Non-standard `(catch ex if ...)`
mobile/android/chrome/content/browser.js
mobile/android/components/Snippets.js
# Bug 1178739: Ignore this file as a quick fix for "Illegal yield expression"
mobile/android/modules/HomeProvider.jsm
# services/ exclusions
# Uses `#filter substitution`
services/sync/modules/constants.js
# toolkit/ exclusions
# Not part of the default build
toolkit/components/help/**
# Intentionally invalid JS
toolkit/components/workerloader/tests/moduleF-syntax-error.js
# Tests old non-star function generators
toolkit/modules/tests/xpcshell/test_task.js
# Not yet updated
toolkit/components/osfile/**
# External code:
toolkit/components/microformats/test/**
toolkit/components/reader/Readability.js
toolkit/components/reader/JSDOMParser.js
# Uses preprocessing
toolkit/content/widgets/wizard.xml
toolkit/components/jsdownloads/src/DownloadIntegration.jsm
toolkit/components/url-classifier/**
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/identity/FirefoxAccounts.jsm
toolkit/modules/AppConstants.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/extensions/internal/AddonConstants.jsm
toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
toolkit/webapps/**
# Third party
toolkit/modules/third_party/**
#NSS
security/nss/**

14
.eslintrc.js Normal file
View file

@ -0,0 +1,14 @@
"use strict";
module.exports = {
// When adding items to this file please check for effects on sub-directories.
"plugins": [
"mozilla"
],
"rules": {
"mozilla/import-globals": "warn",
},
"env": {
"es6": true
},
};

5
.flake8 Normal file
View file

@ -0,0 +1,5 @@
[flake8]
# See http://pep8.readthedocs.io/en/latest/intro.html#configuration
ignore = E121, E123, E126, E133, E226, E241, E242, E704, W503, E402
max-line-length = 99
filename = *.py, +.lint

189
.gdbinit Normal file
View file

@ -0,0 +1,189 @@
# .gdbinit file for debugging Mozilla
# You may need to put an 'add-auto-load-safe-path' command in your
# $HOME/.gdbinit file to get GDB to trust this file. If your builds are
# generally in $HOME/moz, then you can say:
#
# add-auto-load-safe-path ~/moz
# Don't stop for the SIG32/33/etc signals that Flash produces
handle SIG32 noprint nostop pass
handle SIG33 noprint nostop pass
handle SIGPIPE noprint nostop pass
# Don't stop for certain other signals where it's not useful,
# such as the SIG64 signals triggered by the Linux
# sandboxing code on older kernels.
handle SIG38 noprint nostop pass
handle SIG64 noprint nostop pass
# Show the concrete types behind nsIFoo
set print object on
# run when using the auto-solib-add trick
def prun
tbreak main
run
set auto-solib-add 0
cont
end
# run -mail, when using the auto-solib-add trick
def pmail
tbreak main
run -mail
set auto-solib-add 0
cont
end
# Define a "pu" command to display PRUnichar * strings (100 chars max)
# Also allows an optional argument for how many chars to print as long as
# it's less than 100.
def pu
set $uni = $arg0
if $argc == 2
set $limit = $arg1
if $limit > 100
set $limit = 100
end
else
set $limit = 100
end
# scratch array with space for 100 chars plus null terminator. Make
# sure to not use ' ' as the char so this copy/pastes well.
set $scratch = "____________________________________________________________________________________________________"
set $i = 0
set $scratch_idx = 0
while (*$uni && $i++ < $limit)
if (*$uni < 0x80)
set $scratch[$scratch_idx++] = *(char*)$uni++
else
if ($scratch_idx > 0)
set $scratch[$scratch_idx] = '\0'
print $scratch
set $scratch_idx = 0
end
print /x *(short*)$uni++
end
end
if ($scratch_idx > 0)
set $scratch[$scratch_idx] = '\0'
print $scratch
end
end
# Define a "ps" command to display subclasses of nsAC?String. Note that
# this assumes strings as of Gecko 1.9 (well, and probably a few
# releases before that as well); going back far enough will get you
# to string classes that this function doesn't work for.
def ps
set $str = $arg0
if (sizeof(*$str.mData) == 1 && ($str.mFlags & 1) != 0)
print $str.mData
else
pu $str.mData $str.mLength
end
end
# Define a "pa" command to display the string value for an nsIAtom
def pa
set $atom = $arg0
if (sizeof(*((&*$atom)->mString)) == 2)
pu (&*$atom)->mString
end
end
# define a "pxul" command to display the type of a XUL element from
# an nsXULElement* pointer.
def pxul
set $p = $arg0
print $p->mNodeInfo.mRawPtr->mInner.mName->mStaticAtom->mString
end
# define a "prefcnt" command to display the refcount of an XPCOM obj
def prefcnt
set $p = $arg0
print ((nsPurpleBufferEntry*)$p->mRefCnt.mTagged)->mRefCnt
end
# define a "ptag" command to display the tag name of a content node
def ptag
set $p = $arg0
pa $p->mNodeInfo.mRawPtr->mInner.mName
end
##
## nsTArray
##
define ptarray
if $argc == 0
help ptarray
else
set $size = $arg0.mHdr->mLength
set $capacity = $arg0.mHdr->mCapacity
set $size_max = $size - 1
set $elts = $arg0.Elements()
end
if $argc == 1
set $i = 0
while $i < $size
printf "elem[%u]: ", $i
p *($elts + $i)
set $i++
end
end
if $argc == 2
set $idx = $arg1
if $idx < 0 || $idx > $size_max
printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
else
printf "elem[%u]: ", $idx
p *($elts + $idx)
end
end
if $argc == 3
set $start_idx = $arg1
set $stop_idx = $arg2
if $start_idx > $stop_idx
set $tmp_idx = $start_idx
set $start_idx = $stop_idx
set $stop_idx = $tmp_idx
end
if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max
printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
else
set $i = $start_idx
while $i <= $stop_idx
printf "elem[%u]: ", $i
p *($elts + $i)
set $i++
end
end
end
if $argc > 0
printf "nsTArray length = %u\n", $size
printf "nsTArray capacity = %u\n", $capacity
printf "Element "
whatis *$elts
end
end
document ptarray
Prints nsTArray information.
Syntax: ptarray
Note: idx, idx1 and idx2 must be in acceptable range [0...size()-1].
Examples:
ptarray a - Prints tarray content, size, capacity and T typedef
ptarray a 0 - Prints element[idx] from tarray
ptarray a 1 2 - Prints elements in range [idx1..idx2] from tarray
end
def js
call DumpJSStack()
end
def ft
call $arg0->DumpFrameTree()
end
source .gdbinit_python

5
.gdbinit_python Normal file
View file

@ -0,0 +1,5 @@
python
import sys
sys.path.append('python/gdbpp/')
import gdbpp
end

124
.gitignore vendored Normal file
View file

@ -0,0 +1,124 @@
# .gitignore - List of filenames git should ignore
# Filenames that should be ignored wherever they appear
*~
*.pyc
*.pyo
TAGS
tags
ID
.DS_Store*
*.pdb
*.egg-info
# Vim swap files.
.*.sw[a-z]
# Emacs directory variable files.
**/.dir-locals.el
# User files that may appear at the root
/.mozconfig*
/mozconfig
/configure
/old-configure
/config.cache
/config.log
/.clang_complete
/machrc
/.machrc
# Empty marker file that's generated when we check out NSS
security/manager/.nss.checkout
# Build directories
/obj*/
# Build directories for js shell
_DBG.OBJ/
_OPT.OBJ/
# SpiderMonkey configury
js/src/configure
js/src/old-configure
js/src/autom4te.cache
# SpiderMonkey test result logs
js/src/tests/results-*.html
js/src/tests/results-*.txt
# Java HTML5 parser classes
parser/html/java/htmlparser/
parser/html/java/javaparser/
# Ignore the files and directory that Eclipse IDE creates
.project
.cproject
.settings/
# Ignore the files and directory that JetBrains IDEs create.
/.idea/
*.iml
# Gradle cache.
/.gradle/
# Local Gradle configuration properties.
/local.properties
# Python virtualenv artifacts.
python/psutil/**/*.so
python/psutil/**/*.pyd
python/psutil/build/
# Ignore chrome.manifest files from the devtools loader
devtools/client/chrome.manifest
devtools/shared/chrome.manifest
# Ignore node_modules directories in devtools
devtools/**/node_modules
# Tag files generated by GNU Global
GTAGS
GRTAGS
GSYMS
GPATH
# Git clone directory for updating web-platform-tests
testing/web-platform/sync/
# Android Gradle artifacts.
mobile/android/gradle/.gradle
# XCode project cruft
embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
# Ignore mozharness execution files
testing/mozharness/.tox/
testing/mozharness/build/
testing/mozharness/logs/
testing/mozharness/.coverage
testing/mozharness/nosetests.xml
# Ignore node_modules
tools/lint/eslint/node_modules/
# Ignore talos virtualenv and tp5n files.
# The tp5n set is supposed to be decompressed at
# testing/talos/talos/page_load_test/tp5n in order to run tests like tps
# locally. Similarly, running talos requires a Python package virtual
# environment. Both the virtual environment and tp5n files end up littering
# the status command, so we ignore them.
testing/talos/.Python
testing/talos/bin/
testing/talos/include/
testing/talos/lib/
testing/talos/talos/tests/tp5n.zip
testing/talos/talos/tests/tp5n
testing/talos/talos/tests/devtools/damp.manifest.develop
# Ignore files created when running a reftest.
lextab.py
# tup database
/.tup

5
.hg_archival.txt Normal file
View file

@ -0,0 +1,5 @@
repo: 8ba995b74e18334ab3707f27e9eb8f4e37ba3d29
node: a7c8e85285e290fdb1325edfb37e905a3779f3cd
branch: default
tag: FIREFOX_52_6_0esr_BUILD2
tag: FIREFOX_52_6_0esr_RELEASE

134
.hgignore Normal file
View file

@ -0,0 +1,134 @@
# .hgignore - List of filenames hg should ignore
# Filenames that should be ignored wherever they appear
~$
\.py(c|o)$
(?i)(^|/)TAGS$
(^|/)ID$
(^|/)\.DS_Store$
\.pdb
\.egg-info
# Vim swap files.
^\.sw.$
.[^/]*\.sw.$
# Emacs directory variable files.
\.dir-locals\.el
# User files that may appear at the root
^\.mozconfig
^mozconfig*
^configure$
^old-configure$
^config\.cache$
^config\.log$
^\.clang_complete
^\.?machrc$
# Empty marker file that's generated when we check out NSS
^security/manager/\.nss\.checkout$
# Build directories
^obj
# Build directories for js shell
_DBG\.OBJ/
_OPT\.OBJ/
^js/src/.*-obj/
# SpiderMonkey configury
^js/src/configure$
^js/src/old-configure$
^js/src/autom4te.cache$
# SpiderMonkey test result logs
^js/src/tests/results-.*\.(html|txt)$
^js/src/devtools/rootAnalysis/t/out
# Java HTML5 parser classes
^parser/html/java/(html|java)parser/
# SVN directories
\.svn/
# Ignore the files and directory that Eclipse IDE creates
\.project$
\.cproject$
\.settings/
# Ignore the files and directory that JetBrains IDEs create.
\.idea/
\.iml$
# Gradle cache.
^.gradle/
# Local Gradle configuration properties.
^local.properties$
# Python stuff installed at build time.
^python/psutil/.*\.so
^python/psutil/.*\.pyd
^python/psutil/build/
# Git repositories
.git/
# Ignore chrome.manifest files from the devtools loader
^devtools/client/chrome.manifest$
^devtools/shared/chrome.manifest$
# Ignore node_modules directories in devtools
^devtools/.*/node_modules/
# git checkout of libstagefright
^media/libstagefright/android$
# Tag files generated by GNU Global
GTAGS
GRTAGS
GSYMS
GPATH
# Git clone directory for updating web-platform-tests
^testing/web-platform/sync/
# Android Gradle artifacts.
^mobile/android/gradle/.gradle
# XCode project cruft
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
# Ignore mozharness execution files
^testing/mozharness/.tox/
^testing/mozharness/build/
^testing/mozharness/logs/
^testing/mozharness/.coverage
^testing/mozharness/nosetests.xml
# Ignore tox generated dir
.tox/
# Ignore node_modules
^tools/lint/eslint/node_modules/
# Ignore talos virtualenv and tp5n files.
# The tp5n set is supposed to be decompressed at
# testing/talos/talos/page_load_test/tp5n in order to run tests like tps
# locally. Similarly, running talos requires a Python package virtual
# environment. Both the virtual environment and tp5n files end up littering
# the status command, so we ignore them.
^testing/talos/.Python
^testing/talos/bin/
^testing/talos/include/
^testing/talos/lib/
^testing/talos/talos/tests/tp5n.zip
^testing/talos/talos/tests/tp5n
^testing/talos/talos/tests/devtools/damp.manifest.develop
# Ignore files created when running a reftest.
^lextab.py$
# tup database
^\.tup

489
.hgtags Normal file
View file

@ -0,0 +1,489 @@
df7a3c8ffeeaba229067efee5a20e21dae0dd877 MOZILLA_1_9_a4_BASE
4209e16b58411750ac73f761023e46b76b793e2c MOZILLA_1_9_a6_BASE
66a5c7bce7ee86a820d3c0d54fa07cb719be751c MOZILLA_1_9_a7_BASE
caeba7562e495a9f604984df0b48b6f99bec3e2e FENNEC_M4
9d9941eacb14827fdab4716710042fdde84eb60d FIREFOX_3_1a1_RELEASE
9d9941eacb14827fdab4716710042fdde84eb60d FIREFOX_3_1a1_BUILD1
c1d7e318a27574c995631fec166ad42672475702 FIREFOX_3_1a1_BUILD2
c1d7e318a27574c995631fec166ad42672475702 FIREFOX_3_1a1_RELEASE
afc4ee509d9ca3bb4031015c3c22963dcb4b7e7f FIREFOX_3_1a1_RELEASE
afc4ee509d9ca3bb4031015c3c22963dcb4b7e7f FIREFOX_3_1a1_BUILD2
f197b51bbc29a30860e750ee87fd0a047a024f2e FIREFOX_3_1a2_BUILD1
f197b51bbc29a30860e750ee87fd0a047a024f2e FIREFOX_3_1a2_RELEASE
269af1ed75649989575d41f53a12048015c6d50e FENNEC_M8
920a4326d1087b174c2fa2b9a8358e12c697022c SEAMONKEY_2_0a1_BUILD1
920a4326d1087b174c2fa2b9a8358e12c697022c SEAMONKEY_2_0a1_RELEASE
f197b51bbc29a30860e750ee87fd0a047a024f2e UPDATE_PACKAGING_R5
f197b51bbc29a30860e750ee87fd0a047a024f2e -m
f197b51bbc29a30860e750ee87fd0a047a024f2e Adding UPDATE_PACKAGING_R5 tag in order to make patcher work.
15cb5d25db054d2d0b56869a2f6351388bfcddcd THUNDERBIRD_3_0a3_RELEASE
15cb5d25db054d2d0b56869a2f6351388bfcddcd THUNDERBIRD_3_0a3_BUILD1
0000000000000000000000000000000000000000 -m
0000000000000000000000000000000000000000 Adding UPDATE_PACKAGING_R5 tag in order to make patcher work.
f197b51bbc29a30860e750ee87fd0a047a024f2e UPDATE_PACKAGING_R6
d7d64f68423b68a671f623f123e90057ebc49dac UPDATE_PACKAGING_R6
0000000000000000000000000000000000000000 THUNDERBIRD_3_0a3_BUILD1
0000000000000000000000000000000000000000 THUNDERBIRD_3_0a3_RELEASE
0cd41f5990807fb6ab52cb59ba3c8e8247281045 GECKO_1_9_1_BASE
8df5a90281cd4d75835e4b7696da200555eed15f GECKO_1_9_1_BASE
8a601ed6bc4c7b3d1e35aa9e81f257512d984bd5 FENNEC_A2
d7d64f68423b68a671f623f123e90057ebc49dac UPDATE_PACKAGING_R7
fb32f6e1859c07846a01b4478a7b1678019e0b45 UPDATE_PACKAGING_R7
f817a4378f32b1ad0a7c4b5a9949586dba816da5 FENNEC_M11
5c1e7c779b6edc8ff912001990edc579f80597f4 FENNEC_B1
fe9cc55b8db7f56f7e68a246acba363743854979 UPDATE_PACKAGING_R8
6fd4bb500d425c406c1b52f66e5b195b20ae5e0a chromium-import-r15462
6fd4bb500d425c406c1b52f66e5b195b20ae5e0a chromium-import-latest
376b78fc72230aaf2ca4e279a8f4ef1efd4a1d9f GECKO_1_9_2_BASE
941ad9d7d079246481f365c3cfbfc75a5bbefc94 last-mozilla-central
2bae3bbf866e7de2a4b2377e7c2f52cc9ac14a22 last-mozilla-central
2bae3bbf866e7de2a4b2377e7c2f52cc9ac14a22 last-mozilla-central
65c1582465efe99899189519fccaf7b2826fcb2e last-mozilla-central
65c1582465efe99899189519fccaf7b2826fcb2e last-mozilla-central
27937722da69ad0e8fd140a00671413068226a5b last-mozilla-central
27937722da69ad0e8fd140a00671413068226a5b last-mozilla-central
a732c6d3c078f80635255c78bfaadffa5828a8a5 last-mozilla-central
a732c6d3c078f80635255c78bfaadffa5828a8a5 last-mozilla-central
925595f3c08634cc42e33158ea6858bb55623ef7 last-mozilla-central
dba2abb7db57078c5a4810884834d3056a5d56c2 last-mozilla-central
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R9
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R10
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R11
0327e126ea245112c0aa7283fee154e084866fb5 bsmedberg-static-xpcom-registration-base
0327e126ea245112c0aa7283fee154e084866fb5 bsmedberg-static-xpcom-registration-base
2f83edbbeef0de7dd901411d270da61106c8afae bsmedberg-static-xpcom-registration-base
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R12
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R13
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R11_1_MU
e56ecd8b3a68c158025207c5fd081d043e28f5ce GECKO_2_0_BASE
e273946b74c8d631ed86bd74ba9afe0e67b12378 GECKO_2_1_BASE
b70744835d94e54eec97b8fd186c96da5708a506 PRE_MOBILE_MERGE
b70744835d94e54eec97b8fd186c96da5708a506 PRE_MOBILE_MERGE_20110406
a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE
a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE_20110406
a95d426422816513477e5863add1b00ac7041dcb AURORA_BASE_20110412
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 AURORA_BASE_20110524
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R15
54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108
a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
0000000000000000000000000000000000000000 AURORA_BASE_20120131
0000000000000000000000000000000000000000 AURORA_BASE_20120131
bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131
b6627f28b7ec17e1b46a594df0f780d3a40847e4 FIREFOX_AURORA_13_BASE
357da346ceb705d196a46574804c7c4ec44ac186 FIREFOX_AURORA_14_BASE
26dcd1b1a20893ad99341c61c6b1239ff1523858 FIREFOX_AURORA_15_BASE
0accd12a8e7e217836ea3f1ee7c411913fc75d8e FIREFOX_AURORA_16_BASE
0000000000000000000000000000000000000000 FIREFOX_AURORA_16_BASE
9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
9697eadafa13b4e9233b39aaeecfeac79503cb54 FIREFOX_AURORA_16_BASE
6fdf9985acfe6f939da584b2559464ab22264fe7 FIREFOX_AURORA_16_BASE
fd72dbbd692012224145be1bf13df1d7675fd277 FIREFOX_AURORA_17_BASE
2704e441363fe2a48e992dfac694482dfd82664a FIREFOX_AURORA_18_BASE
cf8750abee06cde395c659f8ecd8ae019d7512e3 FIREFOX_AURORA_19_BASE
5bb309998e7050c9ee80b0147de1e473f008e221 FIREFOX_AURORA_20_BASE
cc37417e2c284aed960f98ffa479de4ccdd5c7c3 FIREFOX_AURORA_21_BASE
1c070ab0f9db59f13423b9c1db60419f7a9098f9 FIREFOX_AURORA_22_BASE
d7ce9089999719d5186595d160f25123a4e63e39 FIREFOX_AURORA_23_BASE
8d3810543edccf4fbe458178b88dd4a6e420b010 FIREFOX_AURORA_24_BASE
ad0ae007aa9e03cd74e9005cd6652e544139b3b5 FIREFOX_AURORA_25_BASE
2520866d58740851d862c7c59246a4e3f8b4a176 FIREFOX_AURORA_26_BASE
05025f4889a0bf4dc99ce0c244c750adc002f015 FIREFOX_AURORA_27_BASE
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
ba2cc1eda988a1614d8986ae145d28e1268409b9 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
ba2cc1eda988a1614d8986ae145d28e1268409b9 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
0000000000000000000000000000000000000000 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
0000000000000000000000000000000000000000 FIREFOX_AURORA_29_BASE-m
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
07f5580d8a54e8311fa7641c907065a88de19e08 FIREFOX_AURORA_29_END
3776f72f1967a7068879501eb7c08920032785b8 B2G_1_4_20140317_MERGEDAY
aa70a6ce178a6839cd9e55761c4ac31d43ee7bd9 FIREFOX_BETA_29_END
ba4a8f81efdcf000414f192342ccbd14c9626c36 RELEASE_BASE_20140602
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
83c9853e136451474dfa6d1aaa60a7fca7d2d83a FIREFOX_AURORA_30_BASE
cfde3603b0206e119abea76fdd6e134b634348f1 FIREFOX_AURORA_31_BASE
ab25447610b532eb4c32524a7dbc56a21eeaabb8 FIREFOX_BETA_30_BASE
ab25447610b532eb4c32524a7dbc56a21eeaabb8 FIREFOX_AURORA_30_END
6c18811bcd1b319801fd97aeb09c41b963863968 FIREFOX_BETA_30_END
1772e55568e420f8c7fbf7b9434157e9f419c8f1 FIREFOX_RELEASE_31_BASE
40b1b0712d7b53219a0404e78eec4e6a2796423e FIREFOX_RELEASE_30_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
16f3cac5e8fe471e12f76d6a94a477b14e78df7c FIREFOX_AURORA_32_BASE
f11b164d75442617c4f046177d2ab913ed03a318 FIREFOX_BETA_31_BASE
f11b164d75442617c4f046177d2ab913ed03a318 FIREFOX_AURORA_31_END
dc2fd26b301375f15c935f00fe6283d3e3bc1efc B2G_2_0_20140609_MERGEDAY
d69cd84b6824e018e0906cab0464e11e97a9bdca FIREFOX_BETA_32_BASE
d69cd84b6824e018e0906cab0464e11e97a9bdca FIREFOX_BETA_32_BASE
0000000000000000000000000000000000000000 FIREFOX_BETA_32_BASE
0000000000000000000000000000000000000000 FIREFOX_BETA_32_BASE
ac396ad5a32d60ae5b7eebe5416fdd46e9e12be1 FIREFOX_BETA_32_BASE
6befadcaa68524d24960d8342e00503e4edc1869 FIREFOX_BETA_31_END
92ad4cfa9435fbe136c61071041812f90bc8d89e FIREFOX_RELEASE_32_BASE
cd52a7f8954809fd893196dc857f81b0cb61717c FIREFOX_RELEASE_31_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
dc23164ba2a289a8b22902e30990c77d9677c214 FIREFOX_AURORA_33_BASE
a104ddcd4cdbf950f1755dfaf5a278d53570655f FIREFOX_AURORA_32_END
114b010b6bf1a0efee03f003e54ed6fa00909972 FIREFOX_BETA_33_BASE
ebd0ee3e97dc2756d979261b29f173638fe6aeb6 FIREFOX_BETA_32_END
e8360a0c7d7483491e064c7cd445a94004af0a83 FIREFOX_RELEASE_33_BASE
4641475ee1f3ec3e723e932e0f4f3f3fa7c55a5c FIREFOX_RELEASE_32_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
c360f3d1c00d73b0c1fb0a2c0da525cb55e58b83 FIREFOX_AURORA_34_BASE
9f1aad8e807cc283aafbc14caa3d4775e8d0535c FIREFOX_AURORA_33_END
5b8210dcf52a795646bf0c8a32082a2ed7c4f537 B2G_2_1_20140902_MERGEDAY
e85828ce78a80e2ccda98972d69d5f750335c4ab FIREFOX_BETA_34_BASE
8574e35b517785acc905327f4994e96576218fa8 FIREFOX_BETA_33_END
e247a7f7c43842941bdb4207ca1b9d8881798997 FIREFOX_RELEASE_34_BASE
a47b1b720b67b018a9cb106178de53a363641392 FIREFOX_RELEASE_33_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
cec1a116c4f9a3e887d52e9a26e8bbec200fe162 FIREFOX_AURORA_35_BASE
2608561c091ae83cc85e38740feffa5bfc6b5ed4 FIREFOX_AURORA_34_END
390a34a40ea4e7f4d24b3ed83778e0f408411fcc FIREFOX_BETA_35_BASE
a3cc435fd3c315e5dfe9329d03d5943bb893cced FIREFOX_BETA_34_END
fb06fa0600ab95db48212a237c79b650cac213c5 FIREFOX_RELEASE_35_BASE
f4217563f1568c478c1ddf1647098946e51bc7f8 FIREFOX_RELEASE_34_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
ca89fe55717059e4e43040d16d260765ffa9dca7 FIREFOX_AURORA_36_BASE
6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
ca89fe55717059e4e43040d16d260765ffa9dca7 FIREFOX_AURORA_36_BASE
0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
b297a6727acfd21e757ddd38cd61894812666265 FIREFOX_AURORA_36_BASE
0cf828669d5a0911b6f2b83d501eeef5bdf9905e FIREFOX_AURORA_35_END
75177371cb85baaa9d623f56d849a5c21d18040f FIREFOX_BETA_36_BASE
137baee3dda45c6a3b38be74f5709c24f7c7701a FIREFOX_BETA_35_END
948a2c2e31d4b7eaa282ddeb327708605e3cc7fa FIREFOX_RELEASE_36_BASE
d57ff45ca4fd7ccf1cb924030abf6c7d108eaab0 FIREFOX_RELEASE_35_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
2c951493eef5b50b8085ef78ffe0d7902ff3d593 FIREFOX_AURORA_37_BASE
1bc9beda018a42bdd5f63fc9fc46facf0c6f37ec FIREFOX_AURORA_36_END
030fa1665346dfa94d1f72a1c7830644664ecf08 FIREFOX_BETA_37_BASE
7d4016a05dd32bf2d726975ba9175bb13fc9ea97 FIREFOX_BETA_36_END
196c6575593d6e8997402fb458bf8ed2f954fa4a FIREFOX_RELEASE_37_BASE
58fe203296af528cc711dc314e4769a902e3852f FIREFOX_RELEASE_36_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
d55b99e8010728b0c802e75e967d2a853122dd30 FIREFOX_AURORA_37_END
8bc9656cad94db48cd44f3947f1b3b1d8c57768a FIREFOX_BETA_38_BASE
b41c57eefd69242fc9664a3e5c2dcaa46840051c FIREFOX_BETA_37_END
fc98815acf5f041c6269fd256a68af8a92ba222a FIREFOX_RELEASE_38_BASE
b95583c8e7e7a7ce629b4d4551747e818367d0a0 FIREFOX_RELEASE_37_END
f33925facceefe32f6347faed2d805551d82e60b FIREFOX_RELEASE_38_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
1b6bf6612c0f4d4fee81d18bf18016e692f874e1 FIREFOX_AURORA_39_BASE
d92bf011e305a4a4ad6bb98192a53b86bd64b813 FIREFOX_AURORA_38_END
5e851d50fb9b8d5a28e4fcd4731dd0f17e8fb4b9 FIREFOX_BETA_39_BASE
85229fbaf01713caa9ad26c7d3afec271494113c FIREFOX_BETA_38_END
5fecfbec2e3c934d4646a739bea60d3c93a35f9e FIREFOX_RELEASE_39_BASE
f33925facceefe32f6347faed2d805551d82e60b FIREFOX_RELEASE_38_END
971ab60e21d340e0407d2b9eb82fca67a4a9f1cb FIREFOX_RELEASE_38_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
66a95a483d2c77dfc387019336d18093acd6aac2 FIREFOX_AURORA_40_BASE
eead3ccdf2d11feefc12907467cebbe07aa91ea9 FIREFOX_AURORA_39_END
d77cf39268848f8a7e9b38082348b6cd4d1f3b5e FIREFOX_BETA_40_BASE
0b0822cabbb95d8509852f90c0b7df5da0a4cabc FIREFOX_BETA_39_END
34e00eb800c52e33059d37f6e41fb255b4bae6b8 FIREFOX_RELEASE_40_BASE
ec21f96665f7d3fdd5d7944c90373938390096d7 FIREFOX_RELEASE_39_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
312c68b16549de9cea1557f461d5d234bd5e0a7d FIREFOX_AURORA_41_BASE
9d83ab013ab61c7f6e987bf0e7cbab1a1aed1ca8 FIREFOX_AURORA_40_END
a019592053c4d93fbbafab8d0bd709529e3746de FIREFOX_BETA_41_BASE
f147014ff61a12480d377c8bde1f90891772540f FIREFOX_BETA_40_END
6c0329aacb73ab0510c6f1026ef066dfaed9139c FIREFOX_RELEASE_41_BASE
9c898cde2175e9e98b916d996ed286a9dff0c853 FIREFOX_RELEASE_40_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
7a19194812eb767bee7cdf8fc36ba9a383c1bead FIREFOX_AURORA_42_BASE
d561dc208e61b2f2b4e82ab61710e14f56da4ddb FIREFOX_AURORA_41_END
61bbc30704aa104e9929c719c0fd7957f96f00ea FIREFOX_BETA_42_BASE
a5bf9cf6777519663e8e1db553727e59d3ad5681 FIREFOX_BETA_41_END
3f2ff85b2f16c1fd161dc5ba77a5f3f2c52fd127 FIREFOX_RELEASE_42_BASE
dc3a2ec52077a5cea772cdb267380f7debc3080b FIREFOX_RELEASE_41_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
fcef8ded82219c89298b4e376cfbdfba79a1d35a FIREFOX_AURORA_43_BASE
b0e20ff87b175424edec414215b325918ccb4792 FIREFOX_AURORA_42_END
41fdefd640f368bccdeafe6446d42c0a5ad22797 FIREFOX_BETA_43_BASE
0ec8472a93ac0c7ef0e98ebb91ac780bde12d5a5 FIREFOX_BETA_42_END
38ffeba26f3e420312e04cb3afb408f4c66a6f2e FIREFOX_RELEASE_43_BASE
56320bc06404b926b475051ba643950bd78cf221 FIREFOX_RELEASE_42_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
67a788db9f07822cfef52351bbbe3745dff8bd7f FIREFOX_AURORA_44_BASE
9d3bc275a924a84ab5f34df58c566af0f87479d0 FIREFOX_AURORA_43_END
b717b80eec62a7ba9b8842487f157b68c1419edd FIREFOX_BETA_44_BASE
366dd290472633b06f0942d7737c34e942e0916a FIREFOX_BETA_43_END
ef3cfadfccb97588653ae06eefdac28ec447c1f6 FIREFOX_RELEASE_44_BASE
af39a90c443c2fda798c2797e196eeb5a8b5cedd FIREFOX_RELEASE_43_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
99137d6d4061f408ae0869122649d8bdf489cc30 FIREFOX_AURORA_45_BASE
4fbd53613240c431522521b953d5a62692909e65 FIREFOX_AURORA_44_END
3bfa5bc61b626761d487b45c170b115259f69d6b FIREFOX_BETA_45_BASE
6a7547e4a0f0e213bb8487c773e16543cbea8c73 FIREFOX_BETA_44_END
3a9789adadcd0de9ee31b16a89a11985822c6a11 FENNEC_45_0b1_RELEASE
6c0fd1f666e70f2b11f7083f9e7bf4c844a3716a FENNEC_45_0b1_RELEASE
3a9789adadcd0de9ee31b16a89a11985822c6a11 FENNEC_45_0b1_BUILD1
6c0fd1f666e70f2b11f7083f9e7bf4c844a3716a FENNEC_45_0b1_BUILD1
5e1da6523e97d7f8b01004bbe33118ac784b40ea FIREFOX_45_0b1_RELEASE
6c0fd1f666e70f2b11f7083f9e7bf4c844a3716a FIREFOX_45_0b1_RELEASE
5e1da6523e97d7f8b01004bbe33118ac784b40ea FIREFOX_45_0b1_BUILD1
6c0fd1f666e70f2b11f7083f9e7bf4c844a3716a FIREFOX_45_0b1_BUILD1
bbe048ab30ad3321a6505697703e5fee20e91343 FIREFOX_RELEASE_45_BASE
5fe8de3ca9bbf8bc18259c3490aca55c97e31979 FIREFOX_RELEASE_44_END
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
67c66c2878aed17ae3096d7db483ddbb2293c503 FIREFOX_AURORA_46_BASE
d82221ba4219e4ac04ecfe2a5301703411e176fa FIREFOX_AURORA_45_END
89effb961712d4a5f84e90bcdd4e421ed6645eea FIREFOX_BETA_46_BASE
78fe98c670fcc9a1548ac655ae9a488d940fd9c8 FIREFOX_BETA_45_END
fb3494d06dfb73e26df72ca7a4bc4ef5ebf8795c FIREFOX_46_0b1_RELEASE
5904e3eb711dd263a6d2deb63b14e0c44e577054 FIREFOX_46_0b2_BUILD3
5904e3eb711dd263a6d2deb63b14e0c44e577054 FIREFOX_46_0b2_RELEASE
2f6f69a19150e03ad68062f2ac92342afb1ef787 FIREFOX_46_0b4_BUILD2
2f6f69a19150e03ad68062f2ac92342afb1ef787 FIREFOX_46_0b4_RELEASE
53d6e6648f97402a740687a82a297c66f5396548 FIREFOX_46_0b5_BUILD2
53d6e6648f97402a740687a82a297c66f5396548 FIREFOX_46_0b5_RELEASE
97b81104ac035d6a8f6ed59a1aad63fcc23e73c8 FIREFOX_46_0b6_BUILD1
97b81104ac035d6a8f6ed59a1aad63fcc23e73c8 FIREFOX_46_0b6_RELEASE
191f5eb4cbd72590277296cdb90d355adb347d45 FIREFOX_46_0b7_BUILD2
191f5eb4cbd72590277296cdb90d355adb347d45 FIREFOX_46_0b7_RELEASE
0334bcac4033f4f163476677165acd406e08fed8 FIREFOX_46_0b8_BUILD1
0334bcac4033f4f163476677165acd406e08fed8 FIREFOX_46_0b8_RELEASE
b007110e90053e58946b59765605bfca766c30d1 FIREFOX_46_0b9_BUILD1
b007110e90053e58946b59765605bfca766c30d1 FIREFOX_46_0b9_RELEASE
9ea83990839bd513869018e57bcbedb3454b63bb FIREFOX_46_0b10_BUILD1
9ea83990839bd513869018e57bcbedb3454b63bb FIREFOX_46_0b10_RELEASE
6c4646c7a6d6506e744c92a8170310191904c98e FIREFOX_RELEASE_46_BASE
64b6b8afb34bf8a4416b1d167b48177d0e30bde9 FIREFOX_RELEASE_45_END
078baf501b55eaa47f3b189fda4dd28dae1fa257 FIREFOX_46_0_BUILD5
078baf501b55eaa47f3b189fda4dd28dae1fa257 FIREFOX_46_0_RELEASE
0b8492c110bec959b94e3d54d5bd5ca7f7f97f6c FIREFOX_46_0_1_BUILD1
0b8492c110bec959b94e3d54d5bd5ca7f7f97f6c FIREFOX_46_0_1_RELEASE
076bf6a0ac85ec6a4f3ee7c3efe653964d92b9f2 FIREFOX_46_0b11_BUILD1
076bf6a0ac85ec6a4f3ee7c3efe653964d92b9f2 FIREFOX_46_0b11_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
68d3781deda0d4d58ec9877862830db89669b3a5 FIREFOX_AURORA_47_BASE
43d92f7052e7bab870c9b994d4b0a0f42d26b7a7 FIREFOX_AURORA_46_END
8551b253f4061db31e4be7829c2f70c2610caf42 FIREFOX_BETA_47_BASE
a2290938ee6cf794896a9347980bcb00ebb24c68 FIREFOX_BETA_46_END
5bbf2e7c2fc6ff9010b1948e2f11477f48ee36e2 FIREFOX_47_0b1_BUILD2
5bbf2e7c2fc6ff9010b1948e2f11477f48ee36e2 FIREFOX_47_0b1_RELEASE
6f82d30fe05e1412e744cb76af86f0c9ffe509d4 FIREFOX_47_0b2_BUILD1
6f82d30fe05e1412e744cb76af86f0c9ffe509d4 FIREFOX_47_0b2_RELEASE
609000bcc11211d7c27ceea36fa2d2262fa0523f FIREFOX_47_0b3_BUILD1
609000bcc11211d7c27ceea36fa2d2262fa0523f FIREFOX_47_0b3_RELEASE
2991f214d4f4d8a3c5cfd95e6223f0660006767d FIREFOX_47_0b4_BUILD1
2991f214d4f4d8a3c5cfd95e6223f0660006767d FIREFOX_47_0b4_RELEASE
93a53170dedffdff45bf9eb8dac6e5ef7a13c4ba FIREFOX_47_0b5_BUILD1
93a53170dedffdff45bf9eb8dac6e5ef7a13c4ba FIREFOX_47_0b5_RELEASE
7d1f3450acc47025876964c1eca854ae027934f3 FIREFOX_47_0b6_BUILD1
7d1f3450acc47025876964c1eca854ae027934f3 FIREFOX_47_0b6_RELEASE
0723a0212f5e0b30d7532d4e35eba7759fb54507 FIREFOX_47_0b7_BUILD1
0723a0212f5e0b30d7532d4e35eba7759fb54507 FIREFOX_47_0b7_RELEASE
cb27eacbe04abc5f91a0379c23617715aab432ec FIREFOX_47_0b8_BUILD1
cb27eacbe04abc5f91a0379c23617715aab432ec FIREFOX_47_0b8_RELEASE
2ee4473c729acb2ba7dc723e7affe79ce14bff85 FIREFOX_47_0b9_BUILD1
2ee4473c729acb2ba7dc723e7affe79ce14bff85 FIREFOX_47_0b9_RELEASE
cf6ec12bd62001b93387ffb184a8841644255b5e FIREFOX_RELEASE_47_BASE
32d716995e14ae1e8eb128fde0881b121b7b53d0 FIREFOX_RELEASE_46_END
b0310cb90fd0158abd0e92850a47768649ba3d77 FIREFOX_47_0_BUILD3
b0310cb90fd0158abd0e92850a47768649ba3d77 FIREFOX_47_0_RELEASE
7f5abf95991bda0bc2b8e0d774a8866b726b312b FIREFOX_47_0_1_BUILD1
7f5abf95991bda0bc2b8e0d774a8866b726b312b FIREFOX_47_0_1_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
1c6385ae1fe7e37d8f23f958ce14582f07af729e FIREFOX_AURORA_48_BASE
037f67a2d6c87e32f60b2753f19e4bed69cb60b3 FIREFOX_AURORA_47_END
961e7105e527f5b44e2b3cb6e24f6da33756f6ab FIREFOX_BETA_48_BASE
a86835580095325bf5c85483fad952f5c99d973e FIREFOX_BETA_47_END
55124c7343021091a61fdddac9f22628ca09642a FIREFOX_48_0b1_BUILD2
55124c7343021091a61fdddac9f22628ca09642a FIREFOX_48_0b1_RELEASE
9798772706750302d87a689cbbf056ae04244f80 FIREFOX_48_0b2_BUILD2
9798772706750302d87a689cbbf056ae04244f80 FIREFOX_48_0b2_RELEASE
13b02b96281e550c3cdbdf4eaed034aa1edefd69 FIREFOX_48_0b3_BUILD1
13b02b96281e550c3cdbdf4eaed034aa1edefd69 FIREFOX_48_0b3_RELEASE
676a32cdd41fd372f4c6df3a4954939f73a6ef02 FIREFOX_48_0b4_BUILD1
676a32cdd41fd372f4c6df3a4954939f73a6ef02 FIREFOX_48_0b4_RELEASE
dd7af1fa4ece1cb3158d08c80dfcbf1c6ca830fb FIREFOX_48_0b5_BUILD1
dd7af1fa4ece1cb3158d08c80dfcbf1c6ca830fb FIREFOX_48_0b5_RELEASE
d142c49033c015f67272562b37dbe2912cfc7f14 FIREFOX_48_0b6_BUILD1
d142c49033c015f67272562b37dbe2912cfc7f14 FIREFOX_48_0b6_RELEASE
9d734024ed35d74449601cc04917b327e0973c0d FIREFOX_48_0b7_BUILD1
9d734024ed35d74449601cc04917b327e0973c0d FIREFOX_48_0b7_RELEASE
d2ab9c39bd1059d74acb3d9ac87dbfbba913427b FIREFOX_48_0b9_BUILD1
d2ab9c39bd1059d74acb3d9ac87dbfbba913427b FIREFOX_48_0b9_RELEASE
05853bb06a8739b77c2937f418cdf4e1610d0d9f FIREFOX_48_0b10_BUILD1
05853bb06a8739b77c2937f418cdf4e1610d0d9f FIREFOX_48_0b10_RELEASE
f3d7abb885c267a7657e3b8ea06c18f76eb69641 FIREFOX_RELEASE_48_BASE
2366ae84e268c386a292185bddb0e4a24c2e1d07 FIREFOX_RELEASE_47_END
c1de04f39fa956cfce83f6065b0e709369215ed5 FIREFOX_48_0_BUILD2
c1de04f39fa956cfce83f6065b0e709369215ed5 FIREFOX_48_0_RELEASE
f36f7ace6f487e06f315f343d560b205fa8bd736 FIREFOX_48_0_1_BUILD3
f36f7ace6f487e06f315f343d560b205fa8bd736 FIREFOX_48_0_1_RELEASE
d4af0671004007e58d316b3e49679b66879c205a FIREFOX_48_0_2_BUILD1
d4af0671004007e58d316b3e49679b66879c205a FIREFOX_48_0_2_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
a8fe75f38163d0af067fea60e951a844fb328db9 FIREFOX_AURORA_48_END
d2f65d99fd51e5ee2f34d892115cc5d6c39a8ea9 FIREFOX_BETA_49_BASE
958cee08361af9ed370be06695973bcda3d3aa46 FIREFOX_BETA_48_END
c14631e328bbbd3aabaf06b51597060b55938ba8 FIREFOX_49_0b1_BUILD3
c14631e328bbbd3aabaf06b51597060b55938ba8 FIREFOX_49_0b1_RELEASE
b1bdd0f3482ae07d38a2ec5fbf190a2c4f2310ee FIREFOX_49_0b2_BUILD1
b1bdd0f3482ae07d38a2ec5fbf190a2c4f2310ee FIREFOX_49_0b2_RELEASE
36af692196a1c92cc935cf6569e03fdf5dc5e426 FIREFOX_49_0b3_BUILD1
36af692196a1c92cc935cf6569e03fdf5dc5e426 FIREFOX_49_0b3_RELEASE
ab7b68014a1e20c423aa9b50ca76fd8edccb272c FIREFOX_49_0b4_BUILD1
ab7b68014a1e20c423aa9b50ca76fd8edccb272c FIREFOX_49_0b4_RELEASE
91505c2a68fb868bcab1e3dc03ffc2e89bf71cac FIREFOX_49_0b5_BUILD1
91505c2a68fb868bcab1e3dc03ffc2e89bf71cac FIREFOX_49_0b5_RELEASE
e6d6eb0ce3c42b4ebca91b1b1b64b716b2acb9fd FIREFOX_49_0b6_BUILD1
e6d6eb0ce3c42b4ebca91b1b1b64b716b2acb9fd FIREFOX_49_0b6_RELEASE
b44c72b85a800d9c6e719579d480bb2c3a87a753 FIREFOX_49_0b7_BUILD1
b44c72b85a800d9c6e719579d480bb2c3a87a753 FIREFOX_49_0b7_RELEASE
68d24e6f784c7e375cf6c84c5c92496464d4f7e0 FIREFOX_49_0b8_BUILD1
68d24e6f784c7e375cf6c84c5c92496464d4f7e0 FIREFOX_49_0b8_RELEASE
e3cc699ccef2abb7075c39e7b9a081718eb9c159 FIREFOX_49_0b9_BUILD1
e3cc699ccef2abb7075c39e7b9a081718eb9c159 FIREFOX_49_0b9_RELEASE
77a60bbacb97c2f36815b2c395958a354aa581c9 FIREFOX_49_0b10_BUILD1
77a60bbacb97c2f36815b2c395958a354aa581c9 FIREFOX_49_0b10_RELEASE
5d1f8216843a6374f3bd2bd74ab2532da67d74ee FIREFOX_RELEASE_49_BASE
31ca1b4c28a9f64e49d59ed01d08469a9af41951 FIREFOX_RELEASE_48_END
416dc3163a1f27b8783ed14660d1b373e830df2f FIREFOX_49_0_BUILD4
416dc3163a1f27b8783ed14660d1b373e830df2f FIREFOX_49_0_RELEASE
2d931a5eaf8aeee925eca2aea42582a1fb9793c8 FIREFOX_49_0_1_BUILD3
2d931a5eaf8aeee925eca2aea42582a1fb9793c8 FIREFOX_49_0_1_RELEASE
7356baae8e736a6c9444bdd21562df806a39766b FIREFOX_49_0_2_BUILD2
7356baae8e736a6c9444bdd21562df806a39766b FIREFOX_49_0_2_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
d98f20c25feeac4dd7ebbd1c022957df1ef58af4 FIREFOX_AURORA_49_BASE
465d150bc8be5bbf9f02a8607d4552b6a5e1697c FIREFOX_AURORA_50_BASE
fa80a27f26dfc653d23392c71f562b1827d82174 FIREFOX_AURORA_49_END
c174d0c3195371011b3bf8044512c06c26f5c741 FIREFOX_BETA_50_BASE
ddeb7907bc1e854738b164bab5ff71c3dcf1d786 FIREFOX_BETA_49_END
91faf7ec36cd18a8ebdc0e2edac966e5bbb15da2 FIREFOX_50_0b1_BUILD2
91faf7ec36cd18a8ebdc0e2edac966e5bbb15da2 FIREFOX_50_0b1_RELEASE
865939f4946d80fa532aaa168515a2fe69f9a774 FIREFOX_50_0b2_BUILD1
865939f4946d80fa532aaa168515a2fe69f9a774 FIREFOX_50_0b2_RELEASE
6a7c1c8db5548d077c7fa36bce41af629ba52bd8 FIREFOX_50_0b3_BUILD1
6a7c1c8db5548d077c7fa36bce41af629ba52bd8 FIREFOX_50_0b3_RELEASE
f4b45ea3c0c836e5a457c8009bff423447e54803 FIREFOX_50_0b4_BUILD1
f4b45ea3c0c836e5a457c8009bff423447e54803 FIREFOX_50_0b4_RELEASE
49776d31766dd130969f9ec4ea3354a43e8e6d9d FIREFOX_50_0b5_BUILD1
49776d31766dd130969f9ec4ea3354a43e8e6d9d FIREFOX_50_0b5_RELEASE
70abfe99097824fd510544b188f24c588fd6d5a0 FIREFOX_50_0b6_BUILD1
70abfe99097824fd510544b188f24c588fd6d5a0 FIREFOX_50_0b6_RELEASE
6efc0964ec62bc4abfdc4cb1dc7cc461c3238634 FIREFOX_50_0b7_BUILD1
6efc0964ec62bc4abfdc4cb1dc7cc461c3238634 FIREFOX_50_0b7_RELEASE
b7adb2f10487f6f886e047670ba720a248edcb44 FIREFOX_50_0b8_BUILD1
b7adb2f10487f6f886e047670ba720a248edcb44 FIREFOX_50_0b8_RELEASE
2bb6dc758711c00d84246d74b57e5aa6cae4b447 FIREFOX_50_0b9_BUILD1
2bb6dc758711c00d84246d74b57e5aa6cae4b447 FIREFOX_50_0b9_RELEASE
38cfded1705240c5d20baff4aef99bdd0a13bcec FIREFOX_50_0b10_BUILD1
38cfded1705240c5d20baff4aef99bdd0a13bcec FIREFOX_50_0b10_RELEASE
829a3f99f2606759305e3db204185242566a4ca6 FIREFOX_50_0b11_BUILD1
829a3f99f2606759305e3db204185242566a4ca6 FIREFOX_50_0b11_RELEASE
d7598ee90bc6085d430b2e9e75c13358ef00a5f4 FIREFOX_RELEASE_50_BASE
dd6c8d2be972cbf8729c01292639b8a03ce94728 FIREFOX_RELEASE_49_END
dc617d65c9f0cdbbe4351cc1e5c288b05f25f8f7 FIREFOX_50_0_BUILD2
dc617d65c9f0cdbbe4351cc1e5c288b05f25f8f7 FIREFOX_50_0_RELEASE
8612c3320053b796678921f8f23358e3e9df997e FIREFOX_50_1_0_BUILD2
8612c3320053b796678921f8f23358e3e9df997e FIREFOX_50_1_0_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
fc69febcbf6c0dcc4b3dfc7a346d8d348798a65f FIREFOX_AURORA_51_BASE
3fdfaaed6e0fad2d81fc28e49903ff6f0a43b12e FIREFOX_AURORA_50_END
2d9b6132e7d75327e063a15d8e5e279077adf987 FIREFOX_BETA_51_BASE
24b8f08f77565f859898b45f62d2132ccc64c6d8 FIREFOX_BETA_50_END
749a8d32b74eae516b9427f28aad4ec1c11e0a54 FIREFOX_51_0b1_BUILD2
749a8d32b74eae516b9427f28aad4ec1c11e0a54 FIREFOX_51_0b1_RELEASE
6296ed3dbefd133bba324230ec4f5a07d37041e1 FIREFOX_51_0b2_BUILD1
6296ed3dbefd133bba324230ec4f5a07d37041e1 FIREFOX_51_0b2_RELEASE
f37e99ebc6e0c682003b52573f415e5fd78d425a FIREFOX_51_0b3_BUILD1
f37e99ebc6e0c682003b52573f415e5fd78d425a FIREFOX_51_0b3_RELEASE
1b954c82dd04faf1926804d89c0d130dc6b9ab93 FIREFOX_51_0b4_BUILD1
1b954c82dd04faf1926804d89c0d130dc6b9ab93 FIREFOX_51_0b4_RELEASE
9afe68360fa82c16b760b448b2156230a90caf11 FIREFOX_51_0b5_BUILD1
9afe68360fa82c16b760b448b2156230a90caf11 FIREFOX_51_0b5_RELEASE
2dec3c6c7c90e2e27093b8a3512c1b32a8263a8f FIREFOX_51_0b6_BUILD1
2dec3c6c7c90e2e27093b8a3512c1b32a8263a8f FIREFOX_51_0b6_RELEASE
829fcc7f60f04dffff4d29b007ef8015a1cd2c99 FIREFOX_51_0b7_BUILD1
829fcc7f60f04dffff4d29b007ef8015a1cd2c99 FIREFOX_51_0b7_RELEASE
ae5275b8c53ba76cb98576e4e2a3031b0d659ba3 FIREFOX_51_0b8_BUILD1
ae5275b8c53ba76cb98576e4e2a3031b0d659ba3 FIREFOX_51_0b8_RELEASE
6e4843d1510be426212d31fec03ad4f2d70b1977 FIREFOX_51_0b9_BUILD1
6e4843d1510be426212d31fec03ad4f2d70b1977 FIREFOX_51_0b9_RELEASE
4fbf5d14ce92bd45e0c7881dad20a66896402683 FIREFOX_51_0b10_BUILD1
4fbf5d14ce92bd45e0c7881dad20a66896402683 FIREFOX_51_0b10_RELEASE
0a17d39220700e742bf37a960967480b2f8159f1 FIREFOX_51_0b11_BUILD1
0a17d39220700e742bf37a960967480b2f8159f1 FIREFOX_51_0b11_RELEASE
9ddd4fee07842e72ba49f1583ec5f596f6e60e72 FIREFOX_51_0b12_BUILD1
9ddd4fee07842e72ba49f1583ec5f596f6e60e72 FIREFOX_51_0b12_RELEASE
ce55e4d276031458f0730d481acff05d7c797038 FIREFOX_51_0b13_BUILD1
ce55e4d276031458f0730d481acff05d7c797038 FIREFOX_51_0b13_RELEASE
09142d07fd735e375fc1ae46886a52d6aef43b60 FIREFOX_51_0b14_BUILD1
09142d07fd735e375fc1ae46886a52d6aef43b60 FIREFOX_51_0b14_RELEASE
15467610e733e3549ea86cdf940b0fccd87eff89 FIREFOX_RELEASE_51_BASE
b1e53b9be6d4834f5b3a58c132dfc5f5c73d2bcd FIREFOX_RELEASE_50_END
ea82b5e20cbbd103f8fa65f0df0386ee4135cc47 FIREFOX_51_0_BUILD2
ea82b5e20cbbd103f8fa65f0df0386ee4135cc47 FIREFOX_51_0_RELEASE
327e081221b064b05a302d7877c6e4be2949a617 FIREFOX_51_0_1_BUILD3
327e081221b064b05a302d7877c6e4be2949a617 FIREFOX_51_0_1_RELEASE
9f12a9fab080f2d363d7424e25b9ffe85ebc3414 FIREFOX_AURORA_28_BASE
1196bf3032e1bce1fb07a01fd9082a767426c5fb FIREFOX_AURORA_52_BASE
3223a6411d8c8b9ec8bcbc607c990bf099ade099 FIREFOX_AURORA_51_END
24a81d93e07cc96300f8e1f5c69034dd4743bd63 FIREFOX_BETA_52_BASE
8e692dd4176cba81ce020b29ae8b352dc1db724a FIREFOX_BETA_51_END
78ae21055d9f303be257abe155ba9dee466c0651 FIREFOX_52_0b1_BUILD2
78ae21055d9f303be257abe155ba9dee466c0651 FIREFOX_52_0b1_RELEASE
0f339c1e154f75c484fe2fac507a9a225990d212 FIREFOX_52_0b2_BUILD1
0f339c1e154f75c484fe2fac507a9a225990d212 FIREFOX_52_0b2_RELEASE
d171c36d484800b1bb00db1612460a7120dd2fdf FIREFOX_52_0b3_BUILD1
d171c36d484800b1bb00db1612460a7120dd2fdf FIREFOX_52_0b3_RELEASE
501a3fa83897af9598adfd6f794b5d5ea82fe237 FIREFOX_52_0b4_BUILD1
501a3fa83897af9598adfd6f794b5d5ea82fe237 FIREFOX_52_0b4_RELEASE
49b3ad9f467d48194dab8121f82e4c938b70b484 FIREFOX_52_0b5_BUILD1
49b3ad9f467d48194dab8121f82e4c938b70b484 FIREFOX_52_0b5_RELEASE
7b8aa893944b94d35e47314e52e0abff576c5ce2 FIREFOX_52_0b6_BUILD1
7b8aa893944b94d35e47314e52e0abff576c5ce2 FIREFOX_52_0b6_RELEASE
820bc5bd3120853d611af1056f4a2b35528bb927 FIREFOX_52_0b7_BUILD1
820bc5bd3120853d611af1056f4a2b35528bb927 FIREFOX_52_0b7_RELEASE
3b31bcb88fe341172e93cc8b1143e617c0988694 FIREFOX_52_0b8_BUILD1
3b31bcb88fe341172e93cc8b1143e617c0988694 FIREFOX_52_0b8_RELEASE
61519976b35f2947eeaabefcad83186b7e004167 FIREFOX_52_0b9_BUILD2
61519976b35f2947eeaabefcad83186b7e004167 FIREFOX_52_0b9_RELEASE
4bd2e5d2ac0d0b15b4446fca5647bfa821b52d46 FIREFOX_RELEASE_52_BASE
1f0fc9316e65cd171b03d4382b4c0f7443a258dc FIREFOX_RELEASE_51_END
2183f7cb4f886e5f0b619dcff101bee72210ef38 FIREFOX_ESR_52_BASE
15d9d940341f02ef6dcad96899d284619d3d48db FIREFOX_52_0esr_BUILD4
15d9d940341f02ef6dcad96899d284619d3d48db FIREFOX_52_0esr_RELEASE
3462b933db3cac9b994b32dfc6350f3d549bf2de FIREFOX_52_0_1esr_BUILD2
3462b933db3cac9b994b32dfc6350f3d549bf2de FIREFOX_52_0_1esr_RELEASE
afa66bcf9203e691f164e5e8a9cc8fad783ccddc FIREFOX_52_0_2esr_BUILD1
afa66bcf9203e691f164e5e8a9cc8fad783ccddc FIREFOX_52_0_2esr_RELEASE
3ea0e075203185d7f2d42f439455e97735bd1b20 FIREFOX_52_1_0esr_BUILD3
3ea0e075203185d7f2d42f439455e97735bd1b20 FIREFOX_52_1_0esr_RELEASE
120111e65bc4e9b99da97e9d046765de95fbac8c FIREFOX_52_1_1esr_BUILD1
120111e65bc4e9b99da97e9d046765de95fbac8c FIREFOX_52_1_1esr_RELEASE
0dc7c28f20648d597b11b60e90be9a15687656aa FIREFOX_52_1_2esr_BUILD1
0dc7c28f20648d597b11b60e90be9a15687656aa FIREFOX_52_1_2esr_RELEASE
f68e0d98a22a4712a3710998081fd0ea5cd00ccb FIREFOX_52_2_0esr_BUILD1
f68e0d98a22a4712a3710998081fd0ea5cd00ccb FIREFOX_52_2_0esr_RELEASE
512efd480dac58acf9eebd5f25a76b32917ab56d FIREFOX_52_2_1esr_BUILD2
512efd480dac58acf9eebd5f25a76b32917ab56d FIREFOX_52_2_1esr_RELEASE
20a1a6ad46d5a4ec83d9800614fc288bf79e14a8 FIREFOX_52_3_0esr_BUILD2
20a1a6ad46d5a4ec83d9800614fc288bf79e14a8 FIREFOX_52_3_0esr_RELEASE
285cde3988335103bd8d60cc09d6fa36db3c4d78 FIREFOX_52_4_0esr_BUILD2
285cde3988335103bd8d60cc09d6fa36db3c4d78 FIREFOX_52_4_0esr_RELEASE
e4fec62e5347a77a9a313d6019a2b372e8011a74 FIREFOX_52_4_1esr_BUILD1
e4fec62e5347a77a9a313d6019a2b372e8011a74 FIREFOX_52_4_1esr_RELEASE
cf34a0574e585fab44fbec1718aca5375790cd97 FIREFOX_52_5_0esr_BUILD2
cf34a0574e585fab44fbec1718aca5375790cd97 FIREFOX_52_5_0esr_RELEASE
b0a57c57b5ef0150e8afc3219bceadb1d6f1584e FIREFOX_52_5_2esr_BUILD2
b0a57c57b5ef0150e8afc3219bceadb1d6f1584e FIREFOX_52_5_2esr_RELEASE

31
.lldbinit Normal file
View file

@ -0,0 +1,31 @@
# .lldbinit file for debugging Mozilla
# -----------------------------------------------------------------------------
# For documentation on all of the commands and type summaries defined here
# and in the accompanying Python scripts, see python/lldbutils/README.txt.
# -----------------------------------------------------------------------------
# Import the module that defines complex Gecko debugging commands. This assumes
# you are either running lldb from the top level source directory, the objdir,
# or the dist/bin directory. (.lldbinit files in the objdir and dist/bin set
# topsrcdir appropriately.)
script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "python/lldbutils")); import lldbutils; lldbutils.init()
# Mozilla's use of UNIFIED_SOURCES to include multiple source files into a
# single compiled file breaks lldb breakpoint setting. This works around that.
# See http://lldb.llvm.org/troubleshooting.html for more info.
settings set target.inline-breakpoint-strategy always
# Show the dynamic type of an object when using "expr". This, for example,
# will show a variable declared as "nsIFrame *" that points to an nsBlockFrame
# object as being of type "nsBlockFrame *" rather than "nsIFrame *".
settings set target.prefer-dynamic-value run-target
# Show the string value in atoms.
type summary add nsIAtom --summary-string "${var.mString}"
# Show the value of text nodes.
type summary add nsTextNode --summary-string "${var.mText}"
# Dump the current JS stack.
command alias js expr DumpJSStack()

125
.taskcluster.yml Normal file
View file

@ -0,0 +1,125 @@
---
version: 0
metadata:
name: 'Taskcluster tasks for Gecko'
description: "The taskcluster task graph for Gecko trees"
owner: mozilla-taskcluster-maintenance@mozilla.com
source: {{{source}}}
scopes:
# Note the below scopes are insecure however these get overriden on the server
# side to whatever scopes are set by mozilla-taskcluster.
- queue:*
- docker-worker:*
- scheduler:*
# Available mustache parameters (see the mozilla-taskcluster source):
#
# - owner: push user (email address)
# - source: URL of this YAML file
# - url: repository URL
# - project: alias for the destination repository (basename of
# the repo url)
# - level: SCM level of the destination repository
# (1 = try, 3 = core)
# - revision: (short) hg revision of the head of the push
# - revision_hash: (long) hg revision of the head of the push
# - comment: comment of the push
# - pushlog_id: id in the pushlog table of the repository
#
# and functions:
# - as_slugid: convert a label into a slugId
# - from_now: generate a timestamp at a fixed offset from now
# The resulting tasks' taskGroupId will be equal to the taskId of the first
# task listed here, which should be the decision task. This gives other tools
# an easy way to determine the ID of the decision task that created a
# particular group.
tasks:
- taskId: '{{#as_slugid}}decision task{{/as_slugid}}'
task:
created: '{{now}}'
deadline: '{{#from_now}}1 day{{/from_now}}'
expires: '{{#from_now}}365 day{{/from_now}}'
metadata:
owner: mozilla-taskcluster-maintenance@mozilla.com
source: {{{source}}}
name: "Gecko Decision Task"
description: |
The task that creates all of the other tasks in the task graph
workerType: "gecko-decision"
provisionerId: "aws-provisioner-v1"
tags:
createdForUser: {{owner}}
scopes:
# Bug 1269443: cache scopes, etc. must be listed explicitly
- "docker-worker:cache:level-{{level}}-*"
- "docker-worker:cache:tooltool-cache"
# mozilla-taskcluster will append the appropriate assume:repo:<repo>
# scope here.
routes:
- "index.gecko.v2.{{project}}.latest.firefox.decision"
- "tc-treeherder.v2.{{project}}.{{revision}}.{{pushlog_id}}"
- "tc-treeherder-stage.v2.{{project}}.{{revision}}.{{pushlog_id}}"
payload:
env:
# checkout-gecko uses these to check out the source; the inputs
# to `mach taskgraph decision` are all on the command line.
GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
GECKO_HEAD_REPOSITORY: '{{{url}}}'
GECKO_HEAD_REF: '{{revision}}'
GECKO_HEAD_REV: '{{revision}}'
HG_STORE_PATH: /home/worker/checkouts/hg-store
cache:
level-{{level}}-checkouts: /home/worker/checkouts
features:
taskclusterProxy: true
chainOfTrust: true
# Note: This task is built server side without the context or tooling that
# exist in tree so we must hard code the version
image: 'taskcluster/decision:0.1.7'
maxRunTime: 1800
# TODO use mozilla-unified for the base repository once the tc-vcs
# tar.gz archives are created or tc-vcs isn't being used.
command:
- /home/worker/bin/run-task
- '--vcs-checkout=/home/worker/checkouts/gecko'
- '--'
- bash
- -cx
- >
cd /home/worker/checkouts/gecko &&
ln -s /home/worker/artifacts artifacts &&
./mach --log-no-times taskgraph decision
--pushlog-id='{{pushlog_id}}'
--pushdate='{{pushdate}}'
--project='{{project}}'
--message={{#shellquote}}{{{comment}}}{{/shellquote}}
--owner='{{owner}}'
--level='{{level}}'
--base-repository='https://hg.mozilla.org/mozilla-central'
--head-repository='{{{url}}}'
--head-ref='{{revision}}'
--head-rev='{{revision}}'
--revision-hash='{{revision_hash}}'
artifacts:
'public':
type: 'directory'
path: '/home/worker/artifacts'
expires: '{{#from_now}}364 days{{/from_now}}'
extra:
treeherder:
symbol: D

47
.ycm_extra_conf.py Normal file
View file

@ -0,0 +1,47 @@
# 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/.
import imp
import os
import shlex
import sys
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
old_bytecode = sys.dont_write_bytecode
sys.dont_write_bytecode = True
path = os.path.join(os.path.dirname(__file__), 'mach')
if not os.path.exists(path):
path = os.path.join(os.path.dirname(__file__), 'config.status')
config = imp.load_module('_buildconfig', open(path), path, ('', 'r', imp.PY_SOURCE))
path = os.path.join(config.topsrcdir, 'mach')
mach_module = imp.load_module('_mach', open(path), path, ('', 'r', imp.PY_SOURCE))
sys.dont_write_bytecode = old_bytecode
def FlagsForFile(filename):
mach = mach_module.get_mach()
out = StringIO()
# Mach calls sys.stdout.fileno(), so we need to fake it when capturing it.
# Returning an invalid file descriptor does the trick.
out.fileno = lambda: -1
out.encoding = None
mach.run(['compileflags', filename], stdout=out, stderr=out)
flag_list = shlex.split(out.getvalue())
# This flag is added by Fennec for android build and causes ycmd to fail to parse the file.
# Removing this flag is a workaround until ycmd starts to handle this flag properly.
# https://github.com/Valloric/YouCompleteMe/issues/1490
final_flags = [x for x in flag_list if not x.startswith('-march=armv')]
return {
'flags': final_flags,
'do_cache': True
}

1073
AUTHORS Normal file

File diff suppressed because it is too large Load diff

5
Android.mk Normal file
View file

@ -0,0 +1,5 @@
# 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/.
# empty file to block B2G/Gonk from trying to build anything inside mozilla-central

25
CLOBBER Normal file
View file

@ -0,0 +1,25 @@
# To trigger a clobber replace ALL of the textual description below,
# giving a bug number and a one line description of why a clobber is
# required. Modifying this file will make configure check that a
# clobber has been performed before the build can continue.
#
# MERGE NOTE: When merging two branches that require a CLOBBER, you should
# merge both CLOBBER descriptions, to ensure that users on
# both branches correctly see the clobber warning.
#
# O <-- Users coming from both parents need to Clobber
# / \
# O O
# | |
# O <-- Clobber O <-- Clobber
#
# Note: The description below will be part of the error message shown to users.
#
# Modifying this file will now automatically clobber the buildbot machines \o/
#
# Are you updating CLOBBER because you think it's needed for your WebIDL
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Merge day clobber

14
GNUmakefile Normal file
View file

@ -0,0 +1,14 @@
# This Makefile is used as a shim to aid people with muscle memory
# so that they can type "make".
#
# This file and all of its targets should not be used by anything important.
all: build
build:
./mach build
clean:
./mach clobber
.PHONY: all build clean

43
LEGAL Normal file
View file

@ -0,0 +1,43 @@
Please be apprised of the following Legal Notices:
A) The U.S. District Court for the Eastern District of Virginia has
ruled that the Netscape Navigator code does not infringe Wang's U.S.
Patent No. 4,751,669 ("the '669 Patent") because: 1) HTML is not
Videotex as defined by the '669 patent; 2) web servers are not central
suppliers; and 3) Navigator does not "connect," as defined by the '669
Patent, to web servers on the Internet. Wang may appeal this decision to
the Federal Circuit. Wang contended that its Patent disclosing a
"Videotex" system, is infringed by the following functionality in the
Netscape Navigator code: 1) the animated logo and status line indicators
--See Claims 1,8 and 9; 2) the "File Save As" function --See Claims
23-27; 3) Bookmarks and Rename Bookmarks in the Properties window --See
Claims 20-22; 4) storing HTML, GIF, and JPEG files and adding filename
extensions --See Claim 38
B) Intermind owns pending U.S. patent applications on communications
systems which employ metadata ("channel objects") to define a control
structure for information transfer. The Netscape code does not infringe
as released; however, modifications which utilize channel objects as
described by Intermind should be considered carefully. The following is
a statement from Intermind: "Intermind's claims fundamentally involve
the use of a control structure to automate communications. ...The
essence of Intermind's top claim is that two devices sender and receiver
have persistent storage, communicate over a network, and exchange a
control structure including metadata which describes: 1) what
information is to be updated, 2) when to update this information, and 3)
how to transfer the updated information. In addition, at least the
receiving device must be able to process the metadata in order to
perform the update determination and transfer. Any digital
communications system which incorporates all of these elements will be
covered by Intermind's patents." See Intermind.com.
C) Stac, Inc., and its licensing agent Hi/fn, own several patents which
disclose data compression methods implementing an LZS compression
algorithm, including U.S. Patent Nos. 4,701,745 and 5,016, 009 ("the
Stac Patents"). The Netscape Communicator code does not perform
compression. If you modify the Netscape source code to perform
compression, please take notice of the Stac Patents.
D) Netscape Communications Corporation ("Netscape") does not guarantee
that any source code or executable code available from the mozilla.org
domain is Year 2000 compliant.

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
Please see the file toolkit/content/license.html for the copyright licensing
conditions attached to this codebase, including copies of the licenses
concerned.
You are not granted rights or licenses to the trademarks of the
Mozilla Foundation or any party, including without limitation the
Firefox name or logo.
For more information, see: http://www.mozilla.org/foundation/licensing.html

416
Makefile.in Normal file
View file

@ -0,0 +1,416 @@
#
# 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/.
ifeq (,$(MAKE_VERSION))
$(error GNU Make is required)
endif
make_min_ver := 3.81
ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
$(error GNU Make $(make_min_ver) or higher is required)
endif
export TOPLEVEL_BUILD := 1
default::
ifndef TEST_MOZBUILD
ifdef MOZ_BUILD_APP
include $(wildcard $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk)
endif
endif
include $(topsrcdir)/config/config.mk
GARBAGE_DIRS += _javagen _profile staticlib
DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
config/autoconf.mk \
mozilla-config.h \
netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
.mozconfig.mk
ifndef MOZ_PROFILE_USE
buildid.h source-repo.h: FORCE
endif
ifdef JS_STANDALONE
configure_dir = $(topsrcdir)/js/src
else
configure_dir = $(topsrcdir)
endif
BUILD_BACKEND_FILES := $(addprefix backend.,$(addsuffix Backend,$(BUILD_BACKENDS)))
ifndef TEST_MOZBUILD
ifndef MOZ_PROFILE_USE
# We need to explicitly put BUILD_BACKEND_FILES here otherwise the rule in
# rules.mk doesn't run early enough.
$(TIERS) binaries:: CLOBBER $(configure_dir)/configure config.status $(BUILD_BACKEND_FILES)
ifndef JS_STANDALONE
ifdef COMPILE_ENVIRONMENT
$(TIERS) binaries:: $(topsrcdir)/js/src/configure js/src/config.status
endif
endif
endif
endif
ifdef JS_STANDALONE
.PHONY: CLOBBER
CLOBBER:
else
CLOBBER: $(topsrcdir)/CLOBBER
@echo 'STOP! The CLOBBER file has changed.'
@echo 'Please run the build through a sanctioned build wrapper, such as'
@echo '"mach build" or client.mk.'
@exit 1
endif
$(topsrcdir)/configure: $(topsrcdir)/configure.in $(topsrcdir)/old-configure.in
$(topsrcdir)/js/src/configure: $(topsrcdir)/js/src/configure.in $(topsrcdir)/js/src/old-configure.in
$(topsrcdir)/configure $(topsrcdir)/js/src/configure:
@echo 'STOP! $? has changed, and your configure is out of date.'
@echo 'Please rerun autoconf and re-configure your build directory.'
@echo 'To ignore this message, touch "$@",'
@echo 'but your build might not succeed.'
@exit 1
config.status: $(configure_dir)/configure $(configure_dir)/old-configure
js/src/config.status: $(topsrcdir)/js/src/configure $(topsrcdir)/js/src/old-configure
config.status js/src/config.status:
@echo 'STOP! $? has changed and needs to be run again.'
@echo 'Please rerun it.'
@echo 'To ignore this message, touch "$(CURDIR)/$@",'
@echo 'but your build might not succeed.'
@exit 1
# Regenerate the build backend if it is out of date. We only have this rule in
# this main make file because having it in rules.mk and applied to partial tree
# builds resulted in a world of hurt. Gory details are in bug 877308.
#
# The mach build driver will ensure the backend is up to date for partial tree
# builds. This cleanly avoids most of the pain.
ifndef TEST_MOZBUILD
.PHONY: backend
backend: $(BUILD_BACKEND_FILES)
# A traditional rule would look like this:
# backend.%:
# @echo do stuff
#
# But with -j<n>, and multiple items in BUILD_BACKEND_FILES, the command would
# run multiple times in parallel.
#
# "Fortunately", make has some weird semantics for pattern rules: if there are
# multiple targets in a pattern rule and each of them is matched at most once,
# the command will only run once. So:
# backend%RecursiveMakeBackend backend%FasterMakeBackend:
# @echo do stuff
# backend: backend.RecursiveMakeBackend backend.FasterMakeBackend
# would only execute the command once.
#
# Credit where due: http://stackoverflow.com/questions/2973445/gnu-makefile-rule-generating-a-few-targets-from-a-single-source-file/3077254#3077254
$(subst .,%,$(BUILD_BACKEND_FILES)):
@echo 'Build configuration changed. Regenerating backend.'
$(PYTHON) config.status
Makefile: $(BUILD_BACKEND_FILES)
@$(TOUCH) $@
define build_backend_rule
$(1)_files := $$(shell cat $(1).in)
$(1): $$($(1)_files)
$$($(1)_files):
endef
$(foreach file,$(BUILD_BACKEND_FILES),$(eval $(call build_backend_rule,$(file))))
default:: $(BUILD_BACKEND_FILES)
endif
install_manifests := \
$(addprefix dist/,branding idl include public private sdk xpi-stage) \
_tests \
$(NULL)
# Skip the dist/bin install manifest when using the hybrid
# FasterMake/RecursiveMake backend. This is a hack until bug 1241744 moves
# xpidl handling to FasterMake in that case, mechanically making the dist/bin
# install manifest non-existent (non-existent manifests being skipped)
ifeq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
install_manifests += dist/bin
endif
install_manifest_depends = \
CLOBBER \
$(configure_dir)/configure \
config.status \
$(BUILD_BACKEND_FILES) \
$(NULL)
ifndef JS_STANDALONE
ifdef COMPILE_ENVIRONMENT
install_manifest_depends += \
$(topsrcdir)/js/src/configure \
js/src/config.status \
$(NULL)
endif
endif
.PHONY: install-manifests
install-manifests: $(addprefix install-,$(install_manifests))
# If we're using the hybrid FasterMake/RecursiveMake backend, we want
# to recurse in the faster/ directory in parallel of install manifests.
# But dist/idl needs to happen before (cf. dependencies in
# config/faster/rules.mk)
ifneq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
install-manifests: faster
.PHONY: faster
faster: install-dist/idl
$(MAKE) -C faster FASTER_RECURSIVE_MAKE=1
endif
.PHONY: tup
tup:
$(call BUILDSTATUS,TIERS make tup)
$(call BUILDSTATUS,TIER_START make)
$(MAKE) install-manifests buildid.h source-repo.h
$(call BUILDSTATUS,TIER_FINISH make)
$(call BUILDSTATUS,TIER_START tup)
@$(TUP) $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),,--verbose)
$(call BUILDSTATUS,TIER_FINISH tup)
# process_install_manifest needs to be invoked with --no-remove when building
# js as standalone because automated builds are building nspr separately and
# that would remove the resulting files.
# Eventually, a standalone js build would just be able to build nspr itself,
# removing the need for the former.
ifdef JS_STANDALONE
NO_REMOVE=1
endif
.PHONY: $(addprefix install-,$(subst /,_,$(install_manifests)))
$(addprefix install-,$(install_manifests)): install-%: $(install_manifest_depends)
ifneq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
@# If we're using the hybrid FasterMake/RecursiveMake backend, we want
@# to ensure the FasterMake end doesn't have install manifests for the
@# same directory, because that would blow up
$(if $(wildcard _build_manifests/install/$(subst /,_,$*)),$(if $(wildcard faster/install_$(subst /,_,$*)*),$(error FasterMake and RecursiveMake ends of the hybrid build system want to handle $*)))
endif
$(addprefix $(call py_action,process_install_manifest,$(if $(NO_REMOVE),--no-remove )$*) ,$(wildcard _build_manifests/install/$(subst /,_,$*)))
# Dummy wrapper rule to allow the faster backend to piggy back
$(addprefix install-,$(subst /,_,$(filter dist/%,$(install_manifests)))): install-dist_%: install-dist/% ;
.PHONY: install-tests
install-tests: install-test-files
# We no longer run "make install-tests" directly before running tests, but we still
# want to depend on things like config.status, hence this target.
.PHONY: run-tests-deps
run-tests-deps: $(install_manifest_depends)
# Force --no-remove, because $objdir/_tests is handled by multiple manifests.
.PHONY: install-test-files
install-test-files:
$(call py_action,process_install_manifest,--no-remove _tests _build_manifests/install/_test_files)
include $(topsrcdir)/build/moz-automation.mk
# dist and _tests should be purged during cleaning. However, we don't want them
# purged during PGO builds because they contain some auto-generated files.
ifneq ($(filter-out maybe_clobber_profiledbuild,$(MAKECMDGOALS)),)
GARBAGE_DIRS += dist _tests
endif
# Windows PGO builds don't perform a clean before the 2nd pass. So, we want
# to preserve content for the 2nd pass on Windows. Everywhere else, we always
# process the install manifests as part of export.
# For the binaries rule, not all the install manifests matter, so force only
# the interesting ones to be done.
ifdef MOZ_PROFILE_USE
ifndef NO_PROFILE_GUIDED_OPTIMIZE
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
recurse_pre-export:: install-manifests
binaries::
@$(MAKE) install-manifests NO_REMOVE=1 install_manifests=dist/include
endif
endif
else # !MOZ_PROFILE_USE (normal build)
recurse_pre-export:: install-manifests
binaries::
@$(MAKE) install-manifests NO_REMOVE=1 install_manifests=dist/include
endif
# For historical reasons that are unknown, $(DIST)/sdk is always blown away
# with no regard for PGO passes. This decision could probably be revisited.
recurse_pre-export:: install-dist/sdk
recurse_artifact:
$(topsrcdir)/mach --log-no-times artifact install
ifndef JS_STANDALONE
ifdef ENABLE_TESTS
# Additional makefile targets to call automated test suites
include $(topsrcdir)/testing/testsuite-targets.mk
endif
endif
default all::
$(call BUILDSTATUS,TIERS $(TIERS) $(if $(MOZ_AUTOMATION),$(MOZ_AUTOMATION_TIERS)))
include $(topsrcdir)/config/rules.mk
distclean::
$(RM) $(DIST_GARBAGE)
ifeq ($(OS_ARCH),WINNT)
# we want to copy PDB files on Windows
MAKE_SYM_STORE_ARGS := -c --vcs-info
ifdef PDBSTR_PATH
MAKE_SYM_STORE_ARGS += -i
endif
ifdef MSVC_HAS_DIA_SDK
DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms.exe
else
DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms_vc$(_MSC_VER).exe
endif
# PDB files don't get moved to dist, so we need to scan the whole objdir
MAKE_SYM_STORE_PATH := .
endif
ifeq ($(OS_ARCH),Darwin)
# need to pass arch flags for universal builds
ifdef UNIVERSAL_BINARY
MAKE_SYM_STORE_ARGS := -c --vcs-info
MAKE_SYM_STORE_PATH := $(DIST)/bin $(UNIFY_DIST)/bin
else
MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
MAKE_SYM_STORE_PATH := $(DIST)/bin
endif
DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
endif
ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
MAKE_SYM_STORE_ARGS := -c --vcs-info
DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
MAKE_SYM_STORE_PATH := $(DIST)/bin
endif
MAKE_SYM_STORE_ARGS += --install-manifest=$(DEPTH)/_build_manifests/install/dist_include,$(DIST)/include
SYM_STORE_SOURCE_DIRS := $(topsrcdir)
ifdef MOZ_CRASHREPORTER
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
SYMBOL_INDEX_NAME = \
$(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)-$(CPU_ARCH)-symbols.txt
endif
.PHONY: generatesymbols
generatesymbols:
echo building symbol store
$(RM) -r $(DIST)/crashreporter-symbols
$(RM) '$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip'
$(RM) '$(DIST)/$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
$(NSINSTALL) -D $(DIST)/crashreporter-symbols
OBJCOPY='$(OBJCOPY)' \
$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
$(MAKE_SYM_STORE_ARGS) \
$(foreach dir,$(SYM_STORE_SOURCE_DIRS),-s $(dir)) \
$(DUMP_SYMS_BIN) \
$(DIST)/crashreporter-symbols \
$(MAKE_SYM_STORE_PATH) | grep -iv test > \
$(DIST)/crashreporter-symbols/$(SYMBOL_INDEX_NAME)
echo packing symbols
$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
.PHONY: symbolsfullarchive
symbolsfullarchive: generatesymbols
cd $(DIST)/crashreporter-symbols && \
zip -r5D '../$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip' . -x '*test*' -x '*Test*'
.PHONY: symbolsarchive
symbolsarchive: generatesymbols
cd $(DIST)/crashreporter-symbols && \
grep 'sym' $(SYMBOL_INDEX_NAME) > $(SYMBOL_INDEX_NAME).tmp && \
mv $(SYMBOL_INDEX_NAME).tmp $(SYMBOL_INDEX_NAME)
cd $(DIST)/crashreporter-symbols && \
zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym' -i '*.txt'
ifdef MOZ_CRASHREPORTER
buildsymbols: symbolsfullarchive symbolsarchive
else
buildsymbols:
endif
uploadsymbols:
ifdef MOZ_CRASHREPORTER
ifdef SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE
$(PYTHON) -u $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
endif
endif
.PHONY: update-packaging
update-packaging:
$(MAKE) -C tools/update-packaging
.PHONY: pretty-package
pretty-package:
unset MOZ_SIGN_CMD && $(MAKE) package MOZ_PKG_PRETTYNAMES=1
.PHONY: pretty-package-tests
pretty-package-tests:
unset MOZ_SIGN_CMD && $(MAKE) package-tests MOZ_PKG_PRETTYNAMES=1
.PHONY: pretty-l10n-check
pretty-l10n-check:
unset MOZ_SIGN_CMD && $(MAKE) l10n-check MOZ_PKG_PRETTYNAMES=1
.PHONY: pretty-update-packaging
pretty-update-packaging:
unset MOZ_SIGN_CMD && $(MAKE) -C tools/update-packaging MOZ_PKG_PRETTYNAMES=1
.PHONY: pretty-installer
pretty-installer:
unset MOZ_SIGN_CMD && $(MAKE) installer MOZ_PKG_PRETTYNAMES=1
#XXX: this is a hack, since we don't want to clobber for MSVC
# PGO support, but we can't do this test in client.mk
ifneq ($(OS_ARCH)_$(GNU_CC), WINNT_)
# No point in clobbering if PGO has been explicitly disabled.
ifndef NO_PROFILE_GUIDED_OPTIMIZE
maybe_clobber_profiledbuild: clean
else
maybe_clobber_profiledbuild:
endif
else
maybe_clobber_profiledbuild:
$(RM) $(DIST)/bin/*.pgc
find $(DIST)/$(MOZ_APP_NAME) -name '*.pgc' -exec mv {} $(DIST)/bin \;
endif
.PHONY: maybe_clobber_profiledbuild
# Look for R_386_PC32 relocations in shared libs, these
# break x86_64 builds and SELinux users.
ifeq ($(OS_TARGET)_$(TARGET_XPCOM_ABI),Linux_x86-gcc3)
check::
@relcount=`find $(DIST)/bin -name '*.so' | xargs objdump -R | grep R_386_PC32 | wc -l` && if test $$relcount -gt 0; then echo 'FAILED: R_386_PC32 relocations detected in a shared library. Did you use a system header without adding it to config/system-headers?'; exit 1; else echo 'PASSED'; fi
endif
ifdef JS_STANDALONE
# Delegate js-specific rules to js
check-%:
$(MAKE) -C js/src $@
source-package install:
$(MAKE) -C js/src $@
# Every export rule depends on config/export, but the rule for config/export
# doesn't exist when building js non-standalone.
.PHONY: config/export
config/export:
endif
# There used to be build interdependencies here. They are now in config/recurse.mk

27
README.txt Normal file
View file

@ -0,0 +1,27 @@
An explanation of the Mozilla Source Code Directory Structure and links to
project pages with documentation can be found at:
https://developer.mozilla.org/en/Mozilla_Source_Code_Directory_Structure
For information on how to build Mozilla from the source code, see:
http://developer.mozilla.org/en/docs/Build_Documentation
To have your bug fix / feature added to Mozilla, you should create a patch and
submit it to Bugzilla (https://bugzilla.mozilla.org). Instructions are at:
http://developer.mozilla.org/en/docs/Creating_a_patch
http://developer.mozilla.org/en/docs/Getting_your_patch_in_the_tree
If you have a question about developing Mozilla, and can't find the solution
on http://developer.mozilla.org, you can try asking your question in a
mozilla.* Usenet group, or on IRC at irc.mozilla.org. [The Mozilla news groups
are accessible on Google Groups, or news.mozilla.org with a NNTP reader.]
You can download nightly development builds from the Mozilla FTP server.
Keep in mind that nightly builds, which are used by Mozilla developers for
testing, may be buggy. Firefox nightlies, for example, can be found at:
https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/
- or -
http://nightly.mozilla.org/

17
accessible/.eslintrc.js Normal file
View file

@ -0,0 +1,17 @@
"use strict";
module.exports = {
"extends": [
"../.eslintrc.js"
],
"globals": {
"Cc": true,
"Ci": true,
"Components": true,
"console": true,
"Cu": true,
"dump": true,
"Services": true,
"XPCOMUtils": true
}
};

View file

@ -0,0 +1,68 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessibleNode.h"
#include "mozilla/dom/AccessibleNodeBinding.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "Accessible-inl.h"
#include "nsAccessibilityService.h"
#include "DocAccessible.h"
using namespace mozilla;
using namespace mozilla::a11y;
using namespace mozilla::dom;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(AccessibleNode)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AccessibleNode)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(AccessibleNode)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AccessibleNode)
AccessibleNode::AccessibleNode(nsINode* aNode) : mDOMNode(aNode)
{
DocAccessible* doc =
GetOrCreateAccService()->GetDocAccessible(mDOMNode->OwnerDoc());
if (doc) {
mIntl = doc->GetAccessible(mDOMNode);
}
}
AccessibleNode::~AccessibleNode()
{
}
/* virtual */ JSObject*
AccessibleNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return AccessibleNodeBinding::Wrap(aCx, this, aGivenProto);
}
/* virtual */ ParentObject
AccessibleNode::GetParentObject() const
{
return mDOMNode->GetParentObject();
}
void
AccessibleNode::GetRole(nsAString& aRole)
{
if (mIntl) {
GetOrCreateAccService()->GetStringRole(mIntl->Role(), aRole);
return;
}
aRole.AssignLiteral("unknown");
}
nsINode*
AccessibleNode::GetDOMNode()
{
return mDOMNode;
}

View file

@ -0,0 +1,53 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=40: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef A11Y_AOM_ACCESSIBLENODE_H
#define A11Y_AOM_ACCESSIBLENODE_H
#include "nsWrapperCache.h"
#include "mozilla/RefPtr.h"
class nsINode;
namespace mozilla {
namespace a11y {
class Accessible;
}
namespace dom {
struct ParentObject;
class AccessibleNode : public nsISupports,
public nsWrapperCache
{
public:
explicit AccessibleNode(nsINode* aNode);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AccessibleNode);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
virtual dom::ParentObject GetParentObject() const final;
void GetRole(nsAString& aRole);
nsINode* GetDOMNode();
protected:
AccessibleNode(const AccessibleNode& aCopy) = delete;
AccessibleNode& operator=(const AccessibleNode& aCopy) = delete;
virtual ~AccessibleNode();
RefPtr<a11y::Accessible> mIntl;
RefPtr<nsINode> mDOMNode;
};
} // dom
} // mozilla
#endif // A11Y_JSAPI_ACCESSIBLENODE

40
accessible/aom/moz.build Normal file
View file

@ -0,0 +1,40 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.dom += [
'AccessibleNode.h',
]
UNIFIED_SOURCES += [
'AccessibleNode.cpp',
]
LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/generic',
]
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
LOCAL_INCLUDES += [
'/accessible/atk',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
LOCAL_INCLUDES += [
'/accessible/mac',
]
else:
LOCAL_INCLUDES += [
'/accessible/other',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View file

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H
#define MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H
#include "ARIAGridAccessible.h"
namespace mozilla {
namespace a11y {
typedef class ARIAGridAccessible ARIAGridAccessibleWrap;
typedef class ARIAGridCellAccessible ARIAGridCellAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,92 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __NS_ACCESSIBLE_WRAP_H__
#define __NS_ACCESSIBLE_WRAP_H__
#include "nsCOMPtr.h"
#include "Accessible.h"
struct _AtkObject;
typedef struct _AtkObject AtkObject;
enum AtkProperty {
PROP_0, // gobject convention
PROP_NAME,
PROP_DESCRIPTION,
PROP_PARENT, // ancestry has changed
PROP_ROLE,
PROP_LAYER,
PROP_MDI_ZORDER,
PROP_TABLE_CAPTION,
PROP_TABLE_COLUMN_DESCRIPTION,
PROP_TABLE_COLUMN_HEADER,
PROP_TABLE_ROW_DESCRIPTION,
PROP_TABLE_ROW_HEADER,
PROP_TABLE_SUMMARY,
PROP_LAST // gobject convention
};
struct AtkPropertyChange {
int32_t type; // property type as listed above
void *oldvalue;
void *newvalue;
};
namespace mozilla {
namespace a11y {
class MaiHyperlink;
/**
* AccessibleWrap, and its descendents in atk directory provide the
* implementation of AtkObject.
*/
class AccessibleWrap : public Accessible
{
public:
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
virtual ~AccessibleWrap();
void ShutdownAtkObject();
virtual void Shutdown() override;
// return the atk object for this AccessibleWrap
virtual void GetNativeInterface(void** aOutAccessible) override;
virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
AtkObject * GetAtkObject(void);
static AtkObject* GetAtkObject(Accessible* aAccessible);
bool IsValidObject();
static const char * ReturnString(nsAString &aString) {
static nsCString returnedString;
returnedString = NS_ConvertUTF16toUTF8(aString);
return returnedString.get();
}
static void GetKeyBinding(Accessible* aAccessible, nsAString& aResult);
static Accessible* GetColumnHeader(TableAccessible* aAccessible,
int32_t aColIdx);
static Accessible* GetRowHeader(TableAccessible* aAccessible,
int32_t aRowIdx);
protected:
nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject *aObject);
nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject);
AtkObject *mAtkObject;
private:
uint16_t CreateMaiInterfaces();
};
} // namespace a11y
} // namespace mozilla
#endif /* __NS_ACCESSIBLE_WRAP_H__ */

View file

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ApplicationAccessibleWrap.h"
#include "nsCOMPtr.h"
#include "nsMai.h"
#include "nsAccessibilityService.h"
#include <gtk/gtk.h>
#include <atk/atk.h>
using namespace mozilla;
using namespace mozilla::a11y;
// ApplicationAccessibleWrap
ApplicationAccessibleWrap::ApplicationAccessibleWrap():
ApplicationAccessible()
{
}
ApplicationAccessibleWrap::~ApplicationAccessibleWrap()
{
AccessibleWrap::ShutdownAtkObject();
}
gboolean
toplevel_event_watcher(GSignalInvocationHint* ihint,
guint n_param_values,
const GValue* param_values,
gpointer data)
{
static GQuark sQuark_gecko_acc_obj = 0;
if (!sQuark_gecko_acc_obj)
sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj");
if (nsAccessibilityService::IsShutdown())
return TRUE;
GObject* object = reinterpret_cast<GObject*>(g_value_get_object(param_values));
if (!GTK_IS_WINDOW(object))
return TRUE;
AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object));
// GTK native dialog
if (!IS_MAI_OBJECT(child) &&
(atk_object_get_role(child) == ATK_ROLE_DIALOG)) {
if (data == reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW)) {
// Attach the dialog accessible to app accessible tree
Accessible* windowAcc = GetAccService()->AddNativeRootAccessible(child);
g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj,
reinterpret_cast<gpointer>(windowAcc));
} else {
// Deattach the dialog accessible
Accessible* windowAcc =
reinterpret_cast<Accessible*>
(g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj));
if (windowAcc) {
GetAccService()->RemoveNativeRootAccessible(windowAcc);
g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, nullptr);
}
}
}
return TRUE;
}
ENameValueFlag
ApplicationAccessibleWrap::Name(nsString& aName)
{
// ATK doesn't provide a way to obtain an application name (for example,
// Firefox or Thunderbird) like IA2 does. Thus let's return an application
// name as accessible name that was used to get a branding name (for example,
// Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
AppName(aName);
return eNameOK;
}
void
ApplicationAccessibleWrap::GetNativeInterface(void** aOutAccessible)
{
*aOutAccessible = nullptr;
if (!mAtkObject) {
mAtkObject =
reinterpret_cast<AtkObject*>(g_object_new(MAI_TYPE_ATK_OBJECT, nullptr));
if (!mAtkObject)
return;
atk_object_initialize(mAtkObject, this);
mAtkObject->role = ATK_ROLE_INVALID;
mAtkObject->layer = ATK_LAYER_INVALID;
}
*aOutAccessible = mAtkObject;
}
struct AtkRootAccessibleAddedEvent {
AtkObject *app_accessible;
AtkObject *root_accessible;
uint32_t index;
};
gboolean fireRootAccessibleAddedCB(gpointer data)
{
AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data;
g_signal_emit_by_name(eventData->app_accessible, "children_changed::add",
eventData->index, eventData->root_accessible, nullptr);
g_object_unref(eventData->app_accessible);
g_object_unref(eventData->root_accessible);
free(data);
return FALSE;
}
bool
ApplicationAccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aChild)
{
if (!ApplicationAccessible::InsertChildAt(aIdx, aChild))
return false;
AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
atk_object_set_parent(atkAccessible, mAtkObject);
uint32_t count = mChildren.Length();
// Emit children_changed::add in a timeout
// to make sure aRootAccWrap is fully initialized.
AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)
malloc(sizeof(AtkRootAccessibleAddedEvent));
if (eventData) {
eventData->app_accessible = mAtkObject;
eventData->root_accessible = atkAccessible;
eventData->index = count -1;
g_object_ref(mAtkObject);
g_object_ref(atkAccessible);
g_timeout_add(0, fireRootAccessibleAddedCB, eventData);
}
return true;
}
bool
ApplicationAccessibleWrap::RemoveChild(Accessible* aChild)
{
int32_t index = aChild->IndexInParent();
AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
atk_object_set_parent(atkAccessible, nullptr);
g_signal_emit_by_name(mAtkObject, "children_changed::remove", index,
atkAccessible, nullptr);
return ApplicationAccessible::RemoveChild(aChild);
}

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
#define mozilla_a11y_ApplicationAccessibleWrap_h__
#include "ApplicationAccessible.h"
namespace mozilla {
namespace a11y {
class ApplicationAccessibleWrap: public ApplicationAccessible
{
public:
ApplicationAccessibleWrap();
virtual ~ApplicationAccessibleWrap();
// Accessible
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) override;
virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
virtual bool RemoveChild(Accessible* aChild) override;
/**
* Return the atk object for app root accessible.
*/
virtual void GetNativeInterface(void** aOutAccessible) override;
};
} // namespace a11y
} // namespace mozilla
#endif /* __NS_APP_ROOT_ACCESSIBLE_H__ */

View file

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <atk/atk.h>
#include "AtkSocketAccessible.h"
#include "InterfaceInitFuncs.h"
#include "nsMai.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
AtkSocketEmbedType AtkSocketAccessible::g_atk_socket_embed = nullptr;
GType AtkSocketAccessible::g_atk_socket_type = G_TYPE_INVALID;
const char* AtkSocketAccessible::sATKSocketEmbedSymbol = "atk_socket_embed";
const char* AtkSocketAccessible::sATKSocketGetTypeSymbol = "atk_socket_get_type";
bool AtkSocketAccessible::gCanEmbed = FALSE;
extern "C" void mai_atk_component_iface_init(AtkComponentIface* aIface);
G_DEFINE_TYPE_EXTENDED(MaiAtkSocket, mai_atk_socket,
AtkSocketAccessible::g_atk_socket_type, 0,
G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
mai_atk_component_iface_init))
void
mai_atk_socket_class_init(MaiAtkSocketClass* aAcc)
{
}
void
mai_atk_socket_init(MaiAtkSocket* aAcc)
{
}
static AtkObject*
mai_atk_socket_new(AccessibleWrap* aAccWrap)
{
NS_ENSURE_TRUE(aAccWrap, nullptr);
MaiAtkSocket* acc = nullptr;
acc = static_cast<MaiAtkSocket*>(g_object_new(MAI_TYPE_ATK_SOCKET, nullptr));
NS_ENSURE_TRUE(acc, nullptr);
acc->accWrap = aAccWrap;
return ATK_OBJECT(acc);
}
extern "C" {
static AtkObject*
RefAccessibleAtPoint(AtkComponent* aComponent, gint aX, gint aY,
AtkCoordType aCoordType)
{
NS_ENSURE_TRUE(MAI_IS_ATK_SOCKET(aComponent), nullptr);
return refAccessibleAtPointHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)),
aX, aY, aCoordType);
}
static void
GetExtents(AtkComponent* aComponent, gint* aX, gint* aY, gint* aWidth,
gint* aHeight, AtkCoordType aCoordType)
{
*aX = *aY = *aWidth = *aHeight = 0;
if (!MAI_IS_ATK_SOCKET(aComponent))
return;
getExtentsHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)),
aX, aY, aWidth, aHeight, aCoordType);
}
}
void
mai_atk_component_iface_init(AtkComponentIface* aIface)
{
NS_ASSERTION(aIface, "Invalid Interface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->ref_accessible_at_point = RefAccessibleAtPoint;
aIface->get_extents = GetExtents;
}
AtkSocketAccessible::AtkSocketAccessible(nsIContent* aContent,
DocAccessible* aDoc,
const nsCString& aPlugId) :
AccessibleWrap(aContent, aDoc)
{
mAtkObject = mai_atk_socket_new(this);
if (!mAtkObject)
return;
// Embeds the children of an AtkPlug, specified by plugId, as the children of
// this socket.
// Using G_TYPE macros instead of ATK_SOCKET macros to avoid undefined
// symbols.
if (gCanEmbed && G_TYPE_CHECK_INSTANCE_TYPE(mAtkObject, g_atk_socket_type) &&
!aPlugId.IsVoid()) {
AtkSocket* accSocket =
G_TYPE_CHECK_INSTANCE_CAST(mAtkObject, g_atk_socket_type, AtkSocket);
g_atk_socket_embed(accSocket, (gchar*)aPlugId.get());
}
}
void
AtkSocketAccessible::GetNativeInterface(void** aOutAccessible)
{
*aOutAccessible = mAtkObject;
}
void
AtkSocketAccessible::Shutdown()
{
if (mAtkObject) {
if (MAI_IS_ATK_SOCKET(mAtkObject))
MAI_ATK_SOCKET(mAtkObject)->accWrap = nullptr;
g_object_unref(mAtkObject);
mAtkObject = nullptr;
}
AccessibleWrap::Shutdown();
}

View file

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _AtkSocketAccessible_H_
#define _AtkSocketAccessible_H_
#include "AccessibleWrap.h"
// This file gets included by nsAccessibilityService.cpp, which can't include
// atk.h (or glib.h), so we can't rely on it being included.
#ifdef __ATK_H__
extern "C" typedef void (*AtkSocketEmbedType) (AtkSocket*, gchar*);
#else
extern "C" typedef void (*AtkSocketEmbedType) (void*, void*);
#endif
namespace mozilla {
namespace a11y {
/**
* Provides a AccessibleWrap wrapper around AtkSocket for out-of-process
* accessibles.
*/
class AtkSocketAccessible : public AccessibleWrap
{
public:
// Soft references to AtkSocket
static AtkSocketEmbedType g_atk_socket_embed;
#ifdef __ATK_H__
static GType g_atk_socket_type;
#endif
static const char* sATKSocketEmbedSymbol;
static const char* sATKSocketGetTypeSymbol;
/*
* True if the current Atk version supports AtkSocket and it was correctly
* loaded.
*/
static bool gCanEmbed;
AtkSocketAccessible(nsIContent* aContent, DocAccessible* aDoc,
const nsCString& aPlugId);
virtual void Shutdown() override;
virtual void GetNativeInterface(void** aOutAccessible) override;
};
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMai.h"
#include "DocAccessibleWrap.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// DocAccessibleWrap
////////////////////////////////////////////////////////////////////////////////
DocAccessibleWrap::
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
DocAccessible(aDocument, aPresShell), mActivated(false)
{
}
DocAccessibleWrap::~DocAccessibleWrap()
{
}

View file

@ -0,0 +1,31 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* For documentation of the accessibility architecture,
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
*/
#ifndef mozilla_a11y_DocAccessibleWrap_h__
#define mozilla_a11y_DocAccessibleWrap_h__
#include "DocAccessible.h"
namespace mozilla {
namespace a11y {
class DocAccessibleWrap : public DocAccessible
{
public:
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
virtual ~DocAccessibleWrap();
bool mActivated;
};
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_HTMLTableAccessibleWrap_h__
#define mozilla_a11y_HTMLTableAccessibleWrap_h__
#include "HTMLTableAccessible.h"
namespace mozilla {
namespace a11y {
typedef class HTMLTableAccessible HTMLTableAccessibleWrap;
typedef class HTMLTableCellAccessible HTMLTableCellAccessibleWrap;
typedef class HTMLTableHeaderCellAccessible HTMLTableHeaderCellAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_HyperTextAccessibleWrap_h__
#define mozilla_a11y_HyperTextAccessibleWrap_h__
#include "HyperTextAccessible.h"
namespace mozilla {
namespace a11y {
typedef class HyperTextAccessible HyperTextAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_ImageAccessibleWrap_h__
#define mozilla_a11y_ImageAccessibleWrap_h__
#include "ImageAccessible.h"
namespace mozilla {
namespace a11y {
typedef class ImageAccessible ImageAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* * You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ATK_INTERFACE_INIT_FUNCS_H_
#define ATK_INTERFACE_INIT_FUNCS_H_
#include <atk/atk.h>
namespace mozilla {
namespace a11y {
class AccessibleWrap;
} // namespace a11y
} // namespace mozilla
extern "C" {
void actionInterfaceInitCB(AtkActionIface* aIface);
void componentInterfaceInitCB(AtkComponentIface* aIface);
void documentInterfaceInitCB(AtkDocumentIface *aIface);
void editableTextInterfaceInitCB(AtkEditableTextIface* aIface);
void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface *aIface);
void hypertextInterfaceInitCB(AtkHypertextIface* aIface);
void imageInterfaceInitCB(AtkImageIface* aIface);
void selectionInterfaceInitCB(AtkSelectionIface* aIface);
void tableInterfaceInitCB(AtkTableIface *aIface);
void tableCellInterfaceInitCB(AtkTableCellIface *aIface);
void textInterfaceInitCB(AtkTextIface* aIface);
void valueInterfaceInitCB(AtkValueIface *aIface);
}
/**
* XXX these should live in a file of utils for atk.
*/
AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj,
gint aX, gint aY, AtkCoordType aCoordType);
void getExtentsHelper(AtkObject* aAtkObj,
gint* aX, gint* aY, gint* aWidth, gint* aHeight,
AtkCoordType aCoordType);
#endif // ATK_INTERFACE_INIT_FUNCS_H_

377
accessible/atk/Platform.cpp Normal file
View file

@ -0,0 +1,377 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Platform.h"
#include "nsIAccessibleEvent.h"
#include "nsIGConfService.h"
#include "nsIServiceManager.h"
#include "nsMai.h"
#include "AtkSocketAccessible.h"
#include "prenv.h"
#include "prlink.h"
#ifdef MOZ_ENABLE_DBUS
#include <dbus/dbus.h>
#endif
#include <gtk/gtk.h>
#if (MOZ_WIDGET_GTK == 3)
extern "C" __attribute__((weak,visibility("default"))) int atk_bridge_adaptor_init(int*, char **[]);
#endif
using namespace mozilla;
using namespace mozilla::a11y;
int atkMajorVersion = 1, atkMinorVersion = 12;
GType (*gAtkTableCellGetTypeFunc)();
extern "C" {
typedef GType (* AtkGetTypeType) (void);
typedef void (*GnomeAccessibilityInit) (void);
typedef void (*GnomeAccessibilityShutdown) (void);
}
static PRLibrary* sATKLib = nullptr;
static const char sATKLibName[] = "libatk-1.0.so.0";
static const char sATKHyperlinkImplGetTypeSymbol[] =
"atk_hyperlink_impl_get_type";
gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
gpointer);
static bool sToplevel_event_hook_added = false;
static gulong sToplevel_show_hook = 0;
static gulong sToplevel_hide_hook = 0;
GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
struct GnomeAccessibilityModule
{
const char *libName;
PRLibrary *lib;
const char *initName;
GnomeAccessibilityInit init;
const char *shutdownName;
GnomeAccessibilityShutdown shutdown;
};
static GnomeAccessibilityModule sAtkBridge = {
#ifdef AIX
"libatk-bridge.a(libatk-bridge.so.0)", nullptr,
#else
"libatk-bridge.so", nullptr,
#endif
"gnome_accessibility_module_init", nullptr,
"gnome_accessibility_module_shutdown", nullptr
};
#if (MOZ_WIDGET_GTK == 2)
static GnomeAccessibilityModule sGail = {
"libgail.so", nullptr,
"gnome_accessibility_module_init", nullptr,
"gnome_accessibility_module_shutdown", nullptr
};
#endif
static nsresult
LoadGtkModule(GnomeAccessibilityModule& aModule)
{
NS_ENSURE_ARG(aModule.libName);
if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
//try to load the module with "gtk-2.0/modules" appended
char *curLibPath = PR_GetLibraryPath();
nsAutoCString libPath(curLibPath);
#if defined(LINUX) && defined(__x86_64__)
libPath.AppendLiteral(":/usr/lib64:/usr/lib");
#else
libPath.AppendLiteral(":/usr/lib");
#endif
PR_FreeLibraryName(curLibPath);
int16_t loc1 = 0, loc2 = 0;
int16_t subLen = 0;
while (loc2 >= 0) {
loc2 = libPath.FindChar(':', loc1);
if (loc2 < 0)
subLen = libPath.Length() - loc1;
else
subLen = loc2 - loc1;
nsAutoCString sub(Substring(libPath, loc1, subLen));
#if (MOZ_WIDGET_GTK == 2)
sub.AppendLiteral("/gtk-2.0/modules/");
#else
sub.AppendLiteral("/gtk-3.0/modules/");
#endif
sub.Append(aModule.libName);
aModule.lib = PR_LoadLibrary(sub.get());
if (aModule.lib)
break;
loc1 = loc2+1;
}
if (!aModule.lib)
return NS_ERROR_FAILURE;
}
//we have loaded the library, try to get the function ptrs
if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
aModule.initName)) ||
!(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
aModule.shutdownName))) {
//fail, :(
PR_UnloadLibrary(aModule.lib);
aModule.lib = nullptr;
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
a11y::PlatformInit()
{
if (!ShouldA11yBeEnabled())
return;
sATKLib = PR_LoadLibrary(sATKLibName);
if (!sATKLib)
return;
AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
(AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
if (pfn_atk_hyperlink_impl_get_type)
g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
if (pfn_atk_socket_get_type) {
AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
AtkSocketAccessible::gCanEmbed =
AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
AtkSocketAccessible::g_atk_socket_embed;
}
gAtkTableCellGetTypeFunc = (GType (*)())
PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
const char* (*atkGetVersion)() =
(const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
if (atkGetVersion) {
const char* version = atkGetVersion();
if (version) {
char* endPtr = nullptr;
atkMajorVersion = strtol(version, &endPtr, 10);
if (*endPtr == '.')
atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
}
}
#if (MOZ_WIDGET_GTK == 2)
// Load and initialize gail library.
nsresult rv = LoadGtkModule(sGail);
if (NS_SUCCEEDED(rv))
(*sGail.init)();
#endif
// Initialize the MAI Utility class, it will overwrite gail_util.
g_type_class_unref(g_type_class_ref(mai_util_get_type()));
// Init atk-bridge now
PR_SetEnv("NO_AT_BRIDGE=0");
#if (MOZ_WIDGET_GTK == 3)
if (atk_bridge_adaptor_init) {
atk_bridge_adaptor_init(nullptr, nullptr);
} else
#endif
{
nsresult rv = LoadGtkModule(sAtkBridge);
if (NS_SUCCEEDED(rv)) {
(*sAtkBridge.init)();
}
}
if (!sToplevel_event_hook_added) {
sToplevel_event_hook_added = true;
sToplevel_show_hook =
g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
0, toplevel_event_watcher,
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
nullptr);
sToplevel_hide_hook =
g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
toplevel_event_watcher,
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
nullptr);
}
}
void
a11y::PlatformShutdown()
{
if (sToplevel_event_hook_added) {
sToplevel_event_hook_added = false;
g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
sToplevel_show_hook);
g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
sToplevel_hide_hook);
}
if (sAtkBridge.lib) {
// Do not shutdown/unload atk-bridge,
// an exit function registered will take care of it
// if (sAtkBridge.shutdown)
// (*sAtkBridge.shutdown)();
// PR_UnloadLibrary(sAtkBridge.lib);
sAtkBridge.lib = nullptr;
sAtkBridge.init = nullptr;
sAtkBridge.shutdown = nullptr;
}
#if (MOZ_WIDGET_GTK == 2)
if (sGail.lib) {
// Do not shutdown gail because
// 1) Maybe it's not init-ed by us. e.g. GtkEmbed
// 2) We need it to avoid assert in spi_atk_tidy_windows
// if (sGail.shutdown)
// (*sGail.shutdown)();
// PR_UnloadLibrary(sGail.lib);
sGail.lib = nullptr;
sGail.init = nullptr;
sGail.shutdown = nullptr;
}
#endif
// if (sATKLib) {
// PR_UnloadLibrary(sATKLib);
// sATKLib = nullptr;
// }
}
static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
#ifdef MOZ_ENABLE_DBUS
static DBusPendingCall *sPendingCall = nullptr;
#endif
void
a11y::PreInit()
{
#ifdef MOZ_ENABLE_DBUS
static bool sChecked = FALSE;
if (sChecked)
return;
sChecked = TRUE;
// dbus is only checked if GNOME_ACCESSIBILITY is unset
// also make sure that a session bus address is available to prevent dbus from
// starting a new one. Dbus confuses the test harness when it creates a new
// process (see bug 693343)
if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
return;
DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
if (!bus)
return;
dbus_connection_set_exit_on_disconnect(bus, FALSE);
static const char* iface = "org.a11y.Status";
static const char* member = "IsEnabled";
DBusMessage *message;
message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
"org.freedesktop.DBus.Properties",
"Get");
if (!message)
goto dbus_done;
dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
dbus_message_unref(message);
dbus_done:
dbus_connection_unref(bus);
#endif
}
bool
a11y::ShouldA11yBeEnabled()
{
static bool sChecked = false, sShouldEnable = false;
if (sChecked)
return sShouldEnable;
sChecked = true;
EPlatformDisabledState disabledState = PlatformDisabledState();
if (disabledState == ePlatformIsDisabled)
return sShouldEnable = false;
// check if accessibility enabled/disabled by environment variable
const char* envValue = PR_GetEnv(sAccEnv);
if (envValue)
return sShouldEnable = !!atoi(envValue);
#ifdef MOZ_ENABLE_DBUS
PreInit();
bool dbusSuccess = false;
DBusMessage *reply = nullptr;
if (!sPendingCall)
goto dbus_done;
dbus_pending_call_block(sPendingCall);
reply = dbus_pending_call_steal_reply(sPendingCall);
dbus_pending_call_unref(sPendingCall);
sPendingCall = nullptr;
if (!reply ||
dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
goto dbus_done;
DBusMessageIter iter, iter_variant, iter_struct;
dbus_bool_t dResult;
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse (&iter, &iter_variant);
switch (dbus_message_iter_get_arg_type(&iter_variant)) {
case DBUS_TYPE_STRUCT:
// at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
dbus_message_iter_recurse(&iter_variant, &iter_struct);
if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
dbus_message_iter_get_basic(&iter_struct, &dResult);
sShouldEnable = dResult;
dbusSuccess = true;
}
break;
case DBUS_TYPE_BOOLEAN:
dbus_message_iter_get_basic(&iter_variant, &dResult);
sShouldEnable = dResult;
dbusSuccess = true;
break;
default:
break;
}
dbus_done:
if (reply)
dbus_message_unref(reply);
if (dbusSuccess)
return sShouldEnable;
#endif
//check gconf-2 setting
#define GCONF_A11Y_KEY "/desktop/gnome/interface/accessibility"
nsresult rv = NS_OK;
nsCOMPtr<nsIGConfService> gconf =
do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && gconf)
gconf->GetBool(NS_LITERAL_CSTRING(GCONF_A11Y_KEY), &sShouldEnable);
return sShouldEnable;
}

View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "RootAccessibleWrap.h"
#include "nsMai.h"
using namespace mozilla::a11y;
GtkWindowAccessible::GtkWindowAccessible(AtkObject* aAccessible) :
DummyAccessible()
{
g_object_ref(aAccessible);
mAtkObject = aAccessible;
}
GtkWindowAccessible::~GtkWindowAccessible()
{
g_object_unref(mAtkObject);
mAtkObject = nullptr;
}

View file

@ -0,0 +1,34 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_RootAccessibleWrap_h__
#define mozilla_a11y_RootAccessibleWrap_h__
#include "BaseAccessibles.h"
#include "RootAccessible.h"
namespace mozilla {
namespace a11y {
typedef RootAccessible RootAccessibleWrap;
/* GtkWindowAccessible is the accessible class for gtk+ native window.
* The instance of GtkWindowAccessible is a child of MaiAppRoot instance.
* It is added into root when the toplevel window is created, and removed
* from root when the toplevel window is destroyed.
*/
class GtkWindowAccessible final : public DummyAccessible
{
public:
explicit GtkWindowAccessible(AtkObject* aAccessible);
virtual ~GtkWindowAccessible();
};
} // namespace a11y
} // namespace mozilla
#endif /* mozilla_a11y_Root_Accessible_Wrap_h__ */

View file

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_TextLeafAccessibleWrap_h__
#define mozilla_a11y_TextLeafAccessibleWrap_h__
#include "TextLeafAccessible.h"
namespace mozilla {
namespace a11y {
typedef class TextLeafAccessible TextLeafAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,411 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ApplicationAccessibleWrap.h"
#include "mozilla/Likely.h"
#include "nsAccessibilityService.h"
#include "nsMai.h"
#include <atk/atk.h>
#include <gtk/gtk.h>
#include <string.h>
using namespace mozilla;
using namespace mozilla::a11y;
typedef AtkUtil MaiUtil;
typedef AtkUtilClass MaiUtilClass;
#define MAI_VERSION MOZILLA_VERSION
#define MAI_NAME "Gecko"
extern "C" {
static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
const gchar* event_type);
static void (*gail_remove_global_event_listener) (guint remove_listener);
static void (*gail_remove_key_event_listener) (guint remove_listener);
static AtkObject* (*gail_get_root)();
}
struct MaiUtilListenerInfo
{
gint key;
guint signal_id;
gulong hook_id;
// For window create/destory/minimize/maximize/restore/activate/deactivate
// events, we'll chain gail_util's add/remove_global_event_listener.
// So we store the listenerid returned by gail's add_global_event_listener
// in this structure to call gail's remove_global_event_listener later.
guint gail_listenerid;
};
static GHashTable* sListener_list = nullptr;
static gint sListener_idx = 1;
extern "C" {
static guint
add_listener (GSignalEmissionHook listener,
const gchar *object_type,
const gchar *signal,
const gchar *hook_data,
guint gail_listenerid = 0)
{
GType type;
guint signal_id;
gint rc = 0;
type = g_type_from_name(object_type);
if (type) {
signal_id = g_signal_lookup(signal, type);
if (signal_id > 0) {
MaiUtilListenerInfo *listener_info;
rc = sListener_idx;
listener_info = (MaiUtilListenerInfo *)
g_malloc(sizeof(MaiUtilListenerInfo));
listener_info->key = sListener_idx;
listener_info->hook_id =
g_signal_add_emission_hook(signal_id, 0, listener,
g_strdup(hook_data),
(GDestroyNotify)g_free);
listener_info->signal_id = signal_id;
listener_info->gail_listenerid = gail_listenerid;
g_hash_table_insert(sListener_list, &(listener_info->key),
listener_info);
sListener_idx++;
}
else {
g_warning("Invalid signal type %s\n", signal);
}
}
else {
g_warning("Invalid object type %s\n", object_type);
}
return rc;
}
static guint
mai_util_add_global_event_listener(GSignalEmissionHook listener,
const gchar *event_type)
{
guint rc = 0;
gchar **split_string;
split_string = g_strsplit (event_type, ":", 3);
if (split_string) {
if (!strcmp ("window", split_string[0])) {
guint gail_listenerid = 0;
if (gail_add_global_event_listener) {
// call gail's function to track gtk native window events
gail_listenerid =
gail_add_global_event_listener(listener, event_type);
}
rc = add_listener (listener, "MaiAtkObject", split_string[1],
event_type, gail_listenerid);
}
else {
rc = add_listener (listener, split_string[1], split_string[2],
event_type);
}
g_strfreev(split_string);
}
return rc;
}
static void
mai_util_remove_global_event_listener(guint remove_listener)
{
if (remove_listener > 0) {
MaiUtilListenerInfo *listener_info;
gint tmp_idx = remove_listener;
listener_info = (MaiUtilListenerInfo *)
g_hash_table_lookup(sListener_list, &tmp_idx);
if (listener_info != nullptr) {
if (gail_remove_global_event_listener &&
listener_info->gail_listenerid) {
gail_remove_global_event_listener(listener_info->gail_listenerid);
}
/* Hook id of 0 and signal id of 0 are invalid */
if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
/* Remove the emission hook */
g_signal_remove_emission_hook(listener_info->signal_id,
listener_info->hook_id);
/* Remove the element from the hash */
g_hash_table_remove(sListener_list, &tmp_idx);
}
else {
g_warning("Invalid listener hook_id %ld or signal_id %d\n",
listener_info->hook_id, listener_info->signal_id);
}
}
else {
// atk-bridge is initialized with gail (e.g. yelp)
// try gail_remove_global_event_listener
if (gail_remove_global_event_listener) {
return gail_remove_global_event_listener(remove_listener);
}
g_warning("No listener with the specified listener id %d",
remove_listener);
}
}
else {
g_warning("Invalid listener_id %d", remove_listener);
}
}
static AtkKeyEventStruct *
atk_key_event_from_gdk_event_key (GdkEventKey *key)
{
AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
switch (key->type) {
case GDK_KEY_PRESS:
event->type = ATK_KEY_EVENT_PRESS;
break;
case GDK_KEY_RELEASE:
event->type = ATK_KEY_EVENT_RELEASE;
break;
default:
g_assert_not_reached ();
return nullptr;
}
event->state = key->state;
event->keyval = key->keyval;
event->length = key->length;
if (key->string && key->string [0] &&
(key->state & GDK_CONTROL_MASK ||
g_unichar_isgraph (g_utf8_get_char (key->string)))) {
event->string = key->string;
}
else if (key->type == GDK_KEY_PRESS ||
key->type == GDK_KEY_RELEASE) {
event->string = gdk_keyval_name (key->keyval);
}
event->keycode = key->hardware_keycode;
event->timestamp = key->time;
return event;
}
struct MaiKeyEventInfo
{
AtkKeyEventStruct *key_event;
gpointer func_data;
};
union AtkKeySnoopFuncPointer
{
AtkKeySnoopFunc func_ptr;
gpointer data;
};
static gboolean
notify_hf(gpointer key, gpointer value, gpointer data)
{
MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
AtkKeySnoopFuncPointer atkKeySnoop;
atkKeySnoop.data = value;
return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
}
static void
insert_hf(gpointer key, gpointer value, gpointer data)
{
GHashTable *new_table = (GHashTable *) data;
g_hash_table_insert (new_table, key, value);
}
static GHashTable* sKey_listener_list = nullptr;
static gint
mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
{
/* notify each AtkKeySnoopFunc in turn... */
MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
gint consumed = 0;
if (sKey_listener_list) {
GHashTable *new_hash = g_hash_table_new(nullptr, nullptr);
g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
info->key_event = atk_key_event_from_gdk_event_key (event);
info->func_data = func_data;
consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
g_hash_table_destroy (new_hash);
g_free(info->key_event);
}
g_free(info);
return (consumed ? 1 : 0);
}
static guint sKey_snooper_id = 0;
static guint
mai_util_add_key_event_listener(AtkKeySnoopFunc listener, gpointer data)
{
if (MOZ_UNLIKELY(!listener)) {
return 0;
}
static guint key = 0;
if (!sKey_listener_list) {
sKey_listener_list = g_hash_table_new(nullptr, nullptr);
}
// If we have no registered event listeners then we need to (re)install the
// key event snooper.
if (g_hash_table_size(sKey_listener_list) == 0) {
sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
}
AtkKeySnoopFuncPointer atkKeySnoop;
atkKeySnoop.func_ptr = listener;
key++;
g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key),
atkKeySnoop.data);
return key;
}
static void
mai_util_remove_key_event_listener (guint remove_listener)
{
if (!sKey_listener_list) {
// atk-bridge is initialized with gail (e.g. yelp)
// try gail_remove_key_event_listener
return gail_remove_key_event_listener(remove_listener);
}
g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
if (g_hash_table_size(sKey_listener_list) == 0) {
gtk_key_snooper_remove(sKey_snooper_id);
}
}
static AtkObject*
mai_util_get_root()
{
ApplicationAccessible* app = ApplicationAcc();
if (app)
return app->GetAtkObject();
// We've shutdown, try to use gail instead
// (to avoid assert in spi_atk_tidy_windows())
// XXX tbsaunde then why didn't we replace the gail atk_util impl?
if (gail_get_root)
return gail_get_root();
return nullptr;
}
static const gchar*
mai_util_get_toolkit_name()
{
return MAI_NAME;
}
static const gchar*
mai_util_get_toolkit_version()
{
return MAI_VERSION;
}
static void
_listener_info_destroy(gpointer data)
{
g_free(data);
}
static void
window_added (AtkObject *atk_obj,
guint index,
AtkObject *child)
{
if (!IS_MAI_OBJECT(child))
return;
static guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
g_signal_emit (child, id, 0);
}
static void
window_removed (AtkObject *atk_obj,
guint index,
AtkObject *child)
{
if (!IS_MAI_OBJECT(child))
return;
static guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
g_signal_emit (child, id, 0);
}
static void
UtilInterfaceInit(MaiUtilClass* klass)
{
AtkUtilClass *atk_class;
gpointer data;
data = g_type_class_peek(ATK_TYPE_UTIL);
atk_class = ATK_UTIL_CLASS(data);
// save gail function pointer
gail_add_global_event_listener = atk_class->add_global_event_listener;
gail_remove_global_event_listener = atk_class->remove_global_event_listener;
gail_remove_key_event_listener = atk_class->remove_key_event_listener;
gail_get_root = atk_class->get_root;
atk_class->add_global_event_listener =
mai_util_add_global_event_listener;
atk_class->remove_global_event_listener =
mai_util_remove_global_event_listener;
atk_class->add_key_event_listener = mai_util_add_key_event_listener;
atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
atk_class->get_root = mai_util_get_root;
atk_class->get_toolkit_name = mai_util_get_toolkit_name;
atk_class->get_toolkit_version = mai_util_get_toolkit_version;
sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
_listener_info_destroy);
// Keep track of added/removed windows.
AtkObject *root = atk_get_root ();
g_signal_connect (root, "children-changed::add", (GCallback) window_added, nullptr);
g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, nullptr);
}
}
GType
mai_util_get_type()
{
static GType type = 0;
if (!type) {
static const GTypeInfo tinfo = {
sizeof(MaiUtilClass),
(GBaseInitFunc) nullptr, /* base init */
(GBaseFinalizeFunc) nullptr, /* base finalize */
(GClassInitFunc) UtilInterfaceInit, /* class init */
(GClassFinalizeFunc) nullptr, /* class finalize */
nullptr, /* class data */
sizeof(MaiUtil), /* instance size */
0, /* nb preallocs */
(GInstanceInitFunc) nullptr, /* instance init */
nullptr /* value table */
};
type = g_type_register_static(ATK_TYPE_UTIL,
"MaiUtil", &tinfo, GTypeFlags(0));
}
return type;
}

View file

@ -0,0 +1,21 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_XULListboxAccessibleWrap_h__
#define mozilla_a11y_XULListboxAccessibleWrap_h__
#include "XULListboxAccessible.h"
namespace mozilla {
namespace a11y {
typedef class XULListboxAccessible XULListboxAccessibleWrap;
typedef class XULListCellAccessible XULListCellAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,20 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_XULMenuAccessibleWrap_h__
#define mozilla_a11y_XULMenuAccessibleWrap_h__
#include "XULMenuAccessible.h"
namespace mozilla {
namespace a11y {
typedef class XULMenuitemAccessible XULMenuitemAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_XULTreeGridAccessibleWrap_h__
#define mozilla_a11y_XULTreeGridAccessibleWrap_h__
#include "XULTreeGridAccessible.h"
namespace mozilla {
namespace a11y {
typedef class XULTreeGridAccessible XULTreeGridAccessibleWrap;
typedef class XULTreeGridCellAccessible XULTreeGridCellAccessibleWrap;
} // namespace a11y
} // namespace mozilla
#endif

63
accessible/atk/moz.build Normal file
View file

@ -0,0 +1,63 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS.mozilla.a11y += [
'AccessibleWrap.h',
'HyperTextAccessibleWrap.h',
]
SOURCES += [
'AccessibleWrap.cpp',
'ApplicationAccessibleWrap.cpp',
'AtkSocketAccessible.cpp',
'DocAccessibleWrap.cpp',
'nsMaiHyperlink.cpp',
'nsMaiInterfaceAction.cpp',
'nsMaiInterfaceComponent.cpp',
'nsMaiInterfaceDocument.cpp',
'nsMaiInterfaceEditableText.cpp',
'nsMaiInterfaceHyperlinkImpl.cpp',
'nsMaiInterfaceHypertext.cpp',
'nsMaiInterfaceImage.cpp',
'nsMaiInterfaceSelection.cpp',
'nsMaiInterfaceTable.cpp',
'nsMaiInterfaceTableCell.cpp',
'nsMaiInterfaceText.cpp',
'nsMaiInterfaceValue.cpp',
'Platform.cpp',
'RootAccessibleWrap.cpp',
'UtilInterface.cpp',
]
LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/generic',
'/accessible/html',
'/accessible/ipc',
'/accessible/ipc/other',
'/accessible/xpcom',
'/accessible/xul',
'/other-licenses/atk-1.0',
]
FINAL_LIBRARY = 'xul'
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
CFLAGS += CONFIG['TK_CFLAGS']
CXXFLAGS += CONFIG['TK_CFLAGS']
if CONFIG['MOZ_ENABLE_DBUS']:
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
# Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib /
# gobject headers. See bug 1243331 comment 3.
CXXFLAGS += [
'-Wno-error=shadow',
'-Wno-unused-local-typedefs',
]

149
accessible/atk/nsMai.h Normal file
View file

@ -0,0 +1,149 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __NS_MAI_H__
#define __NS_MAI_H__
#include <atk/atk.h>
#include <glib.h>
#include <glib-object.h>
#include "AccessibleOrProxy.h"
#include "AccessibleWrap.h"
namespace mozilla {
namespace a11y {
class ProxyAccessible;
}
}
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
MAI_TYPE_ATK_OBJECT, MaiAtkObject))
#define MAI_ATK_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
MAI_TYPE_ATK_OBJECT, \
MaiAtkObjectClass))
#define IS_MAI_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
MAI_TYPE_ATK_OBJECT))
#define IS_MAI_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
MAI_TYPE_ATK_OBJECT))
#define MAI_ATK_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
MAI_TYPE_ATK_OBJECT, \
MaiAtkObjectClass))
GType mai_atk_object_get_type(void);
GType mai_util_get_type();
extern "C" GType mai_atk_socket_get_type(void);
/* MaiAtkSocket */
#define MAI_TYPE_ATK_SOCKET (mai_atk_socket_get_type ())
#define MAI_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\
MAI_TYPE_ATK_SOCKET, MaiAtkSocket))
#define MAI_IS_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
MAI_TYPE_ATK_SOCKET))
#define MAI_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\
MAI_TYPE_ATK_SOCKET,\
MaiAtkSocketClass))
#define MAI_IS_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\
MAI_TYPE_ATK_SOCKET))
#define MAI_ATK_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
MAI_TYPE_ATK_SOCKET,\
MaiAtkSocketClass))
typedef struct _MaiAtkSocket
{
AtkSocket parent;
mozilla::a11y::AccessibleWrap* accWrap;
} MaiAtkSocket;
typedef struct _MaiAtkSocketClass
{
AtkSocketClass parent_class;
} MaiAtkSocketClass;
// This is a pointer to the atk_table_cell_get_type function if we are using
// a version of atk that defines that.
extern "C" GType (*gAtkTableCellGetTypeFunc)();
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);
AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
AtkObject* GetWrapperFor(mozilla::a11y::AccessibleOrProxy aObj);
extern int atkMajorVersion, atkMinorVersion;
/**
* Return true if the loaded version of libatk-1.0.so is at least
* aMajor.aMinor.0.
*/
static inline bool
IsAtkVersionAtLeast(int aMajor, int aMinor)
{
return aMajor < atkMajorVersion ||
(aMajor == atkMajorVersion && aMinor <= atkMinorVersion);
}
// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a
// proxy.
static const uintptr_t IS_PROXY = 1;
/**
* This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject
*/
struct MaiAtkObject
{
AtkObject parent;
/*
* The AccessibleWrap whose properties and features are exported
* via this object instance.
*/
mozilla::a11y::AccessibleOrProxy accWrap;
/*
* Get the AtkHyperlink for this atk object.
*/
AtkHyperlink* GetAtkHyperlink();
/*
* Shutdown this AtkObject.
*/
void Shutdown();
/*
* Notify atk of a state change on this AtkObject.
*/
void FireStateChangeEvent(uint64_t aState, bool aEnabled);
/*
* Notify ATK of a text change within this ATK object.
*/
void FireTextChangeEvent(const nsString& aStr, int32_t aStart, uint32_t aLen,
bool aIsInsert, bool aIsFromUser);
/**
* Notify ATK of a shown or hidden subtree rooted at aObject whose parent is
* aParent
*/
void FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, bool aFromUser);
private:
/*
* do we have text-remove and text-insert signals if not we need to use
* text-changed see AccessibleWrap::FireAtkTextChangedEvent() and
* bug 619002
*/
enum EAvailableAtkSignals {
eUnknown,
eHaveNewAtkTextSignals,
eNoNewAtkSignals
};
static EAvailableAtkSignals gAvailableAtkSignals;
};
#endif /* __NS_MAI_H__ */

View file

@ -0,0 +1,262 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIURI.h"
#include "nsMaiHyperlink.h"
#include "mozilla/a11y/ProxyAccessible.h"
using namespace mozilla::a11y;
/* MaiAtkHyperlink */
#define MAI_TYPE_ATK_HYPERLINK (mai_atk_hyperlink_get_type ())
#define MAI_ATK_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlink))
#define MAI_ATK_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlinkClass))
#define MAI_IS_ATK_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
MAI_TYPE_ATK_HYPERLINK))
#define MAI_IS_ATK_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\
MAI_TYPE_ATK_HYPERLINK))
#define MAI_ATK_HYPERLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlinkClass))
/**
* This MaiAtkHyperlink is a thin wrapper, in the MAI namespace,
* for AtkHyperlink
*/
struct MaiAtkHyperlink
{
AtkHyperlink parent;
/*
* The MaiHyperlink whose properties and features are exported via this
* hyperlink instance.
*/
MaiHyperlink *maiHyperlink;
};
struct MaiAtkHyperlinkClass
{
AtkHyperlinkClass parent_class;
};
GType mai_atk_hyperlink_get_type(void);
G_BEGIN_DECLS
/* callbacks for AtkHyperlink */
static void classInitCB(AtkHyperlinkClass *aClass);
static void finalizeCB(GObject *aObj);
/* callbacks for AtkHyperlink virtual functions */
static gchar *getUriCB(AtkHyperlink *aLink, gint aLinkIndex);
static AtkObject *getObjectCB(AtkHyperlink *aLink, gint aLinkIndex);
static gint getEndIndexCB(AtkHyperlink *aLink);
static gint getStartIndexCB(AtkHyperlink *aLink);
static gboolean isValidCB(AtkHyperlink *aLink);
static gint getAnchorCountCB(AtkHyperlink *aLink);
G_END_DECLS
static gpointer parent_class = nullptr;
static MaiHyperlink*
GetMaiHyperlink(AtkHyperlink *aHyperlink)
{
NS_ENSURE_TRUE(MAI_IS_ATK_HYPERLINK(aHyperlink), nullptr);
MaiHyperlink * maiHyperlink =
MAI_ATK_HYPERLINK(aHyperlink)->maiHyperlink;
NS_ENSURE_TRUE(maiHyperlink != nullptr, nullptr);
NS_ENSURE_TRUE(maiHyperlink->GetAtkHyperlink() == aHyperlink, nullptr);
return maiHyperlink;
}
GType
mai_atk_hyperlink_get_type(void)
{
static GType type = 0;
if (!type) {
static const GTypeInfo tinfo = {
sizeof(MaiAtkHyperlinkClass),
(GBaseInitFunc)nullptr,
(GBaseFinalizeFunc)nullptr,
(GClassInitFunc)classInitCB,
(GClassFinalizeFunc)nullptr,
nullptr, /* class data */
sizeof(MaiAtkHyperlink), /* instance size */
0, /* nb preallocs */
(GInstanceInitFunc)nullptr,
nullptr /* value table */
};
type = g_type_register_static(ATK_TYPE_HYPERLINK,
"MaiAtkHyperlink",
&tinfo, GTypeFlags(0));
}
return type;
}
MaiHyperlink::MaiHyperlink(AccessibleOrProxy aHyperLink) :
mHyperlink(aHyperLink),
mMaiAtkHyperlink(nullptr)
{
mMaiAtkHyperlink =
reinterpret_cast<AtkHyperlink *>
(g_object_new(mai_atk_hyperlink_get_type(), nullptr));
NS_ASSERTION(mMaiAtkHyperlink, "OUT OF MEMORY");
if (!mMaiAtkHyperlink)
return;
MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = this;
}
MaiHyperlink::~MaiHyperlink()
{
if (mMaiAtkHyperlink) {
MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = nullptr;
g_object_unref(mMaiAtkHyperlink);
}
}
/* static functions for ATK callbacks */
void
classInitCB(AtkHyperlinkClass *aClass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(aClass);
parent_class = g_type_class_peek_parent(aClass);
aClass->get_uri = getUriCB;
aClass->get_object = getObjectCB;
aClass->get_end_index = getEndIndexCB;
aClass->get_start_index = getStartIndexCB;
aClass->is_valid = isValidCB;
aClass->get_n_anchors = getAnchorCountCB;
gobject_class->finalize = finalizeCB;
}
void
finalizeCB(GObject *aObj)
{
NS_ASSERTION(MAI_IS_ATK_HYPERLINK(aObj), "Invalid MaiAtkHyperlink");
if (!MAI_IS_ATK_HYPERLINK(aObj))
return;
MaiAtkHyperlink *maiAtkHyperlink = MAI_ATK_HYPERLINK(aObj);
maiAtkHyperlink->maiHyperlink = nullptr;
/* call parent finalize function */
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize(aObj);
}
gchar *
getUriCB(AtkHyperlink *aLink, gint aLinkIndex)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink)
return nullptr;
nsAutoCString cautoStr;
if (Accessible* hyperlink = maiLink->GetAccHyperlink()) {
nsCOMPtr<nsIURI> uri = hyperlink->AnchorURIAt(aLinkIndex);
if (!uri)
return nullptr;
nsresult rv = uri->GetSpec(cautoStr);
NS_ENSURE_SUCCESS(rv, nullptr);
return g_strdup(cautoStr.get());
}
bool valid;
maiLink->Proxy()->AnchorURIAt(aLinkIndex, cautoStr, &valid);
if (!valid)
return nullptr;
return g_strdup(cautoStr.get());
}
AtkObject *
getObjectCB(AtkHyperlink *aLink, gint aLinkIndex)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink) {
return nullptr;
}
if (Accessible* hyperlink = maiLink->GetAccHyperlink()) {
Accessible* anchor = hyperlink->AnchorAt(aLinkIndex);
NS_ENSURE_TRUE(anchor, nullptr);
return AccessibleWrap::GetAtkObject(anchor);
}
ProxyAccessible* anchor = maiLink->Proxy()->AnchorAt(aLinkIndex);
return anchor ? GetWrapperFor(anchor) : nullptr;
}
gint
getEndIndexCB(AtkHyperlink *aLink)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink)
return false;
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
return static_cast<gint>(hyperlink->EndOffset());
bool valid = false;
uint32_t endIdx = maiLink->Proxy()->EndOffset(&valid);
return valid ? static_cast<gint>(endIdx) : -1;
}
gint
getStartIndexCB(AtkHyperlink *aLink)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink)
return -1;
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
return static_cast<gint>(hyperlink->StartOffset());
bool valid = false;
uint32_t startIdx = maiLink->Proxy()->StartOffset(&valid);
return valid ? static_cast<gint>(startIdx) : -1;
}
gboolean
isValidCB(AtkHyperlink *aLink)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink)
return false;
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
return static_cast<gboolean>(hyperlink->IsLinkValid());
return static_cast<gboolean>(maiLink->Proxy()->IsLinkValid());
}
gint
getAnchorCountCB(AtkHyperlink *aLink)
{
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
if (!maiLink)
return -1;
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
return static_cast<gint>(hyperlink->AnchorCount());
bool valid = false;
uint32_t anchorCount = maiLink->Proxy()->AnchorCount(&valid);
return valid ? static_cast<gint>(anchorCount) : -1;
}

View file

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __MAI_HYPERLINK_H__
#define __MAI_HYPERLINK_H__
#include "nsMai.h"
#include "Accessible.h"
struct _AtkHyperlink;
typedef struct _AtkHyperlink AtkHyperlink;
namespace mozilla {
namespace a11y {
/*
* MaiHyperlink is a auxiliary class for MaiInterfaceHyperText.
*/
class MaiHyperlink
{
public:
explicit MaiHyperlink(AccessibleOrProxy aHyperLink);
~MaiHyperlink();
public:
AtkHyperlink* GetAtkHyperlink() const { return mMaiAtkHyperlink; }
Accessible* GetAccHyperlink()
{
if (!mHyperlink.IsAccessible())
return nullptr;
Accessible* link = mHyperlink.AsAccessible();
if (!link) {
return nullptr;
}
NS_ASSERTION(link->IsLink(), "Why isn't it a link!");
return link;
}
ProxyAccessible* Proxy() const { return mHyperlink.AsProxy(); }
protected:
AccessibleOrProxy mHyperlink;
AtkHyperlink* mMaiAtkHyperlink;
};
} // namespace a11y
} // namespace mozilla
#endif /* __MAI_HYPERLINK_H__ */

View file

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "nsMai.h"
#include "Role.h"
#include "mozilla/Likely.h"
#include "ProxyAccessible.h"
#include "nsString.h"
using namespace mozilla::a11y;
extern "C" {
static gboolean
doActionCB(AtkAction *aAction, gint aActionIndex)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
if (accWrap) {
return accWrap->DoAction(aActionIndex);
}
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction));
return proxy && proxy->DoAction(aActionIndex);
}
static gint
getActionCountCB(AtkAction *aAction)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
if (accWrap) {
return accWrap->ActionCount();
}
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction));
return proxy ? proxy->ActionCount() : 0;
}
static const gchar*
getActionDescriptionCB(AtkAction *aAction, gint aActionIndex)
{
nsAutoString description;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
if (accWrap) {
accWrap->ActionDescriptionAt(aActionIndex, description);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
proxy->ActionDescriptionAt(aActionIndex, description);
} else {
return nullptr;
}
return AccessibleWrap::ReturnString(description);
}
static const gchar*
getActionNameCB(AtkAction *aAction, gint aActionIndex)
{
nsAutoString autoStr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
if (accWrap) {
accWrap->ActionNameAt(aActionIndex, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
proxy->ActionNameAt(aActionIndex, autoStr);
} else {
return nullptr;
}
return AccessibleWrap::ReturnString(autoStr);
}
static const gchar*
getKeyBindingCB(AtkAction *aAction, gint aActionIndex)
{
nsAutoString keyBindingsStr;
AccessibleWrap* acc = GetAccessibleWrap(ATK_OBJECT(aAction));
if (acc) {
AccessibleWrap::GetKeyBinding(acc, keyBindingsStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
proxy->AtkKeyBinding(keyBindingsStr);
} else {
return nullptr;
}
return AccessibleWrap::ReturnString(keyBindingsStr);
}
}
void
actionInterfaceInitCB(AtkActionIface* aIface)
{
NS_ASSERTION(aIface, "Invalid aIface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->do_action = doActionCB;
aIface->get_n_actions = getActionCountCB;
aIface->get_description = getActionDescriptionCB;
aIface->get_keybinding = getKeyBindingCB;
aIface->get_name = getActionNameCB;
}

View file

@ -0,0 +1,151 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "AccessibleWrap.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsMai.h"
#include "mozilla/Likely.h"
#include "mozilla/a11y/ProxyAccessible.h"
using namespace mozilla::a11y;
extern "C" {
static AtkObject*
refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX, gint aAccY,
AtkCoordType aCoordType)
{
return refAccessibleAtPointHelper(ATK_OBJECT(aComponent),
aAccX, aAccY, aCoordType);
}
static void
getExtentsCB(AtkComponent* aComponent, gint* aX, gint* aY,
gint* aWidth, gint* aHeight, AtkCoordType aCoordType)
{
getExtentsHelper(ATK_OBJECT(aComponent),
aX, aY, aWidth, aHeight, aCoordType);
}
static gboolean
grabFocusCB(AtkComponent* aComponent)
{
AtkObject* atkObject = ATK_OBJECT(aComponent);
AccessibleWrap* accWrap = GetAccessibleWrap(atkObject);
if (accWrap) {
accWrap->TakeFocus();
return TRUE;
}
ProxyAccessible* proxy = GetProxy(atkObject);
if (proxy) {
proxy->TakeFocus();
return TRUE;
}
return FALSE;
}
}
AtkObject*
refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY,
AtkCoordType aCoordType)
{
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (accWrap) {
if (accWrap->IsDefunct() || nsAccUtils::MustPrune(accWrap)) {
return nullptr;
}
// Accessible::ChildAtPoint(x,y) is in screen pixels.
if (aCoordType == ATK_XY_WINDOW) {
nsIntPoint winCoords =
nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode());
aX += winCoords.x;
aY += winCoords.y;
}
Accessible* accAtPoint = accWrap->ChildAtPoint(aX, aY,
Accessible::eDirectChild);
if (!accAtPoint) {
return nullptr;
}
AtkObject* atkObj = AccessibleWrap::GetAtkObject(accAtPoint);
if (atkObj) {
g_object_ref(atkObj);
}
return atkObj;
}
if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
ProxyAccessible* result =
proxy->AccessibleAtPoint(aX, aY, aCoordType == ATK_XY_WINDOW);
AtkObject* atkObj = result ? GetWrapperFor(result) : nullptr;
if (atkObj) {
g_object_ref(atkObj);
}
return atkObj;
}
return nullptr;
}
void
getExtentsHelper(AtkObject* aAtkObj,
gint* aX, gint* aY, gint* aWidth, gint* aHeight,
AtkCoordType aCoordType)
{
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
*aX = *aY = *aWidth = *aHeight = 0;
if (accWrap) {
if (accWrap->IsDefunct()) {
return;
}
nsIntRect screenRect = accWrap->Bounds();
if (screenRect.IsEmpty())
return;
if (aCoordType == ATK_XY_WINDOW) {
nsIntPoint winCoords =
nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode());
screenRect.x -= winCoords.x;
screenRect.y -= winCoords.y;
}
*aX = screenRect.x;
*aY = screenRect.y;
*aWidth = screenRect.width;
*aHeight = screenRect.height;
return;
}
if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
proxy->Extents(aCoordType == ATK_XY_WINDOW, aX, aY, aWidth, aHeight);
}
}
void
componentInterfaceInitCB(AtkComponentIface* aIface)
{
NS_ASSERTION(aIface, "Invalid Interface");
if(MOZ_UNLIKELY(!aIface))
return;
/*
* Use default implementation in atk for contains, get_position,
* and get_size
*/
aIface->ref_accessible_at_point = refAccessibleAtPointCB;
aIface->get_extents = getExtentsCB;
aIface->grab_focus = grabFocusCB;
}

View file

@ -0,0 +1,151 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "AccessibleWrap.h"
#include "DocAccessible.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
static const char* const kDocTypeName = "W3C-doctype";
static const char* const kDocUrlName = "DocURL";
static const char* const kMimeTypeName = "MimeType";
// below functions are vfuncs on an ATK interface so they need to be C call
extern "C" {
static const gchar* getDocumentLocaleCB(AtkDocument* aDocument);
static AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument);
static const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument,
const gchar* aAttrName);
void
documentInterfaceInitCB(AtkDocumentIface *aIface)
{
NS_ASSERTION(aIface, "Invalid Interface");
if(MOZ_UNLIKELY(!aIface))
return;
/*
* We don't support get_document or set_attribute right now.
* get_document_type is deprecated, we return DocType in
* get_document_attribute_value and get_document_attributes instead.
*/
aIface->get_document_attributes = getDocumentAttributesCB;
aIface->get_document_attribute_value = getDocumentAttributeValueCB;
aIface->get_document_locale = getDocumentLocaleCB;
}
const gchar *
getDocumentLocaleCB(AtkDocument *aDocument)
{
nsAutoString locale;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
if (accWrap) {
accWrap->Language(locale);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
proxy->Language(locale);
}
return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale);
}
static inline GSList *
prependToList(GSList *aList, const char *const aName, const nsAutoString &aValue)
{
if (aValue.IsEmpty()) {
return aList;
}
// libspi will free these
AtkAttribute *atkAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
atkAttr->name = g_strdup(aName);
atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
return g_slist_prepend(aList, atkAttr);
}
AtkAttributeSet *
getDocumentAttributesCB(AtkDocument *aDocument)
{
nsAutoString url;
nsAutoString w3cDocType;
nsAutoString mimeType;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
if (accWrap) {
if (!accWrap->IsDoc()) {
return nullptr;
}
DocAccessible* document = accWrap->AsDoc();
document->URL(url);
document->DocType(w3cDocType);
document->MimeType(mimeType);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
proxy->URLDocTypeMimeType(url, w3cDocType, mimeType);
} else {
return nullptr;
}
// according to atkobject.h, AtkAttributeSet is a GSList
GSList* attributes = nullptr;
attributes = prependToList(attributes, kDocUrlName, url);
attributes = prependToList(attributes, kDocTypeName, w3cDocType);
attributes = prependToList(attributes, kMimeTypeName, mimeType);
return attributes;
}
const gchar *
getDocumentAttributeValueCB(AtkDocument *aDocument,
const gchar *aAttrName)
{
ProxyAccessible* proxy = nullptr;
DocAccessible* document = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
if (accWrap) {
if (!accWrap->IsDoc()) {
return nullptr;
}
document = accWrap->AsDoc();
} else {
proxy = GetProxy(ATK_OBJECT(aDocument));
if (!proxy) {
return nullptr;
}
}
nsAutoString attrValue;
if (!strcasecmp(aAttrName, kDocTypeName)) {
if (document) {
document->DocType(attrValue);
} else {
proxy->DocType(attrValue);
}
} else if (!strcasecmp(aAttrName, kDocUrlName)) {
if (document) {
document->URL(attrValue);
} else {
proxy->URL(attrValue);
}
} else if (!strcasecmp(aAttrName, kMimeTypeName)) {
if (document) {
document->MimeType(attrValue);
} else {
proxy->MimeType(attrValue);
}
} else {
return nullptr;
}
return attrValue.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(attrValue);
}
}

View file

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "HyperTextAccessible-inl.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "nsString.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static void
setTextContentsCB(AtkEditableText *aText, const gchar *aString)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
NS_ConvertUTF8toUTF16 strContent(aString);
text->ReplaceText(strContent);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
NS_ConvertUTF8toUTF16 strContent(aString);
proxy->ReplaceText(strContent);
}
}
static void
insertTextCB(AtkEditableText *aText,
const gchar *aString, gint aLength, gint *aPosition)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
NS_ConvertUTF8toUTF16 strContent(aString);
text->InsertText(strContent, *aPosition);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
NS_ConvertUTF8toUTF16 strContent(aString);
proxy->InsertText(strContent, *aPosition);
}
}
static void
copyTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
text->CopyText(aStartPos, aEndPos);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->CopyText(aStartPos, aEndPos);
}
}
static void
cutTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
text->CutText(aStartPos, aEndPos);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->CutText(aStartPos, aEndPos);
}
}
static void
deleteTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
text->DeleteText(aStartPos, aEndPos);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->DeleteText(aStartPos, aEndPos);
}
}
static void
pasteTextCB(AtkEditableText *aText, gint aPosition)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
text->PasteText(aPosition);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->PasteText(aPosition);
}
}
}
void
editableTextInterfaceInitCB(AtkEditableTextIface* aIface)
{
NS_ASSERTION(aIface, "Invalid aIface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->set_text_contents = setTextContentsCB;
aIface->insert_text = insertTextCB;
aIface->copy_text = copyTextCB;
aIface->cut_text = cutTextCB;
aIface->delete_text = deleteTextCB;
aIface->paste_text = pasteTextCB;
}

View file

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "nsMaiHyperlink.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static AtkHyperlink*
getHyperlinkCB(AtkHyperlinkImpl* aImpl)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImpl));
if (!accWrap && !GetProxy(ATK_OBJECT(aImpl)))
return nullptr;
if (accWrap)
NS_ASSERTION(accWrap->IsLink(), "why isn't it a link!");
return MAI_ATK_OBJECT(aImpl)->GetAtkHyperlink();
}
}
void
hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface *aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_hyperlink = getHyperlinkCB;
}

View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "HyperTextAccessible.h"
#include "nsMai.h"
#include "nsMaiHyperlink.h"
#include "ProxyAccessible.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static AtkHyperlink*
getLinkCB(AtkHypertext *aText, gint aLinkIndex)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
AtkObject* atkHyperLink = nullptr;
if (accWrap) {
HyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, nullptr);
Accessible* hyperLink = hyperText->LinkAt(aLinkIndex);
if (!hyperLink || !hyperLink->IsLink()) {
return nullptr;
}
atkHyperLink = AccessibleWrap::GetAtkObject(hyperLink);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
ProxyAccessible* proxyLink = proxy->LinkAt(aLinkIndex);
if (!proxyLink)
return nullptr;
atkHyperLink = GetWrapperFor(proxyLink);
}
NS_ENSURE_TRUE(IS_MAI_OBJECT(atkHyperLink), nullptr);
return MAI_ATK_OBJECT(atkHyperLink)->GetAtkHyperlink();
}
static gint
getLinkCountCB(AtkHypertext *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, -1);
return hyperText->LinkCount();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->LinkCount();
}
return -1;
}
static gint
getLinkIndexCB(AtkHypertext *aText, gint aCharIndex)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* hyperText = accWrap->AsHyperText();
NS_ENSURE_TRUE(hyperText, -1);
return hyperText->LinkIndexAtOffset(aCharIndex);
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->LinkIndexAtOffset(aCharIndex);
}
return -1;
}
}
void
hypertextInterfaceInitCB(AtkHypertextIface* aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_link = getLinkCB;
aIface->get_n_links = getLinkCountCB;
aIface->get_link_index = getLinkIndexCB;
}

View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "AccessibleWrap.h"
#include "ImageAccessible.h"
#include "mozilla/Likely.h"
#include "nsMai.h"
#include "nsIAccessibleTypes.h"
#include "nsIURI.h"
#include "ProxyAccessible.h"
using namespace mozilla;
using namespace mozilla::a11y;
extern "C" {
const gchar* getDescriptionCB(AtkObject* aAtkObj);
static void
getImagePositionCB(AtkImage* aImage, gint* aAccX, gint* aAccY,
AtkCoordType aCoordType)
{
nsIntPoint pos;
uint32_t geckoCoordType = (aCoordType == ATK_XY_WINDOW) ?
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE :
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
if (accWrap && accWrap->IsImage()) {
ImageAccessible* image = accWrap->AsImage();
pos = image->Position(geckoCoordType);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
pos = proxy->ImagePosition(geckoCoordType);
}
*aAccX = pos.x;
*aAccY = pos.y;
}
static const gchar*
getImageDescriptionCB(AtkImage* aImage)
{
return getDescriptionCB(ATK_OBJECT(aImage));
}
static void
getImageSizeCB(AtkImage* aImage, gint* aAccWidth, gint* aAccHeight)
{
nsIntSize size;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
if (accWrap && accWrap->IsImage()) {
size = accWrap->AsImage()->Size();
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
size = proxy->ImageSize();
}
*aAccWidth = size.width;
*aAccHeight = size.height;
}
} // extern "C"
void
imageInterfaceInitCB(AtkImageIface* aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_image_position = getImagePositionCB;
aIface->get_image_description = getImageDescriptionCB;
aIface->get_image_size = getImageSizeCB;
}

View file

@ -0,0 +1,152 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "AccessibleWrap.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "mozilla/Likely.h"
#include <atk/atk.h>
using namespace mozilla::a11y;
extern "C" {
static gboolean
addSelectionCB(AtkSelection *aSelection, gint i)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->AddItemToSelection(i);
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->AddItemToSelection(i);
}
return FALSE;
}
static gboolean
clearSelectionCB(AtkSelection *aSelection)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->UnselectAll();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->UnselectAll();
}
return FALSE;
}
static AtkObject*
refSelectionCB(AtkSelection *aSelection, gint i)
{
AtkObject* atkObj = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
Accessible* selectedItem = accWrap->GetSelectedItem(i);
if (!selectedItem) {
return nullptr;
}
atkObj = AccessibleWrap::GetAtkObject(selectedItem);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
ProxyAccessible* selectedItem = proxy->GetSelectedItem(i);
if (selectedItem) {
atkObj = GetWrapperFor(selectedItem);
}
}
if (atkObj) {
g_object_ref(atkObj);
}
return atkObj;
}
static gint
getSelectionCountCB(AtkSelection *aSelection)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->SelectedItemCount();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->SelectedItemCount();
}
return -1;
}
static gboolean
isChildSelectedCB(AtkSelection *aSelection, gint i)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->IsItemSelected(i);
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->IsItemSelected(i);
}
return FALSE;
}
static gboolean
removeSelectionCB(AtkSelection *aSelection, gint i)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->RemoveItemFromSelection(i);
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->RemoveItemFromSelection(i);
}
return FALSE;
}
static gboolean
selectAllSelectionCB(AtkSelection *aSelection)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
if (accWrap && accWrap->IsSelect()) {
return accWrap->SelectAll();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
return proxy->SelectAll();
}
return FALSE;
}
}
void
selectionInterfaceInitCB(AtkSelectionIface* aIface)
{
NS_ASSERTION(aIface, "Invalid aIface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->add_selection = addSelectionCB;
aIface->clear_selection = clearSelectionCB;
aIface->ref_selection = refSelectionCB;
aIface->get_selection_count = getSelectionCountCB;
aIface->is_child_selected = isChildSelectedCB;
aIface->remove_selection = removeSelectionCB;
aIface->select_all_selection = selectAllSelectionCB;
}

View file

@ -0,0 +1,391 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "AccessibleWrap.h"
#include "nsAccUtils.h"
#include "TableAccessible.h"
#include "TableCellAccessible.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "nsArrayUtils.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static AtkObject*
refAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx)
{
if (aRowIdx < 0 || aColIdx < 0) {
return nullptr;
}
AtkObject* cellAtkObj = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
Accessible* cell = accWrap->AsTable()->CellAt(aRowIdx, aColIdx);
if (!cell) {
return nullptr;
}
cellAtkObj = AccessibleWrap::GetAtkObject(cell);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
ProxyAccessible* cell = proxy->TableCellAt(aRowIdx, aColIdx);
if (!cell) {
return nullptr;
}
cellAtkObj = GetWrapperFor(cell);
}
if (cellAtkObj) {
g_object_ref(cellAtkObj);
}
return cellAtkObj;
}
static gint
getIndexAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx)
{
if (aRowIdx < 0 || aColIdx < 0) {
return -1;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->CellIndexAt(aRowIdx, aColIdx));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableCellIndexAt(aRowIdx, aColIdx));
}
return -1;
}
static gint
getColumnAtIndexCB(AtkTable *aTable, gint aIdx)
{
if (aIdx < 0) {
return -1;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->ColIndexAt(aIdx));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableColumnIndexAt(aIdx));
}
return -1;
}
static gint
getRowAtIndexCB(AtkTable *aTable, gint aIdx)
{
if (aIdx < 0) {
return -1;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->RowIndexAt(aIdx));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableRowIndexAt(aIdx));
}
return -1;
}
static gint
getColumnCountCB(AtkTable *aTable)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->ColCount());
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableColumnCount());
}
return -1;
}
static gint
getRowCountCB(AtkTable *aTable)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->RowCount());
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableRowCount());
}
return -1;
}
static gint
getColumnExtentAtCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
{
if (aRowIdx < 0 || aColIdx < 0) {
return -1;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->ColExtentAt(aRowIdx, aColIdx));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableColumnExtentAt(aRowIdx, aColIdx));
}
return -1;
}
static gint
getRowExtentAtCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gint>(accWrap->AsTable()->RowExtentAt(aRowIdx, aColIdx));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gint>(proxy->TableRowExtentAt(aRowIdx, aColIdx));
}
return -1;
}
static AtkObject*
getCaptionCB(AtkTable* aTable)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
Accessible* caption = accWrap->AsTable()->Caption();
return caption ? AccessibleWrap::GetAtkObject(caption) : nullptr;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
ProxyAccessible* caption = proxy->TableCaption();
return caption ? GetWrapperFor(caption) : nullptr;
}
return nullptr;
}
static const gchar*
getColumnDescriptionCB(AtkTable *aTable, gint aColumn)
{
nsAutoString autoStr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
accWrap->AsTable()->ColDescription(aColumn, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
proxy->TableColumnDescription(aColumn, autoStr);
} else {
return nullptr;
}
return AccessibleWrap::ReturnString(autoStr);
}
static AtkObject*
getColumnHeaderCB(AtkTable *aTable, gint aColIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
Accessible* header =
AccessibleWrap::GetColumnHeader(accWrap->AsTable(), aColIdx);
return header ? AccessibleWrap::GetAtkObject(header) : nullptr;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
ProxyAccessible* header = proxy->AtkTableColumnHeader(aColIdx);
return header ? GetWrapperFor(header) : nullptr;
}
return nullptr;
}
static const gchar*
getRowDescriptionCB(AtkTable *aTable, gint aRow)
{
nsAutoString autoStr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
accWrap->AsTable()->RowDescription(aRow, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
proxy->TableRowDescription(aRow, autoStr);
} else {
return nullptr;
}
return AccessibleWrap::ReturnString(autoStr);
}
static AtkObject*
getRowHeaderCB(AtkTable *aTable, gint aRowIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
Accessible* header =
AccessibleWrap::GetRowHeader(accWrap->AsTable(), aRowIdx);
return header ? AccessibleWrap::GetAtkObject(header) : nullptr;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
ProxyAccessible* header = proxy->AtkTableRowHeader(aRowIdx);
return header ? GetWrapperFor(header) : nullptr;
}
return nullptr;
}
static AtkObject*
getSummaryCB(AtkTable *aTable)
{
// Neither html:table nor xul:tree nor ARIA grid/tree have an ability to
// link an accessible object to specify a summary. There is closes method
// in TableAccessible::summary to get a summary as a string which is not
// mapped directly to ATK.
return nullptr;
}
static gint
getSelectedColumnsCB(AtkTable *aTable, gint** aSelected)
{
*aSelected = nullptr;
AutoTArray<uint32_t, 10> cols;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
accWrap->AsTable()->SelectedColIndices(&cols);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
proxy->TableSelectedColumnIndices(&cols);
} else {
return 0;
}
if (cols.IsEmpty())
return 0;
gint* atkColumns = g_new(gint, cols.Length());
if (!atkColumns) {
NS_WARNING("OUT OF MEMORY");
return 0;
}
memcpy(atkColumns, cols.Elements(), cols.Length() * sizeof(uint32_t));
*aSelected = atkColumns;
return cols.Length();
}
static gint
getSelectedRowsCB(AtkTable *aTable, gint **aSelected)
{
AutoTArray<uint32_t, 10> rows;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
accWrap->AsTable()->SelectedRowIndices(&rows);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
proxy->TableSelectedRowIndices(&rows);
} else {
return 0;
}
gint* atkRows = g_new(gint, rows.Length());
if (!atkRows) {
NS_WARNING("OUT OF MEMORY");
return 0;
}
memcpy(atkRows, rows.Elements(), rows.Length() * sizeof(uint32_t));
*aSelected = atkRows;
return rows.Length();
}
static gboolean
isColumnSelectedCB(AtkTable *aTable, gint aColIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->IsColSelected(aColIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableColumnSelected(aColIdx));
}
return FALSE;
}
static gboolean
isRowSelectedCB(AtkTable *aTable, gint aRowIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->IsRowSelected(aRowIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableRowSelected(aRowIdx));
}
return FALSE;
}
static gboolean
isCellSelectedCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
if (accWrap) {
return static_cast<gboolean>(accWrap->AsTable()->
IsCellSelected(aRowIdx, aColIdx));
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
return static_cast<gboolean>(proxy->TableCellSelected(aRowIdx, aColIdx));
}
return FALSE;
}
}
void
tableInterfaceInitCB(AtkTableIface* aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->ref_at = refAtCB;
aIface->get_index_at = getIndexAtCB;
aIface->get_column_at_index = getColumnAtIndexCB;
aIface->get_row_at_index = getRowAtIndexCB;
aIface->get_n_columns = getColumnCountCB;
aIface->get_n_rows = getRowCountCB;
aIface->get_column_extent_at = getColumnExtentAtCB;
aIface->get_row_extent_at = getRowExtentAtCB;
aIface->get_caption = getCaptionCB;
aIface->get_column_description = getColumnDescriptionCB;
aIface->get_column_header = getColumnHeaderCB;
aIface->get_row_description = getRowDescriptionCB;
aIface->get_row_header = getRowHeaderCB;
aIface->get_summary = getSummaryCB;
aIface->get_selected_columns = getSelectedColumnsCB;
aIface->get_selected_rows = getSelectedRowsCB;
aIface->is_column_selected = isColumnSelectedCB;
aIface->is_row_selected = isRowSelectedCB;
aIface->is_selected = isCellSelectedCB;
}

View file

@ -0,0 +1,216 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "AccessibleWrap.h"
#include "nsAccUtils.h"
#include "TableAccessible.h"
#include "TableCellAccessible.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "nsArrayUtils.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static gint
GetColumnSpanCB(AtkTableCell* aCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
if (accWrap) {
return accWrap->AsTableCell()->ColExtent();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
return proxy->ColExtent();
}
return 0;
}
static gboolean
GetRowSpanCB(AtkTableCell* aCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
if (accWrap) {
return accWrap->AsTableCell()->RowExtent();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
return proxy->RowExtent();
}
return 0;
}
static gboolean
GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
TableCellAccessible* cell = accWrap->AsTableCell();
*aRow = cell->RowIdx();
*aCol = cell->ColIdx();
return true;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
uint32_t rowIdx = 0, colIdx = 0;
proxy->GetPosition(&rowIdx, &colIdx);
*aCol = colIdx;
*aRow = rowIdx;
return true;
}
return false;
}
static gboolean
GetColumnRowSpanCB(AtkTableCell* aCell, gint* aCol, gint* aRow,
gint* aColExtent, gint* aRowExtent) {
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
TableCellAccessible* cellAcc = accWrap->AsTableCell();
*aCol = cellAcc->ColIdx();
*aRow = cellAcc->RowIdx();
*aColExtent = cellAcc->ColExtent();
*aRowExtent = cellAcc->ColExtent();
return true;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
uint32_t colIdx = 0, rowIdx = 0, colExtent = 0, rowExtent = 0;
proxy->GetColRowExtents(&colIdx, &rowIdx, &colExtent, &rowExtent);
*aCol = colIdx;
*aRow = rowIdx;
*aColExtent = colExtent;
*aRowExtent = rowExtent;
return true;
}
return false;
}
static AtkObject*
GetTableCB(AtkTableCell* aTableCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTableCell));
if (accWrap) {
TableAccessible* table = accWrap->AsTableCell()->Table();
if (!table) {
return nullptr;
}
Accessible* tableAcc = table->AsAccessible();
return tableAcc ? AccessibleWrap::GetAtkObject(tableAcc) : nullptr;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTableCell))) {
ProxyAccessible* table = proxy->TableOfACell();
return table ? GetWrapperFor(table) : nullptr;
}
return nullptr;
}
static GPtrArray*
GetColumnHeaderCellsCB(AtkTableCell* aCell)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
AutoTArray<Accessible*, 10> headers;
accWrap->AsTableCell()->ColHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (Accessible* header: headers) {
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
AutoTArray<ProxyAccessible*, 10> headers;
proxy->ColHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (ProxyAccessible* header: headers) {
AtkObject* atkHeader = GetWrapperFor(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
return nullptr;
}
static GPtrArray*
GetRowHeaderCellsCB(AtkTableCell* aCell)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
AutoTArray<Accessible*, 10> headers;
accWrap->AsTableCell()->RowHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (Accessible* header: headers) {
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
AutoTArray<ProxyAccessible*, 10> headers;
proxy->RowHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (ProxyAccessible* header: headers) {
AtkObject* atkHeader = GetWrapperFor(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
return nullptr;
}
}
void
tableCellInterfaceInitCB(AtkTableCellIface* aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_column_span = GetColumnSpanCB;
aIface->get_column_header_cells = GetColumnHeaderCellsCB;
aIface->get_position = GetPositionCB;
aIface->get_row_span = GetRowSpanCB;
aIface->get_row_header_cells = GetRowHeaderCellsCB;
aIface->get_row_column_span = GetColumnRowSpanCB;
aIface->get_table = GetTableCB;
}

View file

@ -0,0 +1,629 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "mozilla/a11y/PDocAccessible.h"
#include "Accessible-inl.h"
#include "HyperTextAccessible-inl.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "nsIAccessibleTypes.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
#include "mozilla/Likely.h"
using namespace mozilla;
using namespace mozilla::a11y;
static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
void
ConvertTextAttributeToAtkAttribute(const nsACString& aName,
const nsAString& aValue,
AtkAttributeSet** aAttributeSet)
{
// Handle attributes where atk has its own name.
const char* atkName = nullptr;
nsAutoString atkValue;
if (aName.EqualsLiteral("color")) {
// The format of the atk attribute is r,g,b and the gecko one is
// rgb(r, g, b).
atkValue = Substring(aValue, 4, aValue.Length() - 5);
atkValue.StripWhitespace();
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR];
} else if (aName.EqualsLiteral("background-color")) {
// The format of the atk attribute is r,g,b and the gecko one is
// rgb(r, g, b).
atkValue = Substring(aValue, 4, aValue.Length() - 5);
atkValue.StripWhitespace();
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR];
} else if (aName.EqualsLiteral("font-family")) {
atkValue = aValue;
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME];
} else if (aName.EqualsLiteral("font-size")) {
// ATK wants the number of pixels without px at the end.
atkValue = StringHead(aValue, aValue.Length() - 2);
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE];
} else if (aName.EqualsLiteral("font-weight")) {
atkValue = aValue;
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT];
} else if (aName.EqualsLiteral("invalid")) {
atkValue = aValue;
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID];
}
if (atkName) {
AtkAttribute* objAttr =
static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
objAttr->name = g_strdup(atkName);
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get());
*aAttributeSet = g_slist_prepend(*aAttributeSet, objAttr);
}
}
static AtkAttributeSet*
ConvertToAtkTextAttributeSet(nsTArray<Attribute>& aAttributes)
{
AtkAttributeSet* objAttributeSet = nullptr;
for (size_t i = 0; i < aAttributes.Length(); ++i) {
AtkAttribute* objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
objAttr->name = g_strdup(aAttributes[i].Name().get());
objAttr->value =
g_strdup(NS_ConvertUTF16toUTF8(aAttributes[i].Value()).get());
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
ConvertTextAttributeToAtkAttribute(aAttributes[i].Name(),
aAttributes[i].Value(),
&objAttributeSet);
}
return objAttributeSet;
}
static AtkAttributeSet*
ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes)
{
if (!aAttributes)
return nullptr;
AtkAttributeSet* objAttributeSet = nullptr;
nsCOMPtr<nsISimpleEnumerator> propEnum;
nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
NS_ENSURE_SUCCESS(rv, nullptr);
bool hasMore = false;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> sup;
rv = propEnum->GetNext(getter_AddRefs(sup));
NS_ENSURE_SUCCESS(rv, objAttributeSet);
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
NS_ENSURE_TRUE(propElem, objAttributeSet);
nsAutoCString name;
rv = propElem->GetKey(name);
NS_ENSURE_SUCCESS(rv, objAttributeSet);
nsAutoString value;
rv = propElem->GetValue(value);
NS_ENSURE_SUCCESS(rv, objAttributeSet);
AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
objAttr->name = g_strdup(name.get());
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
ConvertTextAttributeToAtkAttribute(name, value, &objAttributeSet);
}
// libatk-adaptor will free it
return objAttributeSet;
}
static void
ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString)
{
// convert each char to "*" when it's "password text"
if (accWrap->NativeRole() == roles::PASSWORD_TEXT) {
for (uint32_t i = 0; i < aString.Length(); i++)
aString.Replace(i, 1, NS_LITERAL_STRING("*"));
}
}
extern "C" {
static gchar*
getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
nsAutoString autoStr;
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole())
return nullptr;
text->TextSubstring(aStartOffset, aEndOffset, autoStr);
ConvertTexttoAsterisks(accWrap, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->TextSubstring(aStartOffset, aEndOffset, autoStr);
}
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
//copy and return, libspi will free it.
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
}
static gchar*
getTextAfterOffsetCB(AtkText *aText, gint aOffset,
AtkTextBoundary aBoundaryType,
gint *aStartOffset, gint *aEndOffset)
{
nsAutoString autoStr;
int32_t startOffset = 0, endOffset = 0;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole())
return nullptr;
text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
ConvertTexttoAsterisks(accWrap, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->GetTextAfterOffset(aOffset, aBoundaryType, autoStr, &startOffset,
&endOffset);
}
*aStartOffset = startOffset;
*aEndOffset = endOffset;
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
}
static gchar*
getTextAtOffsetCB(AtkText *aText, gint aOffset,
AtkTextBoundary aBoundaryType,
gint *aStartOffset, gint *aEndOffset)
{
nsAutoString autoStr;
int32_t startOffset = 0, endOffset = 0;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole())
return nullptr;
text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
ConvertTexttoAsterisks(accWrap, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->GetTextAtOffset(aOffset, aBoundaryType, autoStr, &startOffset,
&endOffset);
}
*aStartOffset = startOffset;
*aEndOffset = endOffset;
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
}
static gunichar
getCharacterAtOffsetCB(AtkText* aText, gint aOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return 0;
}
// char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib.
return static_cast<gunichar>(text->CharAt(aOffset));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return static_cast<gunichar>(proxy->CharAt(aOffset));
}
return 0;
}
static gchar*
getTextBeforeOffsetCB(AtkText *aText, gint aOffset,
AtkTextBoundary aBoundaryType,
gint *aStartOffset, gint *aEndOffset)
{
nsAutoString autoStr;
int32_t startOffset = 0, endOffset = 0;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole())
return nullptr;
text->TextBeforeOffset(aOffset, aBoundaryType,
&startOffset, &endOffset, autoStr);
ConvertTexttoAsterisks(accWrap, autoStr);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->GetTextBeforeOffset(aOffset, aBoundaryType, autoStr, &startOffset,
&endOffset);
}
*aStartOffset = startOffset;
*aEndOffset = endOffset;
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
}
static gint
getCaretOffsetCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return 0;
}
return static_cast<gint>(text->CaretOffset());
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return static_cast<gint>(proxy->CaretOffset());
}
return 0;
}
static AtkAttributeSet*
getRunAttributesCB(AtkText *aText, gint aOffset,
gint *aStartOffset,
gint *aEndOffset)
{
*aStartOffset = -1;
*aEndOffset = -1;
int32_t startOffset = 0, endOffset = 0;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return nullptr;
}
nsCOMPtr<nsIPersistentProperties> attributes =
text->TextAttributes(false, aOffset, &startOffset, &endOffset);
*aStartOffset = startOffset;
*aEndOffset = endOffset;
return ConvertToAtkTextAttributeSet(attributes);
}
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText));
if (!proxy) {
return nullptr;
}
AutoTArray<Attribute, 10> attrs;
proxy->TextAttributes(false, aOffset, &attrs, &startOffset, &endOffset);
*aStartOffset = startOffset;
*aEndOffset = endOffset;
return ConvertToAtkTextAttributeSet(attrs);
}
static AtkAttributeSet*
getDefaultAttributesCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return nullptr;
}
nsCOMPtr<nsIPersistentProperties> attributes = text->DefaultTextAttributes();
return ConvertToAtkTextAttributeSet(attributes);
}
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText));
if (!proxy) {
return nullptr;
}
AutoTArray<Attribute, 10> attrs;
proxy->DefaultTextAttributes(&attrs);
return ConvertToAtkTextAttributeSet(attrs);
}
static void
getCharacterExtentsCB(AtkText *aText, gint aOffset,
gint *aX, gint *aY,
gint *aWidth, gint *aHeight,
AtkCoordType aCoords)
{
if(!aX || !aY || !aWidth || !aHeight) {
return;
}
nsIntRect rect;
uint32_t geckoCoordType;
if (aCoords == ATK_XY_SCREEN) {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
} else {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
rect = text->CharBounds(aOffset, geckoCoordType);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
rect = proxy->CharBounds(aOffset, geckoCoordType);
} else {
return;
}
*aX = rect.x;
*aY = rect.y;
*aWidth = rect.width;
*aHeight = rect.height;
}
static void
getRangeExtentsCB(AtkText *aText, gint aStartOffset, gint aEndOffset,
AtkCoordType aCoords, AtkTextRectangle *aRect)
{
if (!aRect) {
return;
}
nsIntRect rect;
uint32_t geckoCoordType;
if (aCoords == ATK_XY_SCREEN) {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
} else {
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
}
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if(accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return;
}
rect = text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
rect = proxy->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
} else {
return;
}
aRect->x = rect.x;
aRect->y = rect.y;
aRect->width = rect.width;
aRect->height = rect.height;
}
static gint
getCharacterCountCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* textAcc = accWrap->AsHyperText();
return
textAcc->IsDefunct() ? 0 : static_cast<gint>(textAcc->CharacterCount());
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->CharacterCount();
}
return 0;
}
static gint
getOffsetAtPointCB(AtkText *aText,
gint aX, gint aY,
AtkCoordType aCoords)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return -1;
}
return static_cast<gint>(
text->OffsetAtPoint(aX, aY,
(aCoords == ATK_XY_SCREEN ?
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return static_cast<gint>(
proxy->OffsetAtPoint(aX, aY,
(aCoords == ATK_XY_SCREEN ?
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
}
return -1;
}
static gint
getTextSelectionCountCB(AtkText *aText)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return 0;
}
return text->SelectionCount();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->SelectionCount();
}
return 0;
}
static gchar*
getTextSelectionCB(AtkText *aText, gint aSelectionNum,
gint *aStartOffset, gint *aEndOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
int32_t startOffset = 0, endOffset = 0;
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return nullptr;
}
text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
*aStartOffset = startOffset;
*aEndOffset = endOffset;
return getTextCB(aText, *aStartOffset, *aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
nsString data;
proxy->SelectionBoundsAt(aSelectionNum, data, &startOffset, &endOffset);
*aStartOffset = startOffset;
*aEndOffset = endOffset;
NS_ConvertUTF16toUTF8 dataAsUTF8(data);
return (dataAsUTF8.get()) ? g_strdup(dataAsUTF8.get()) : nullptr;
}
return nullptr;
}
// set methods
static gboolean
addTextSelectionCB(AtkText *aText,
gint aStartOffset,
gint aEndOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return FALSE;
}
return text->AddToSelection(aStartOffset, aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->AddToSelection(aStartOffset, aEndOffset);
}
return FALSE;
}
static gboolean
removeTextSelectionCB(AtkText *aText,
gint aSelectionNum)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return FALSE;
}
return text->RemoveFromSelection(aSelectionNum);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->RemoveFromSelection(aSelectionNum);
}
return FALSE;
}
static gboolean
setTextSelectionCB(AtkText *aText, gint aSelectionNum,
gint aStartOffset, gint aEndOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole()) {
return FALSE;
}
return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
return proxy->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
}
return FALSE;
}
static gboolean
setCaretOffsetCB(AtkText *aText, gint aOffset)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
if (accWrap) {
HyperTextAccessible* text = accWrap->AsHyperText();
if (!text || !text->IsTextRole() || !text->IsValidOffset(aOffset)) {
return FALSE;
}
text->SetCaretOffset(aOffset);
return TRUE;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
proxy->SetCaretOffset(aOffset);
return TRUE;
}
return FALSE;
}
}
void
textInterfaceInitCB(AtkTextIface* aIface)
{
NS_ASSERTION(aIface, "Invalid aIface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_text = getTextCB;
aIface->get_text_after_offset = getTextAfterOffsetCB;
aIface->get_text_at_offset = getTextAtOffsetCB;
aIface->get_character_at_offset = getCharacterAtOffsetCB;
aIface->get_text_before_offset = getTextBeforeOffsetCB;
aIface->get_caret_offset = getCaretOffsetCB;
aIface->get_run_attributes = getRunAttributesCB;
aIface->get_default_attributes = getDefaultAttributesCB;
aIface->get_character_extents = getCharacterExtentsCB;
aIface->get_range_extents = getRangeExtentsCB;
aIface->get_character_count = getCharacterCountCB;
aIface->get_offset_at_point = getOffsetAtPointCB;
aIface->get_n_selections = getTextSelectionCountCB;
aIface->get_selection = getTextSelectionCB;
// set methods
aIface->add_selection = addTextSelectionCB;
aIface->remove_selection = removeTextSelectionCB;
aIface->set_selection = setTextSelectionCB;
aIface->set_caret_offset = setCaretOffsetCB;
// Cache the string values of the atk text attribute names.
for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++)
sAtkTextAttrNames[i] =
atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
}

View file

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "AccessibleWrap.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
using namespace mozilla;
using namespace mozilla::a11y;
extern "C" {
static void
getCurrentValueCB(AtkValue *obj, GValue *value)
{
ProxyAccessible* proxy = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
if (!accWrap) {
proxy = GetProxy(ATK_OBJECT(obj));
if (!proxy) {
return;
}
}
memset (value, 0, sizeof (GValue));
double accValue = accWrap ? accWrap->CurValue() : proxy->CurValue();
if (IsNaN(accValue))
return;
g_value_init (value, G_TYPE_DOUBLE);
g_value_set_double (value, accValue);
}
static void
getMaximumValueCB(AtkValue *obj, GValue *value)
{
ProxyAccessible* proxy = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
if (!accWrap) {
proxy = GetProxy(ATK_OBJECT(obj));
if (!proxy) {
return;
}
}
memset(value, 0, sizeof (GValue));
double accValue = accWrap ? accWrap->MaxValue() : proxy->MaxValue();
if (IsNaN(accValue))
return;
g_value_init(value, G_TYPE_DOUBLE);
g_value_set_double(value, accValue);
}
static void
getMinimumValueCB(AtkValue *obj, GValue *value)
{
ProxyAccessible* proxy = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
if (!accWrap) {
proxy = GetProxy(ATK_OBJECT(obj));
if (!proxy) {
return;
}
}
memset(value, 0, sizeof (GValue));
double accValue = accWrap ? accWrap->MinValue() : proxy->MinValue();
if (IsNaN(accValue))
return;
g_value_init(value, G_TYPE_DOUBLE);
g_value_set_double(value, accValue);
}
static void
getMinimumIncrementCB(AtkValue *obj, GValue *minimumIncrement)
{
ProxyAccessible* proxy = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
if (!accWrap) {
proxy = GetProxy(ATK_OBJECT(obj));
if (!proxy) {
return;
}
}
memset(minimumIncrement, 0, sizeof (GValue));
double accValue = accWrap ? accWrap->Step() : proxy->Step();
if (IsNaN(accValue))
accValue = 0; // zero if the minimum increment is undefined
g_value_init(minimumIncrement, G_TYPE_DOUBLE);
g_value_set_double(minimumIncrement, accValue);
}
static gboolean
setCurrentValueCB(AtkValue *obj, const GValue *value)
{
ProxyAccessible* proxy = nullptr;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
if (!accWrap) {
proxy = GetProxy(ATK_OBJECT(obj));
if (!proxy) {
return FALSE;
}
}
double accValue =g_value_get_double(value);
return accWrap ? accWrap->SetCurValue(accValue) : proxy->SetCurValue(accValue);
}
}
void
valueInterfaceInitCB(AtkValueIface* aIface)
{
NS_ASSERTION(aIface, "Invalid aIface");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_current_value = getCurrentValueCB;
aIface->get_maximum_value = getMaximumValueCB;
aIface->get_minimum_value = getMinimumValueCB;
aIface->get_minimum_increment = getMinimumIncrementCB;
aIface->set_current_value = setCurrentValueCB;
}

117
accessible/atk/nsStateMap.h Normal file
View file

@ -0,0 +1,117 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <atk/atk.h>
#include "AccessibleWrap.h"
/******************************************************************************
The following accessible states aren't translated, just ignored:
STATE_READONLY: Supported indirectly via EXT_STATE_EDITABLE
STATE_HOTTRACKED: No ATK equivalent. No known use case.
The nsIAccessible state is not currently supported.
STATE_FLOATING: No ATK equivalent. No known use case.
The nsIAccessible state is not currently supported.
STATE_MOVEABLE: No ATK equivalent. No known use case.
The nsIAccessible state is not currently supported.
STATE_SELFVOICING: No ATK equivalent -- the object has self-TTS.
The nsIAccessible state is not currently supported.
STATE_LINKED: The object is formatted as a hyperlink. Supported via ATK_ROLE_LINK.
STATE_EXTSELECTABLE: Indicates that an object extends its selection.
This is supported via STATE_MULTISELECTABLE.
STATE_PROTECTED: The object is a password-protected edit control.
Supported via ATK_ROLE_PASSWORD_TEXT
STATE_HASPOPUP: Object displays a pop-up menu or window when invoked.
No ATK equivalent. The accessible state is not
currently supported.
STATE_PINNED: The object is pinned, usually indicating it is fixed in
place and has permanence. No ATK equivalent. The
accessible state is not currently supported.
The following ATK states are not supported:
ATK_STATE_ARMED: No clear use case, used briefly when button is activated
ATK_STATE_HAS_TOOLTIP: No clear use case, no IA2 equivalent
ATK_STATE_ICONIFIED: Mozilla does not have elements which are collapsable into icons
ATK_STATE_TRUNCATED: No clear use case. Indicates that an object's onscreen content is truncated,
e.g. a text value in a spreadsheet cell. No IA2 state.
******************************************************************************/
enum EStateMapEntryType {
kMapDirectly,
kMapOpposite, // For example, UNAVAILABLE is the opposite of ENABLED
kNoStateChange, // Don't fire state change event
kNoSuchState
};
const AtkStateType kNone = ATK_STATE_INVALID;
struct AtkStateMap {
AtkStateType atkState;
EStateMapEntryType stateMapEntryType;
static int32_t GetStateIndexFor(uint64_t aState)
{
int32_t stateIndex = -1;
while (aState > 0) {
++ stateIndex;
aState >>= 1;
}
return stateIndex; // Returns -1 if not mapped
}
};
// Map array from cross platform states to ATK states
static const AtkStateMap gAtkStateMap[] = { // Cross Platform States
{ kNone, kMapOpposite }, // states::UNAVAILABLE = 1 << 0
{ ATK_STATE_SELECTED, kMapDirectly }, // states::SELECTED = 1 << 1
{ ATK_STATE_FOCUSED, kMapDirectly }, // states::FOCUSED = 1 << 2
{ ATK_STATE_PRESSED, kMapDirectly }, // states::PRESSED = 1 << 3
{ ATK_STATE_CHECKED, kMapDirectly }, // states::CHECKED = 1 << 4
{ ATK_STATE_INDETERMINATE, kMapDirectly }, // states::MIXED = 1 << 5
{ kNone, kMapDirectly }, // states::READONLY = 1 << 6
{ kNone, kMapDirectly }, // states::HOTTRACKED = 1 << 7
{ ATK_STATE_DEFAULT, kMapDirectly }, // states::DEFAULT = 1 << 8
{ ATK_STATE_EXPANDED, kMapDirectly }, // states::EXPANDED = 1 << 9
{ kNone, kNoStateChange }, // states::COLLAPSED = 1 << 10
{ ATK_STATE_BUSY, kMapDirectly }, // states::BUSY = 1 << 11
{ kNone, kMapDirectly }, // states::FLOATING = 1 << 12
{ ATK_STATE_CHECKABLE, kMapDirectly }, // states::CHECKABLE = 1 << 13
{ ATK_STATE_ANIMATED, kMapDirectly }, // states::ANIMATED = 1 << 14
{ ATK_STATE_VISIBLE, kMapOpposite }, // states::INVISIBLE = 1 << 15
{ ATK_STATE_SHOWING, kMapOpposite }, // states::OFFSCREEN = 1 << 16
{ ATK_STATE_RESIZABLE, kMapDirectly }, // states::SIZEABLE = 1 << 17
{ kNone, kMapDirectly }, // states::MOVEABLE = 1 << 18
{ kNone, kMapDirectly }, // states::SELFVOICING = 1 << 19
{ ATK_STATE_FOCUSABLE, kMapDirectly }, // states::FOCUSABLE = 1 << 20
{ ATK_STATE_SELECTABLE, kMapDirectly }, // states::SELECTABLE = 1 << 21
{ kNone, kMapDirectly }, // states::LINKED = 1 << 22
{ ATK_STATE_VISITED, kMapDirectly }, // states::TRAVERSED = 1 << 23
{ ATK_STATE_MULTISELECTABLE, kMapDirectly }, // states::MULTISELECTABLE = 1 << 24
{ kNone, kMapDirectly }, // states::EXTSELECTABLE = 1 << 25
{ ATK_STATE_REQUIRED, kMapDirectly }, // states::STATE_REQUIRED = 1 << 26
{ kNone, kMapDirectly }, // states::ALERT_MEDIUM = 1 << 27
{ ATK_STATE_INVALID_ENTRY, kMapDirectly }, // states::INVALID = 1 << 28
{ kNone, kMapDirectly }, // states::PROTECTED = 1 << 29
{ kNone, kMapDirectly }, // states::HASPOPUP = 1 << 30
{ ATK_STATE_SUPPORTS_AUTOCOMPLETION, kMapDirectly }, // states::SUPPORTS_AUTOCOMPLETION = 1 << 31
{ ATK_STATE_DEFUNCT, kMapDirectly }, // states::DEFUNCT = 1 << 32
{ ATK_STATE_SELECTABLE_TEXT, kMapDirectly }, // states::SELECTABLE_TEXT = 1 << 33
{ ATK_STATE_EDITABLE, kMapDirectly }, // states::EDITABLE = 1 << 34
{ ATK_STATE_ACTIVE, kMapDirectly }, // states::ACTIVE = 1 << 35
{ ATK_STATE_MODAL, kMapDirectly }, // states::MODAL = 1 << 36
{ ATK_STATE_MULTI_LINE, kMapDirectly }, // states::MULTI_LINE = 1 << 37
{ ATK_STATE_HORIZONTAL, kMapDirectly }, // states::HORIZONTAL = 1 << 38
{ ATK_STATE_OPAQUE, kMapDirectly }, // states::OPAQUE = 1 << 39
{ ATK_STATE_SINGLE_LINE, kMapDirectly }, // states::SINGLE_LINE = 1 << 40
{ ATK_STATE_TRANSIENT, kMapDirectly }, // states::TRANSIENT = 1 << 41
{ ATK_STATE_VERTICAL, kMapDirectly }, // states::VERTICAL = 1 << 42
{ ATK_STATE_STALE, kMapDirectly }, // states::STALE = 1 << 43
{ ATK_STATE_ENABLED, kMapDirectly }, // states::ENABLED = 1 << 44
{ ATK_STATE_SENSITIVE, kMapDirectly }, // states::SENSITIVE = 1 << 45
{ ATK_STATE_EXPANDABLE, kMapDirectly }, // states::EXPANDABLE = 1 << 46
{ kNone, kMapDirectly }, // states::PINNED = 1 << 47
{ kNone, kNoSuchState }, // = 1 << 48
};

1008
accessible/base/ARIAMap.cpp Normal file

File diff suppressed because it is too large Load diff

305
accessible/base/ARIAMap.h Normal file
View file

@ -0,0 +1,305 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_aria_ARIAMap_h_
#define mozilla_a11y_aria_ARIAMap_h_
#include "ARIAStateMap.h"
#include "mozilla/a11y/AccTypes.h"
#include "mozilla/a11y/Role.h"
#include "nsIAtom.h"
#include "nsIContent.h"
class nsINode;
////////////////////////////////////////////////////////////////////////////////
// Value constants
/**
* Used to define if role requires to expose Value interface.
*/
enum EValueRule
{
/**
* Value interface isn't exposed.
*/
eNoValue,
/**
* Value interface is implemented, supports value, min and max from
* aria-valuenow, aria-valuemin and aria-valuemax.
*/
eHasValueMinMax
};
////////////////////////////////////////////////////////////////////////////////
// Action constants
/**
* Used to define if the role requires to expose action.
*/
enum EActionRule
{
eNoAction,
eActivateAction,
eClickAction,
ePressAction,
eCheckUncheckAction,
eExpandAction,
eJumpAction,
eOpenCloseAction,
eSelectAction,
eSortAction,
eSwitchAction
};
////////////////////////////////////////////////////////////////////////////////
// Live region constants
/**
* Used to define if role exposes default value of aria-live attribute.
*/
enum ELiveAttrRule
{
eNoLiveAttr,
eOffLiveAttr,
ePoliteLiveAttr
};
////////////////////////////////////////////////////////////////////////////////
// Role constants
/**
* ARIA role overrides role from native markup.
*/
const bool kUseMapRole = true;
/**
* ARIA role doesn't override the role from native markup.
*/
const bool kUseNativeRole = false;
////////////////////////////////////////////////////////////////////////////////
// ARIA attribute characteristic masks
/**
* This mask indicates the attribute should not be exposed as an object
* attribute via the catch-all logic in Accessible::Attributes().
* This means it either isn't mean't to be exposed as an object attribute, or
* that it should, but is already handled in other code.
*/
const uint8_t ATTR_BYPASSOBJ = 0x1 << 0;
const uint8_t ATTR_BYPASSOBJ_IF_FALSE = 0x1 << 1;
/**
* This mask indicates the attribute is expected to have an NMTOKEN or bool value.
* (See for example usage in Accessible::Attributes())
*/
const uint8_t ATTR_VALTOKEN = 0x1 << 2;
/**
* Indicate the attribute is global state or property (refer to
* http://www.w3.org/TR/wai-aria/states_and_properties#global_states).
*/
const uint8_t ATTR_GLOBAL = 0x1 << 3;
////////////////////////////////////////////////////////////////////////////////
// State map entry
/**
* Used in nsRoleMapEntry.state if no nsIAccessibleStates are automatic for
* a given role.
*/
#define kNoReqStates 0
////////////////////////////////////////////////////////////////////////////////
// Role map entry
/**
* For each ARIA role, this maps the nsIAccessible information.
*/
struct nsRoleMapEntry
{
/**
* Return true if matches to the given ARIA role.
*/
bool Is(nsIAtom* aARIARole) const
{ return *roleAtom == aARIARole; }
/**
* Return true if ARIA role has the given accessible type.
*/
bool IsOfType(mozilla::a11y::AccGenericType aType) const
{ return accTypes & aType; }
/**
* Return ARIA role.
*/
const nsDependentAtomString ARIARoleString() const
{ return nsDependentAtomString(*roleAtom); }
// ARIA role: string representation such as "button"
nsIAtom** roleAtom;
// Role mapping rule: maps to enum Role
mozilla::a11y::role role;
// Role rule: whether to use mapped role or native semantics
bool roleRule;
// Value mapping rule: how to compute accessible value
EValueRule valueRule;
// Action mapping rule, how to expose accessible action
EActionRule actionRule;
// 'live' and 'container-live' object attributes mapping rule: how to expose
// these object attributes if ARIA 'live' attribute is missed.
ELiveAttrRule liveAttRule;
// Accessible types this role belongs to.
uint32_t accTypes;
// Automatic state mapping rule: always include in states
uint64_t state; // or kNoReqStates if no default state for this role
// ARIA properties supported for this role (in other words, the aria-foo
// attribute to accessible states mapping rules).
// Currently you cannot have unlimited mappings, because
// a variable sized array would not allow the use of
// C++'s struct initialization feature.
mozilla::a11y::aria::EStateRule attributeMap1;
mozilla::a11y::aria::EStateRule attributeMap2;
mozilla::a11y::aria::EStateRule attributeMap3;
mozilla::a11y::aria::EStateRule attributeMap4;
};
////////////////////////////////////////////////////////////////////////////////
// ARIA map
/**
* These provide the mappings for WAI-ARIA roles, states and properties using
* the structs defined in this file and ARIAStateMap files.
*/
namespace mozilla {
namespace a11y {
namespace aria {
/**
* Empty role map entry. Used by accessibility service to create an accessible
* if the accessible can't use role of used accessible class. For example,
* it is used for table cells that aren't contained by table.
*/
extern nsRoleMapEntry gEmptyRoleMap;
/**
* Constants for the role map entry index to indicate that the role map entry
* isn't in sWAIRoleMaps, but rather is a special entry: nullptr,
* gEmptyRoleMap, and sLandmarkRoleMap
*/
const uint8_t NO_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 2;
const uint8_t EMPTY_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 1;
const uint8_t LANDMARK_ROLE_MAP_ENTRY_INDEX = UINT8_MAX;
/**
* Get the role map entry for a given DOM node. This will use the first
* ARIA role if the role attribute provides a space delimited list of roles.
*
* @param aEl [in] the DOM node to get the role map entry for
* @return a pointer to the role map entry for the ARIA role, or nullptr
* if none
*/
const nsRoleMapEntry* GetRoleMap(dom::Element* aEl);
/**
* Get the role map entry pointer's index for a given DOM node. This will use
* the first ARIA role if the role attribute provides a space delimited list of
* roles.
*
* @param aEl [in] the DOM node to get the role map entry for
* @return the index of the pointer to the role map entry for the ARIA
* role, or NO_ROLE_MAP_ENTRY_INDEX if none
*/
uint8_t GetRoleMapIndex(dom::Element* aEl);
/**
* Get the role map entry pointer for a given role map entry index.
*
* @param aRoleMapIndex [in] the role map index to get the role map entry
* pointer for
* @return a pointer to the role map entry for the ARIA role,
* or nullptr, if none
*/
const nsRoleMapEntry* GetRoleMapFromIndex(uint8_t aRoleMapIndex);
/**
* Get the role map entry index for a given role map entry pointer. If the role
* map entry is within sWAIRoleMaps, return the index within that array,
* otherwise return one of the special index constants listed above.
*
* @param aRoleMap [in] the role map entry pointer to get the index for
* @return the index of the pointer to the role map entry, or
* NO_ROLE_MAP_ENTRY_INDEX if none
*/
uint8_t GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMap);
/**
* Return accessible state from ARIA universal states applied to the given
* element.
*/
uint64_t UniversalStatesFor(mozilla::dom::Element* aElement);
/**
* Get the ARIA attribute characteristics for a given ARIA attribute.
*
* @param aAtom ARIA attribute
* @return A bitflag representing the attribute characteristics
* (see above for possible bit masks, prefixed "ATTR_")
*/
uint8_t AttrCharacteristicsFor(nsIAtom* aAtom);
/**
* Return true if the element has defined aria-hidden.
*/
bool HasDefinedARIAHidden(nsIContent* aContent);
/**
* Represents a simple enumerator for iterating through ARIA attributes
* exposed as object attributes on a given accessible.
*/
class AttrIterator
{
public:
explicit AttrIterator(nsIContent* aContent) :
mContent(aContent), mAttrIdx(0)
{
mAttrCount = mContent->GetAttrCount();
}
bool Next(nsAString& aAttrName, nsAString& aAttrValue);
private:
AttrIterator() = delete;
AttrIterator(const AttrIterator&) = delete;
AttrIterator& operator= (const AttrIterator&) = delete;
nsIContent* mContent;
uint32_t mAttrIdx;
uint32_t mAttrCount;
};
} // namespace aria
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,376 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ARIAMap.h"
#include "nsAccUtils.h"
#include "States.h"
#include "mozilla/dom/Element.h"
using namespace mozilla;
using namespace mozilla::a11y;
using namespace mozilla::a11y::aria;
/**
* Used to store state map rule data for ARIA attribute of enum type.
*/
struct EnumTypeData
{
// ARIA attribute name.
nsIAtom* const mAttrName;
// States if the attribute value is matched to the enum value. Used as
// nsIContent::AttrValuesArray, last item must be nullptr.
nsIAtom* const* const mValues[4];
// States applied if corresponding enum values are matched.
const uint64_t mStates[3];
// States to clear in case of match.
const uint64_t mClearState;
};
enum ETokenType
{
eBoolType = 0,
eMixedType = 1, // can take 'mixed' value
eDefinedIfAbsent = 2 // permanent and false state are applied if absent
};
/**
* Used to store state map rule data for ARIA attribute of token type (including
* mixed value).
*/
struct TokenTypeData
{
TokenTypeData(nsIAtom* aAttrName, uint32_t aType,
uint64_t aPermanentState,
uint64_t aTrueState,
uint64_t aFalseState = 0) :
mAttrName(aAttrName), mType(aType), mPermanentState(aPermanentState),
mTrueState(aTrueState), mFalseState(aFalseState)
{ }
// ARIA attribute name.
nsIAtom* const mAttrName;
// Type.
const uint32_t mType;
// State applied if the attribute is defined or mType doesn't have
// eDefinedIfAbsent flag set.
const uint64_t mPermanentState;
// States applied if the attribute value is true/false.
const uint64_t mTrueState;
const uint64_t mFalseState;
};
/**
* Map enum type attribute value to accessible state.
*/
static void MapEnumType(dom::Element* aElement, uint64_t* aState,
const EnumTypeData& aData);
/**
* Map token type attribute value to states.
*/
static void MapTokenType(dom::Element* aContent, uint64_t* aState,
const TokenTypeData& aData);
bool
aria::MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState)
{
switch (aRule) {
case eARIAAutoComplete:
{
static const EnumTypeData data = {
nsGkAtoms::aria_autocomplete,
{ &nsGkAtoms::inlinevalue,
&nsGkAtoms::list,
&nsGkAtoms::both, nullptr },
{ states::SUPPORTS_AUTOCOMPLETION,
states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION,
states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION }, 0
};
MapEnumType(aElement, aState, data);
return true;
}
case eARIABusy:
{
static const EnumTypeData data = {
nsGkAtoms::aria_busy,
{ &nsGkAtoms::_true,
&nsGkAtoms::error, nullptr },
{ states::BUSY,
states::INVALID }, 0
};
MapEnumType(aElement, aState, data);
return true;
}
case eARIACheckableBool:
{
static const TokenTypeData data(
nsGkAtoms::aria_checked, eBoolType | eDefinedIfAbsent,
states::CHECKABLE, states::CHECKED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIACheckableMixed:
{
static const TokenTypeData data(
nsGkAtoms::aria_checked, eMixedType | eDefinedIfAbsent,
states::CHECKABLE, states::CHECKED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIACheckedMixed:
{
static const TokenTypeData data(
nsGkAtoms::aria_checked, eMixedType,
states::CHECKABLE, states::CHECKED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIADisabled:
{
static const TokenTypeData data(
nsGkAtoms::aria_disabled, eBoolType,
0, states::UNAVAILABLE);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAExpanded:
{
static const TokenTypeData data(
nsGkAtoms::aria_expanded, eBoolType,
0, states::EXPANDED, states::COLLAPSED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAHasPopup:
{
static const TokenTypeData data(
nsGkAtoms::aria_haspopup, eBoolType,
0, states::HASPOPUP);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAInvalid:
{
static const TokenTypeData data(
nsGkAtoms::aria_invalid, eBoolType,
0, states::INVALID);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAModal:
{
static const TokenTypeData data(
nsGkAtoms::aria_modal, eBoolType,
0, states::MODAL);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAMultiline:
{
static const TokenTypeData data(
nsGkAtoms::aria_multiline, eBoolType | eDefinedIfAbsent,
0, states::MULTI_LINE, states::SINGLE_LINE);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAMultiSelectable:
{
static const TokenTypeData data(
nsGkAtoms::aria_multiselectable, eBoolType,
0, states::MULTISELECTABLE | states::EXTSELECTABLE);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAOrientation:
{
static const EnumTypeData data = {
nsGkAtoms::aria_orientation,
{ &nsGkAtoms::horizontal,
&nsGkAtoms::vertical, nullptr },
{ states::HORIZONTAL,
states::VERTICAL },
states::HORIZONTAL | states::VERTICAL
};
MapEnumType(aElement, aState, data);
return true;
}
case eARIAPressed:
{
static const TokenTypeData data(
nsGkAtoms::aria_pressed, eMixedType,
0, states::PRESSED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAReadonly:
{
static const TokenTypeData data(
nsGkAtoms::aria_readonly, eBoolType,
0, states::READONLY);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAReadonlyOrEditable:
{
static const TokenTypeData data(
nsGkAtoms::aria_readonly, eBoolType | eDefinedIfAbsent,
0, states::READONLY, states::EDITABLE);
MapTokenType(aElement, aState, data);
return true;
}
case eARIAReadonlyOrEditableIfDefined:
{
static const TokenTypeData data(
nsGkAtoms::aria_readonly, eBoolType,
0, states::READONLY, states::EDITABLE);
MapTokenType(aElement, aState, data);
return true;
}
case eARIARequired:
{
static const TokenTypeData data(
nsGkAtoms::aria_required, eBoolType,
0, states::REQUIRED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIASelectable:
{
static const TokenTypeData data(
nsGkAtoms::aria_selected, eBoolType | eDefinedIfAbsent,
states::SELECTABLE, states::SELECTED);
MapTokenType(aElement, aState, data);
return true;
}
case eARIASelectableIfDefined:
{
static const TokenTypeData data(
nsGkAtoms::aria_selected, eBoolType,
states::SELECTABLE, states::SELECTED);
MapTokenType(aElement, aState, data);
return true;
}
case eReadonlyUntilEditable:
{
if (!(*aState & states::EDITABLE))
*aState |= states::READONLY;
return true;
}
case eIndeterminateIfNoValue:
{
if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow) &&
!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext))
*aState |= states::MIXED;
return true;
}
case eFocusableUntilDisabled:
{
if (!nsAccUtils::HasDefinedARIAToken(aElement, nsGkAtoms::aria_disabled) ||
aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
nsGkAtoms::_false, eCaseMatters))
*aState |= states::FOCUSABLE;
return true;
}
default:
return false;
}
}
static void
MapEnumType(dom::Element* aElement, uint64_t* aState, const EnumTypeData& aData)
{
switch (aElement->FindAttrValueIn(kNameSpaceID_None, aData.mAttrName,
aData.mValues, eCaseMatters)) {
case 0:
*aState = (*aState & ~aData.mClearState) | aData.mStates[0];
return;
case 1:
*aState = (*aState & ~aData.mClearState) | aData.mStates[1];
return;
case 2:
*aState = (*aState & ~aData.mClearState) | aData.mStates[2];
return;
}
}
static void
MapTokenType(dom::Element* aElement, uint64_t* aState,
const TokenTypeData& aData)
{
if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
if ((aData.mType & eMixedType) &&
aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
nsGkAtoms::mixed, eCaseMatters)) {
*aState |= aData.mPermanentState | states::MIXED;
return;
}
if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
nsGkAtoms::_false, eCaseMatters)) {
*aState |= aData.mPermanentState | aData.mFalseState;
return;
}
*aState |= aData.mPermanentState | aData.mTrueState;
return;
}
if (aData.mType & eDefinedIfAbsent)
*aState |= aData.mPermanentState | aData.mFalseState;
}

View file

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _mozilla_a11y_aria_ARIAStateMap_h_
#define _mozilla_a11y_aria_ARIAStateMap_h_
#include <stdint.h>
namespace mozilla {
namespace dom {
class Element;
}
namespace a11y {
namespace aria {
/**
* List of the ARIA state mapping rules.
*/
enum EStateRule
{
eARIANone,
eARIAAutoComplete,
eARIABusy,
eARIACheckableBool,
eARIACheckableMixed,
eARIACheckedMixed,
eARIADisabled,
eARIAExpanded,
eARIAHasPopup,
eARIAInvalid,
eARIAModal,
eARIAMultiline,
eARIAMultiSelectable,
eARIAOrientation,
eARIAPressed,
eARIAReadonly,
eARIAReadonlyOrEditable,
eARIAReadonlyOrEditableIfDefined,
eARIARequired,
eARIASelectable,
eARIASelectableIfDefined,
eReadonlyUntilEditable,
eIndeterminateIfNoValue,
eFocusableUntilDisabled
};
/**
* Expose the accessible states for the given element accordingly to state
* mapping rule.
*
* @param aRule [in] state mapping rule ID
* @param aElement [in] node of the accessible
* @param aState [in/out] accessible states
* @return true if state map rule ID is valid
*/
bool MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState);
} // namespace aria
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,271 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccEvent.h"
#include "nsAccUtils.h"
#include "DocAccessible.h"
#include "xpcAccEvents.h"
#include "States.h"
#include "xpcAccessibleDocument.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/dom/Selection.h"
using namespace mozilla;
using namespace mozilla::a11y;
static_assert(static_cast<bool>(eNoUserInput) == false &&
static_cast<bool>(eFromUserInput) == true,
"EIsFromUserInput cannot be casted to bool");
////////////////////////////////////////////////////////////////////////////////
// AccEvent
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// AccEvent constructors
AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
{
if (aIsFromUserInput == eAutoDetect)
mIsFromUserInput = EventStateManager::IsHandlingUserInput();
else
mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
// AccEvent cycle collection
NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible)
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
tmEvent->SetNextEvent(nullptr);
tmEvent->SetPrevEvent(nullptr);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible)
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
CycleCollectionNoteChild(cb, tmEvent->NextEvent(), "mNext");
CycleCollectionNoteChild(cb, tmEvent->PrevEvent(), "mPrevEvent");
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// AccTextChangeEvent
////////////////////////////////////////////////////////////////////////////////
// Note: we pass in eAllowDupes to the base class because we don't support text
// events coalescence. We fire delayed text change events in DocAccessible but
// we continue to base the event off the accessible object rather than just the
// node. This means we won't try to create an accessible based on the node when
// we are ready to fire the event and so we will no longer assert at that point
// if the node was removed from the document. Either way, the AT won't work with
// a defunct accessible so the behaviour should be equivalent.
AccTextChangeEvent::
AccTextChangeEvent(Accessible* aAccessible, int32_t aStart,
const nsAString& aModifiedText, bool aIsInserted,
EIsFromUserInput aIsFromUserInput)
: AccEvent(aIsInserted ?
static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) :
static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_REMOVED),
aAccessible, aIsFromUserInput, eAllowDupes)
, mStart(aStart)
, mIsInserted(aIsInserted)
, mModifiedText(aModifiedText)
{
// XXX We should use IsFromUserInput here, but that isn't always correct
// when the text change isn't related to content insertion or removal.
mIsFromUserInput = mAccessible->State() &
(states::FOCUSED | states::EDITABLE);
}
////////////////////////////////////////////////////////////////////////////////
// AccHideEvent
////////////////////////////////////////////////////////////////////////////////
AccHideEvent::
AccHideEvent(Accessible* aTarget, bool aNeedsShutdown) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget),
mNeedsShutdown(aNeedsShutdown)
{
mNextSibling = mAccessible->NextSibling();
mPrevSibling = mAccessible->PrevSibling();
}
////////////////////////////////////////////////////////////////////////////////
// AccShowEvent
////////////////////////////////////////////////////////////////////////////////
AccShowEvent::
AccShowEvent(Accessible* aTarget) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget)
{
int32_t idx = aTarget->IndexInParent();
MOZ_ASSERT(idx >= 0);
mInsertionIndex = idx;
}
////////////////////////////////////////////////////////////////////////////////
// AccTextSelChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
dom::Selection* aSelection,
int32_t aReason) :
AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
eAutoDetect, eCoalesceTextSelChange),
mSel(aSelection), mReason(aReason) {}
AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
bool
AccTextSelChangeEvent::IsCaretMoveOnly() const
{
return mSel->RangeCount() == 1 && mSel->IsCollapsed() &&
((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON |
nsISelectionListener::COLLAPSETOEND_REASON)) == 0);
}
////////////////////////////////////////////////////////////////////////////////
// AccSelChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccSelChangeEvent::
AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
SelChangeType aSelChangeType) :
AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
mPreceedingCount(0), mPackedEvent(nullptr)
{
if (aSelChangeType == eSelectionAdd) {
if (mWidget->GetSelectedItem(1))
mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
else
mEventType = nsIAccessibleEvent::EVENT_SELECTION;
} else {
mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
}
}
////////////////////////////////////////////////////////////////////////////////
// AccTableChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccTableChangeEvent::
AccTableChangeEvent(Accessible* aAccessible, uint32_t aEventType,
int32_t aRowOrColIndex, int32_t aNumRowsOrCols) :
AccEvent(aEventType, aAccessible),
mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
{
}
////////////////////////////////////////////////////////////////////////////////
// AccVCChangeEvent
////////////////////////////////////////////////////////////////////////////////
AccVCChangeEvent::
AccVCChangeEvent(Accessible* aAccessible,
Accessible* aOldAccessible,
int32_t aOldStart, int32_t aOldEnd,
int16_t aReason, EIsFromUserInput aIsFromUserInput) :
AccEvent(::nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, aAccessible,
aIsFromUserInput),
mOldAccessible(aOldAccessible), mOldStart(aOldStart), mOldEnd(aOldEnd),
mReason(aReason)
{
}
already_AddRefed<nsIAccessibleEvent>
a11y::MakeXPCEvent(AccEvent* aEvent)
{
DocAccessible* doc = aEvent->Document();
Accessible* acc = aEvent->GetAccessible();
nsINode* node = acc->GetNode();
nsIDOMNode* domNode = node ? node->AsDOMNode() : nullptr;
bool fromUser = aEvent->IsFromUserInput();
uint32_t type = aEvent->GetEventType();
uint32_t eventGroup = aEvent->GetEventGroups();
nsCOMPtr<nsIAccessibleEvent> xpEvent;
if (eventGroup & (1 << AccEvent::eStateChangeEvent)) {
AccStateChangeEvent* sc = downcast_accEvent(aEvent);
bool extra = false;
uint32_t state = nsAccUtils::To32States(sc->GetState(), &extra);
xpEvent = new xpcAccStateChangeEvent(type, ToXPC(acc), ToXPCDocument(doc),
domNode, fromUser,
state, extra, sc->IsStateEnabled());
return xpEvent.forget();
}
if (eventGroup & (1 << AccEvent::eTextChangeEvent)) {
AccTextChangeEvent* tc = downcast_accEvent(aEvent);
nsString text;
tc->GetModifiedText(text);
xpEvent = new xpcAccTextChangeEvent(type, ToXPC(acc), ToXPCDocument(doc),
domNode, fromUser,
tc->GetStartOffset(), tc->GetLength(),
tc->IsTextInserted(), text);
return xpEvent.forget();
}
if (eventGroup & (1 << AccEvent::eHideEvent)) {
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
xpEvent = new xpcAccHideEvent(type, ToXPC(acc), ToXPCDocument(doc),
domNode, fromUser,
ToXPC(hideEvent->TargetParent()),
ToXPC(hideEvent->TargetNextSibling()),
ToXPC(hideEvent->TargetPrevSibling()));
return xpEvent.forget();
}
if (eventGroup & (1 << AccEvent::eCaretMoveEvent)) {
AccCaretMoveEvent* cm = downcast_accEvent(aEvent);
xpEvent = new xpcAccCaretMoveEvent(type, ToXPC(acc), ToXPCDocument(doc),
domNode, fromUser,
cm->GetCaretOffset());
return xpEvent.forget();
}
if (eventGroup & (1 << AccEvent::eVirtualCursorChangeEvent)) {
AccVCChangeEvent* vcc = downcast_accEvent(aEvent);
xpEvent = new xpcAccVirtualCursorChangeEvent(type,
ToXPC(acc), ToXPCDocument(doc),
domNode, fromUser,
ToXPC(vcc->OldAccessible()),
vcc->OldStartOffset(),
vcc->OldEndOffset(),
vcc->Reason());
return xpEvent.forget();
}
if (eventGroup & (1 << AccEvent::eObjectAttrChangedEvent)) {
AccObjectAttrChangedEvent* oac = downcast_accEvent(aEvent);
xpEvent = new xpcAccObjectAttributeChangedEvent(type,
ToXPC(acc),
ToXPCDocument(doc), domNode,
fromUser,
oac->GetAttribute());
return xpEvent.forget();
}
xpEvent = new xpcAccEvent(type, ToXPC(acc), ToXPCDocument(doc), domNode, fromUser);
return xpEvent.forget();
}

570
accessible/base/AccEvent.h Normal file
View file

@ -0,0 +1,570 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _AccEvent_H_
#define _AccEvent_H_
#include "nsIAccessibleEvent.h"
#include "mozilla/a11y/Accessible.h"
class nsEventShell;
namespace mozilla {
namespace dom {
class Selection;
}
namespace a11y {
class DocAccessible;
// Constants used to point whether the event is from user input.
enum EIsFromUserInput
{
// eNoUserInput: event is not from user input
eNoUserInput = 0,
// eFromUserInput: event is from user input
eFromUserInput = 1,
// eAutoDetect: the value should be obtained from event state manager
eAutoDetect = -1
};
/**
* Generic accessible event.
*/
class AccEvent
{
public:
// Rule for accessible events.
// The rule will be applied when flushing pending events.
enum EEventRule {
// eAllowDupes : More than one event of the same type is allowed.
// This event will always be emitted. This flag is used for events that
// don't support coalescence.
eAllowDupes,
// eCoalesceReorder : For reorder events from the same subtree or the same
// node, only the umbrella event on the ancestor will be emitted.
eCoalesceReorder,
// eCoalesceOfSameType : For events of the same type, only the newest event
// will be processed.
eCoalesceOfSameType,
// eCoalesceSelectionChange: coalescence of selection change events.
eCoalesceSelectionChange,
// eCoalesceStateChange: coalesce state change events.
eCoalesceStateChange,
// eCoalesceTextSelChange: coalescence of text selection change events.
eCoalesceTextSelChange,
// eRemoveDupes : For repeat events, only the newest event in queue
// will be emitted.
eRemoveDupes,
// eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
eDoNotEmit
};
// Initialize with an accessible.
AccEvent(uint32_t aEventType, Accessible* aAccessible,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
// AccEvent
uint32_t GetEventType() const { return mEventType; }
EEventRule GetEventRule() const { return mEventRule; }
bool IsFromUserInput() const { return mIsFromUserInput; }
EIsFromUserInput FromUserInput() const
{ return static_cast<EIsFromUserInput>(mIsFromUserInput); }
Accessible* GetAccessible() const { return mAccessible; }
DocAccessible* Document() const { return mAccessible->Document(); }
/**
* Down casting.
*/
enum EventGroup {
eGenericEvent,
eStateChangeEvent,
eTextChangeEvent,
eTreeMutationEvent,
eMutationEvent,
eReorderEvent,
eHideEvent,
eShowEvent,
eCaretMoveEvent,
eTextSelChangeEvent,
eSelectionChangeEvent,
eTableChangeEvent,
eVirtualCursorChangeEvent,
eObjectAttrChangedEvent
};
static const EventGroup kEventGroup = eGenericEvent;
virtual unsigned int GetEventGroups() const
{
return 1U << eGenericEvent;
}
/**
* Reference counting and cycle collection.
*/
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AccEvent)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
protected:
virtual ~AccEvent() {}
bool mIsFromUserInput;
uint32_t mEventType;
EEventRule mEventRule;
RefPtr<Accessible> mAccessible;
friend class EventQueue;
friend class EventTree;
friend class ::nsEventShell;
friend class NotificationController;
};
/**
* Accessible state change event.
*/
class AccStateChangeEvent: public AccEvent
{
public:
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState,
bool aIsEnabled,
EIsFromUserInput aIsFromUserInput = eAutoDetect) :
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
aIsFromUserInput, eCoalesceStateChange),
mState(aState), mIsEnabled(aIsEnabled) { }
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState) :
AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
eAutoDetect, eCoalesceStateChange), mState(aState)
{ mIsEnabled = (mAccessible->State() & mState) != 0; }
// AccEvent
static const EventGroup kEventGroup = eStateChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eStateChangeEvent);
}
// AccStateChangeEvent
uint64_t GetState() const { return mState; }
bool IsStateEnabled() const { return mIsEnabled; }
private:
uint64_t mState;
bool mIsEnabled;
friend class EventQueue;
};
/**
* Accessible text change event.
*/
class AccTextChangeEvent: public AccEvent
{
public:
AccTextChangeEvent(Accessible* aAccessible, int32_t aStart,
const nsAString& aModifiedText, bool aIsInserted,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
// AccEvent
static const EventGroup kEventGroup = eTextChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eTextChangeEvent);
}
// AccTextChangeEvent
int32_t GetStartOffset() const { return mStart; }
uint32_t GetLength() const { return mModifiedText.Length(); }
bool IsTextInserted() const { return mIsInserted; }
void GetModifiedText(nsAString& aModifiedText)
{ aModifiedText = mModifiedText; }
const nsString& ModifiedText() const { return mModifiedText; }
private:
int32_t mStart;
bool mIsInserted;
nsString mModifiedText;
friend class EventTree;
friend class NotificationController;
};
/**
* A base class for events related to tree mutation, either an AccMutation
* event, or an AccReorderEvent.
*/
class AccTreeMutationEvent : public AccEvent
{
public:
AccTreeMutationEvent(uint32_t aEventType, Accessible* aTarget) :
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceReorder), mGeneration(0) {}
// Event
static const EventGroup kEventGroup = eTreeMutationEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eTreeMutationEvent);
}
void SetNextEvent(AccTreeMutationEvent* aNext) { mNextEvent = aNext; }
void SetPrevEvent(AccTreeMutationEvent* aPrev) { mPrevEvent = aPrev; }
AccTreeMutationEvent* NextEvent() const { return mNextEvent; }
AccTreeMutationEvent* PrevEvent() const { return mPrevEvent; }
/**
* A sequence number to know when this event was fired.
*/
uint32_t EventGeneration() const { return mGeneration; }
void SetEventGeneration(uint32_t aGeneration) { mGeneration = aGeneration; }
private:
RefPtr<AccTreeMutationEvent> mNextEvent;
RefPtr<AccTreeMutationEvent> mPrevEvent;
uint32_t mGeneration;
};
/**
* Base class for show and hide accessible events.
*/
class AccMutationEvent: public AccTreeMutationEvent
{
public:
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
AccTreeMutationEvent(aEventType, aTarget)
{
// Don't coalesce these since they are coalesced by reorder event. Coalesce
// contained text change events.
mParent = mAccessible->Parent();
}
virtual ~AccMutationEvent() { }
// Event
static const EventGroup kEventGroup = eMutationEvent;
virtual unsigned int GetEventGroups() const override
{
return AccTreeMutationEvent::GetEventGroups() | (1U << eMutationEvent);
}
// MutationEvent
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
Accessible* Parent() const { return mParent; }
protected:
nsCOMPtr<nsINode> mNode;
RefPtr<Accessible> mParent;
RefPtr<AccTextChangeEvent> mTextChangeEvent;
friend class EventTree;
friend class NotificationController;
};
/**
* Accessible hide event.
*/
class AccHideEvent: public AccMutationEvent
{
public:
explicit AccHideEvent(Accessible* aTarget, bool aNeedsShutdown = true);
// Event
static const EventGroup kEventGroup = eHideEvent;
virtual unsigned int GetEventGroups() const override
{
return AccMutationEvent::GetEventGroups() | (1U << eHideEvent);
}
// AccHideEvent
Accessible* TargetParent() const { return mParent; }
Accessible* TargetNextSibling() const { return mNextSibling; }
Accessible* TargetPrevSibling() const { return mPrevSibling; }
bool NeedsShutdown() const { return mNeedsShutdown; }
protected:
bool mNeedsShutdown;
RefPtr<Accessible> mNextSibling;
RefPtr<Accessible> mPrevSibling;
friend class EventTree;
friend class NotificationController;
};
/**
* Accessible show event.
*/
class AccShowEvent: public AccMutationEvent
{
public:
explicit AccShowEvent(Accessible* aTarget);
// Event
static const EventGroup kEventGroup = eShowEvent;
virtual unsigned int GetEventGroups() const override
{
return AccMutationEvent::GetEventGroups() | (1U << eShowEvent);
}
uint32_t InsertionIndex() const { return mInsertionIndex; }
private:
nsTArray<RefPtr<AccHideEvent>> mPrecedingEvents;
uint32_t mInsertionIndex;
friend class EventTree;
};
/**
* Class for reorder accessible event. Takes care about
*/
class AccReorderEvent : public AccTreeMutationEvent
{
public:
explicit AccReorderEvent(Accessible* aTarget) :
AccTreeMutationEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget) { }
virtual ~AccReorderEvent() { }
// Event
static const EventGroup kEventGroup = eReorderEvent;
virtual unsigned int GetEventGroups() const override
{
return AccTreeMutationEvent::GetEventGroups() | (1U << eReorderEvent);
}
};
/**
* Accessible caret move event.
*/
class AccCaretMoveEvent: public AccEvent
{
public:
AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset,
EIsFromUserInput aIsFromUserInput = eAutoDetect) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
aIsFromUserInput),
mCaretOffset(aCaretOffset) { }
virtual ~AccCaretMoveEvent() { }
// AccEvent
static const EventGroup kEventGroup = eCaretMoveEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent);
}
// AccCaretMoveEvent
int32_t GetCaretOffset() const { return mCaretOffset; }
private:
int32_t mCaretOffset;
};
/**
* Accessible text selection change event.
*/
class AccTextSelChangeEvent : public AccEvent
{
public:
AccTextSelChangeEvent(HyperTextAccessible* aTarget,
dom::Selection* aSelection,
int32_t aReason);
virtual ~AccTextSelChangeEvent();
// AccEvent
static const EventGroup kEventGroup = eTextSelChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent);
}
// AccTextSelChangeEvent
/**
* Return true if the text selection change wasn't caused by pure caret move.
*/
bool IsCaretMoveOnly() const;
private:
RefPtr<dom::Selection> mSel;
int32_t mReason;
friend class EventQueue;
friend class SelectionManager;
};
/**
* Accessible widget selection change event.
*/
class AccSelChangeEvent : public AccEvent
{
public:
enum SelChangeType {
eSelectionAdd,
eSelectionRemove
};
AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
SelChangeType aSelChangeType);
virtual ~AccSelChangeEvent() { }
// AccEvent
static const EventGroup kEventGroup = eSelectionChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
}
// AccSelChangeEvent
Accessible* Widget() const { return mWidget; }
private:
RefPtr<Accessible> mWidget;
RefPtr<Accessible> mItem;
SelChangeType mSelChangeType;
uint32_t mPreceedingCount;
AccSelChangeEvent* mPackedEvent;
friend class EventQueue;
};
/**
* Accessible table change event.
*/
class AccTableChangeEvent : public AccEvent
{
public:
AccTableChangeEvent(Accessible* aAccessible, uint32_t aEventType,
int32_t aRowOrColIndex, int32_t aNumRowsOrCols);
// AccEvent
static const EventGroup kEventGroup = eTableChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eTableChangeEvent);
}
// AccTableChangeEvent
uint32_t GetIndex() const { return mRowOrColIndex; }
uint32_t GetCount() const { return mNumRowsOrCols; }
private:
uint32_t mRowOrColIndex; // the start row/column after which the rows are inserted/deleted.
uint32_t mNumRowsOrCols; // the number of inserted/deleted rows/columns
};
/**
* Accessible virtual cursor change event.
*/
class AccVCChangeEvent : public AccEvent
{
public:
AccVCChangeEvent(Accessible* aAccessible,
Accessible* aOldAccessible,
int32_t aOldStart, int32_t aOldEnd,
int16_t aReason,
EIsFromUserInput aIsFromUserInput = eFromUserInput);
virtual ~AccVCChangeEvent() { }
// AccEvent
static const EventGroup kEventGroup = eVirtualCursorChangeEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eVirtualCursorChangeEvent);
}
// AccTableChangeEvent
Accessible* OldAccessible() const { return mOldAccessible; }
int32_t OldStartOffset() const { return mOldStart; }
int32_t OldEndOffset() const { return mOldEnd; }
int32_t Reason() const { return mReason; }
private:
RefPtr<Accessible> mOldAccessible;
int32_t mOldStart;
int32_t mOldEnd;
int16_t mReason;
};
/**
* Accessible object attribute changed event.
*/
class AccObjectAttrChangedEvent: public AccEvent
{
public:
AccObjectAttrChangedEvent(Accessible* aAccessible, nsIAtom* aAttribute) :
AccEvent(::nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, aAccessible),
mAttribute(aAttribute) { }
// AccEvent
static const EventGroup kEventGroup = eObjectAttrChangedEvent;
virtual unsigned int GetEventGroups() const override
{
return AccEvent::GetEventGroups() | (1U << eObjectAttrChangedEvent);
}
// AccObjectAttrChangedEvent
nsIAtom* GetAttribute() const { return mAttribute; }
private:
nsCOMPtr<nsIAtom> mAttribute;
virtual ~AccObjectAttrChangedEvent() { }
};
/**
* Downcast the generic accessible event object to derived type.
*/
class downcast_accEvent
{
public:
explicit downcast_accEvent(AccEvent* e) : mRawPtr(e) { }
template<class Destination>
operator Destination*() {
if (!mRawPtr)
return nullptr;
return mRawPtr->GetEventGroups() & (1U << Destination::kEventGroup) ?
static_cast<Destination*>(mRawPtr) : nullptr;
}
private:
AccEvent* mRawPtr;
};
/**
* Return a new xpcom accessible event for the given internal one.
*/
already_AddRefed<nsIAccessibleEvent>
MakeXPCEvent(AccEvent* aEvent);
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,227 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccGroupInfo.h"
#include "nsAccUtils.h"
#include "Role.h"
#include "States.h"
using namespace mozilla::a11y;
AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole)
{
MOZ_COUNT_CTOR(AccGroupInfo);
Update();
}
void
AccGroupInfo::Update()
{
Accessible* parent = mItem->Parent();
if (!parent)
return;
int32_t indexInParent = mItem->IndexInParent();
uint32_t siblingCount = parent->ChildCount();
if (indexInParent == -1 ||
indexInParent >= static_cast<int32_t>(siblingCount)) {
NS_ERROR("Wrong index in parent! Tree invalidation problem.");
return;
}
int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
// Compute position in set.
mPosInSet = 1;
for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
Accessible* sibling = parent->GetChildAt(idx);
roles::Role siblingRole = sibling->Role();
// If the sibling is separator then the group is ended.
if (siblingRole == roles::SEPARATOR)
break;
// If sibling is not visible and hasn't the same base role.
if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
continue;
// Check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is split by some child elements
// (group will be continued).
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level) {
mParent = sibling;
break;
}
// Skip subset.
if (siblingLevel > level)
continue;
// If the previous item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mBits.groupInfo) {
mPosInSet += sibling->mBits.groupInfo->mPosInSet;
mParent = sibling->mBits.groupInfo->mParent;
mSetSize = sibling->mBits.groupInfo->mSetSize;
return;
}
mPosInSet++;
}
// Compute set size.
mSetSize = mPosInSet;
for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
Accessible* sibling = parent->GetChildAt(idx);
roles::Role siblingRole = sibling->Role();
// If the sibling is separator then the group is ended.
if (siblingRole == roles::SEPARATOR)
break;
// If sibling is visible and has the same base role
if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
continue;
// and check if it's hierarchical flatten structure.
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
// Skip subset.
if (siblingLevel > level)
continue;
// If the next item in the group has calculated group information then
// build group information for this item based on found one.
if (sibling->mBits.groupInfo) {
mParent = sibling->mBits.groupInfo->mParent;
mSetSize = sibling->mBits.groupInfo->mSetSize;
return;
}
mSetSize++;
}
if (mParent)
return;
roles::Role parentRole = parent->Role();
if (ShouldReportRelations(mRole, parentRole))
mParent = parent;
// ARIA tree and list can be arranged by using ARIA groups to organize levels.
if (parentRole != roles::GROUPING)
return;
// Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a
// parent. In other words the parent of the tree item will be a group and
// the previous tree item of the group is a conceptual parent of the tree
// item.
if (mRole == roles::OUTLINEITEM) {
Accessible* parentPrevSibling = parent->PrevSibling();
if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
mParent = parentPrevSibling;
return;
}
}
// Way #2 for ARIA list and tree: group is a child of an item. In other words
// the parent of the item will be a group and containing item of the group is
// a conceptual parent of the item.
if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
Accessible* grandParent = parent->Parent();
if (grandParent && grandParent->Role() == mRole)
mParent = grandParent;
}
}
Accessible*
AccGroupInfo::FirstItemOf(Accessible* aContainer)
{
// ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a
// group is a parent) or by aria-level.
a11y::role containerRole = aContainer->Role();
Accessible* item = aContainer->NextSibling();
if (item) {
if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
item = item->FirstChild();
if (item) {
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
}
}
// ARIA list and tree can be arranged by ARIA groups case #2 (group is
// a child of an item).
item = aContainer->LastChild();
if (!item)
return nullptr;
if (item->Role() == roles::GROUPING &&
(containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
item = item->FirstChild();
if (item) {
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
}
}
// Otherwise, it can be a direct child if the container is a list or tree.
item = aContainer->FirstChild();
if (ShouldReportRelations(item->Role(), containerRole))
return item;
return nullptr;
}
Accessible*
AccGroupInfo::NextItemTo(Accessible* aItem)
{
AccGroupInfo* groupInfo = aItem->GetGroupInfo();
if (!groupInfo)
return nullptr;
// If the item in middle of the group then search next item in siblings.
if (groupInfo->PosInSet() >= groupInfo->SetSize())
return nullptr;
Accessible* parent = aItem->Parent();
uint32_t childCount = parent->ChildCount();
for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
Accessible* nextItem = parent->GetChildAt(idx);
AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
if (nextGroupInfo &&
nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
return nextItem;
}
}
NS_NOTREACHED("Item in the middle of the group but there's no next item!");
return nullptr;
}
bool
AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole)
{
// We only want to report hierarchy-based node relations for items in tree or
// list form. ARIA level/owns relations are always reported.
if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
return true;
if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW)
return true;
if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
return true;
return false;
}

View file

@ -0,0 +1,114 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef AccGroupInfo_h_
#define AccGroupInfo_h_
#include "Accessible-inl.h"
namespace mozilla {
namespace a11y {
/**
* Calculate and store group information.
*/
class AccGroupInfo
{
public:
~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
/**
* Return 1-based position in the group.
*/
uint32_t PosInSet() const { return mPosInSet; }
/**
* Return a number of items in the group.
*/
uint32_t SetSize() const { return mSetSize; }
/**
* Return a direct or logical parent of the accessible that this group info is
* created for.
*/
Accessible* ConceptualParent() const { return mParent; }
/**
* Update group information.
*/
void Update();
/**
* Create group info.
*/
static AccGroupInfo* CreateGroupInfo(Accessible* aAccessible)
{
mozilla::a11y::role role = aAccessible->Role();
if (role != mozilla::a11y::roles::ROW &&
role != mozilla::a11y::roles::OUTLINEITEM &&
role != mozilla::a11y::roles::OPTION &&
role != mozilla::a11y::roles::LISTITEM &&
role != mozilla::a11y::roles::MENUITEM &&
role != mozilla::a11y::roles::COMBOBOX_OPTION &&
role != mozilla::a11y::roles::RICH_OPTION &&
role != mozilla::a11y::roles::CHECK_RICH_OPTION &&
role != mozilla::a11y::roles::PARENT_MENUITEM &&
role != mozilla::a11y::roles::CHECK_MENU_ITEM &&
role != mozilla::a11y::roles::RADIO_MENU_ITEM &&
role != mozilla::a11y::roles::RADIOBUTTON &&
role != mozilla::a11y::roles::PAGETAB)
return nullptr;
AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
return info;
}
/**
* Return a first item for the given container.
*/
static Accessible* FirstItemOf(Accessible* aContainer);
/**
* Return next item of the same group to the given item.
*/
static Accessible* NextItemTo(Accessible* aItem);
protected:
AccGroupInfo(Accessible* aItem, a11y::role aRole);
private:
AccGroupInfo() = delete;
AccGroupInfo(const AccGroupInfo&) = delete;
AccGroupInfo& operator =(const AccGroupInfo&) = delete;
static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
{
if (aRole == mozilla::a11y::roles::CHECK_MENU_ITEM ||
aRole == mozilla::a11y::roles::PARENT_MENUITEM ||
aRole == mozilla::a11y::roles::RADIO_MENU_ITEM)
return mozilla::a11y::roles::MENUITEM;
if (aRole == mozilla::a11y::roles::CHECK_RICH_OPTION)
return mozilla::a11y::roles::RICH_OPTION;
return aRole;
}
/**
* Return true if the given parent and child roles should have their node
* relations reported.
*/
static bool ShouldReportRelations(a11y::role aRole, a11y::role aParentRole);
uint32_t mPosInSet;
uint32_t mSetSize;
Accessible* mParent;
Accessible* mItem;
a11y::role mRole;
};
} // namespace mozilla
} // namespace a11y
#endif

View file

@ -0,0 +1,414 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccIterator.h"
#include "AccGroupInfo.h"
#ifdef MOZ_XUL
#include "XULTreeAccessible.h"
#endif
#include "mozilla/dom/HTMLLabelElement.h"
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// AccIterator
////////////////////////////////////////////////////////////////////////////////
AccIterator::AccIterator(Accessible* aAccessible,
filters::FilterFuncPtr aFilterFunc) :
mFilterFunc(aFilterFunc)
{
mState = new IteratorState(aAccessible);
}
AccIterator::~AccIterator()
{
while (mState) {
IteratorState *tmp = mState;
mState = tmp->mParentState;
delete tmp;
}
}
Accessible*
AccIterator::Next()
{
while (mState) {
Accessible* child = mState->mParent->GetChildAt(mState->mIndex++);
if (!child) {
IteratorState* tmp = mState;
mState = mState->mParentState;
delete tmp;
continue;
}
uint32_t result = mFilterFunc(child);
if (result & filters::eMatch)
return child;
if (!(result & filters::eSkipSubtree)) {
IteratorState* childState = new IteratorState(child, mState);
mState = childState;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccIterator::IteratorState
AccIterator::IteratorState::IteratorState(Accessible* aParent,
IteratorState *mParentState) :
mParent(aParent), mIndex(0), mParentState(mParentState)
{
}
////////////////////////////////////////////////////////////////////////////////
// RelatedAccIterator
////////////////////////////////////////////////////////////////////////////////
RelatedAccIterator::
RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent,
nsIAtom* aRelAttr) :
mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nullptr),
mBindingParent(nullptr), mIndex(0)
{
mBindingParent = aDependentContent->GetBindingParent();
nsIAtom* IDAttr = mBindingParent ?
nsGkAtoms::anonid : nsGkAtoms::id;
nsAutoString id;
if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
mProviders = mDocument->mDependentIDsHash.Get(id);
}
Accessible*
RelatedAccIterator::Next()
{
if (!mProviders)
return nullptr;
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.
if (provider->mRelAttr == mRelAttr) {
nsIContent* bindingParent = provider->mContent->GetBindingParent();
bool inScope = mBindingParent == bindingParent ||
mBindingParent == provider->mContent;
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;
}
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLLabelIterator
////////////////////////////////////////////////////////////////////////////////
HTMLLabelIterator::
HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible,
LabelFilter aFilter) :
mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for),
mAcc(aAccessible), mLabelFilter(aFilter)
{
}
bool
HTMLLabelIterator::IsLabel(Accessible* aLabel)
{
dom::HTMLLabelElement* labelEl =
dom::HTMLLabelElement::FromContent(aLabel->GetContent());
return labelEl && labelEl->GetControl() == mAcc->GetContent();
}
Accessible*
HTMLLabelIterator::Next()
{
// Get either <label for="[id]"> element which explicitly points to given
// element, or <label> ancestor which implicitly point to it.
Accessible* label = nullptr;
while ((label = mRelIter.Next())) {
if (IsLabel(label)) {
return label;
}
}
// Ignore ancestor label on not widget accessible.
if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget())
return nullptr;
// Go up tree to get a name of ancestor label if there is one (an ancestor
// <label> implicitly points to us). Don't go up farther than form or
// document.
Accessible* walkUp = mAcc->Parent();
while (walkUp && !walkUp->IsDoc()) {
nsIContent* walkUpEl = walkUp->GetContent();
if (IsLabel(walkUp) &&
!walkUpEl->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
return walkUp;
}
if (walkUpEl->IsHTMLElement(nsGkAtoms::form))
break;
walkUp = walkUp->Parent();
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLOutputIterator
////////////////////////////////////////////////////////////////////////////////
HTMLOutputIterator::
HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement) :
mRelIter(aDocument, aElement, nsGkAtoms::_for)
{
}
Accessible*
HTMLOutputIterator::Next()
{
Accessible* output = nullptr;
while ((output = mRelIter.Next())) {
if (output->GetContent()->IsHTMLElement(nsGkAtoms::output))
return output;
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// XULLabelIterator
////////////////////////////////////////////////////////////////////////////////
XULLabelIterator::
XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement) :
mRelIter(aDocument, aElement, nsGkAtoms::control)
{
}
Accessible*
XULLabelIterator::Next()
{
Accessible* label = nullptr;
while ((label = mRelIter.Next())) {
if (label->GetContent()->IsXULElement(nsGkAtoms::label))
return label;
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// XULDescriptionIterator
////////////////////////////////////////////////////////////////////////////////
XULDescriptionIterator::
XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement) :
mRelIter(aDocument, aElement, nsGkAtoms::control)
{
}
Accessible*
XULDescriptionIterator::Next()
{
Accessible* descr = nullptr;
while ((descr = mRelIter.Next())) {
if (descr->GetContent()->IsXULElement(nsGkAtoms::description))
return descr;
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// IDRefsIterator
////////////////////////////////////////////////////////////////////////////////
IDRefsIterator::
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsIAtom* aIDRefsAttr) :
mContent(aContent), mDoc(aDoc), mCurrIdx(0)
{
if (mContent->IsInUncomposedDoc())
mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
}
const nsDependentSubstring
IDRefsIterator::NextID()
{
for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
break;
}
if (mCurrIdx >= mIDs.Length())
return nsDependentSubstring();
nsAString::index_type idStartIdx = mCurrIdx;
while (++mCurrIdx < mIDs.Length()) {
if (NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
break;
}
return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx);
}
nsIContent*
IDRefsIterator::NextElem()
{
while (true) {
const nsDependentSubstring id = NextID();
if (id.IsEmpty())
break;
nsIContent* refContent = GetElem(id);
if (refContent)
return refContent;
}
return nullptr;
}
nsIContent*
IDRefsIterator::GetElem(const nsDependentSubstring& 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()) {
dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID);
if (refElm || !mContent->GetXBLBinding())
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;
}
Accessible*
IDRefsIterator::Next()
{
nsIContent* nextEl = nullptr;
while ((nextEl = NextElem())) {
Accessible* acc = mDoc->GetAccessible(nextEl);
if (acc) {
return acc;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// SingleAccIterator
////////////////////////////////////////////////////////////////////////////////
Accessible*
SingleAccIterator::Next()
{
RefPtr<Accessible> nextAcc;
mAcc.swap(nextAcc);
if (!nextAcc || nextAcc->IsDefunct()) {
return nullptr;
}
return nextAcc;
}
////////////////////////////////////////////////////////////////////////////////
// ItemIterator
////////////////////////////////////////////////////////////////////////////////
Accessible*
ItemIterator::Next()
{
if (mContainer) {
mAnchor = AccGroupInfo::FirstItemOf(mContainer);
mContainer = nullptr;
return mAnchor;
}
return mAnchor ? (mAnchor = AccGroupInfo::NextItemTo(mAnchor)) : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemIterator
////////////////////////////////////////////////////////////////////////////////
XULTreeItemIterator::XULTreeItemIterator(XULTreeAccessible* aXULTree,
nsITreeView* aTreeView,
int32_t aRowIdx) :
mXULTree(aXULTree), mTreeView(aTreeView), mRowCount(-1),
mContainerLevel(-1), mCurrRowIdx(aRowIdx + 1)
{
mTreeView->GetRowCount(&mRowCount);
if (aRowIdx != -1)
mTreeView->GetLevel(aRowIdx, &mContainerLevel);
}
Accessible*
XULTreeItemIterator::Next()
{
while (mCurrRowIdx < mRowCount) {
int32_t level = 0;
mTreeView->GetLevel(mCurrRowIdx, &level);
if (level == mContainerLevel + 1)
return mXULTree->GetTreeItemAccessible(mCurrRowIdx++);
if (level <= mContainerLevel) { // got level up
mCurrRowIdx = mRowCount;
break;
}
mCurrRowIdx++;
}
return nullptr;
}

View file

@ -0,0 +1,325 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_AccIterator_h__
#define mozilla_a11y_AccIterator_h__
#include "DocAccessible.h"
#include "Filters.h"
#include <memory>
class nsITreeView;
namespace mozilla {
namespace a11y {
/**
* AccIterable is a basic interface for iterators over accessibles.
*/
class AccIterable
{
public:
virtual ~AccIterable() { }
virtual Accessible* Next() = 0;
private:
friend class Relation;
std::unique_ptr<AccIterable> mNextIter;
};
/**
* Allows to iterate through accessible children or subtree complying with
* filter function.
*/
class AccIterator : public AccIterable
{
public:
AccIterator(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
virtual ~AccIterator();
/**
* Return next accessible complying with filter function. Return the first
* accessible for the first time.
*/
virtual Accessible* Next() override;
private:
AccIterator();
AccIterator(const AccIterator&);
AccIterator& operator =(const AccIterator&);
struct IteratorState
{
explicit IteratorState(Accessible* aParent, IteratorState* mParentState = nullptr);
Accessible* mParent;
int32_t mIndex;
IteratorState* mParentState;
};
filters::FilterFuncPtr mFilterFunc;
IteratorState* mState;
};
/**
* Allows to traverse through related accessibles that are pointing to the given
* dependent accessible by relation attribute.
*/
class RelatedAccIterator : public AccIterable
{
public:
/**
* Constructor.
*
* @param aDocument [in] the document accessible the related
* & accessibles belong to.
* @param aDependentContent [in] the content of dependent accessible that
* relations were requested for
* @param aRelAttr [in] relation attribute that relations are
* pointed by
*/
RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent,
nsIAtom* aRelAttr);
virtual ~RelatedAccIterator() { }
/**
* Return next related accessible for the given dependent accessible.
*/
virtual Accessible* Next() override;
private:
RelatedAccIterator();
RelatedAccIterator(const RelatedAccIterator&);
RelatedAccIterator& operator = (const RelatedAccIterator&);
DocAccessible* mDocument;
nsIAtom* mRelAttr;
DocAccessible::AttrRelProviderArray* mProviders;
nsIContent* mBindingParent;
uint32_t mIndex;
};
/**
* Used to iterate through HTML labels associated with the given accessible.
*/
class HTMLLabelIterator : public AccIterable
{
public:
enum LabelFilter {
eAllLabels,
eSkipAncestorLabel
};
HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible,
LabelFilter aFilter = eAllLabels);
virtual ~HTMLLabelIterator() { }
/**
* Return next label accessible associated with the given element.
*/
virtual Accessible* Next() override;
private:
HTMLLabelIterator();
HTMLLabelIterator(const HTMLLabelIterator&);
HTMLLabelIterator& operator = (const HTMLLabelIterator&);
bool IsLabel(Accessible* aLabel);
RelatedAccIterator mRelIter;
// XXX: replace it on weak reference (bug 678429), it's safe to use raw
// pointer now because iterators life cycle is short.
const Accessible* mAcc;
LabelFilter mLabelFilter;
};
/**
* Used to iterate through HTML outputs associated with the given element.
*/
class HTMLOutputIterator : public AccIterable
{
public:
HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement);
virtual ~HTMLOutputIterator() { }
/**
* Return next output accessible associated with the given element.
*/
virtual Accessible* Next() override;
private:
HTMLOutputIterator();
HTMLOutputIterator(const HTMLOutputIterator&);
HTMLOutputIterator& operator = (const HTMLOutputIterator&);
RelatedAccIterator mRelIter;
};
/**
* Used to iterate through XUL labels associated with the given element.
*/
class XULLabelIterator : public AccIterable
{
public:
XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement);
virtual ~XULLabelIterator() { }
/**
* Return next label accessible associated with the given element.
*/
virtual Accessible* Next() override;
private:
XULLabelIterator();
XULLabelIterator(const XULLabelIterator&);
XULLabelIterator& operator = (const XULLabelIterator&);
RelatedAccIterator mRelIter;
};
/**
* Used to iterate through XUL descriptions associated with the given element.
*/
class XULDescriptionIterator : public AccIterable
{
public:
XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement);
virtual ~XULDescriptionIterator() { }
/**
* Return next description accessible associated with the given element.
*/
virtual Accessible* Next() override;
private:
XULDescriptionIterator();
XULDescriptionIterator(const XULDescriptionIterator&);
XULDescriptionIterator& operator = (const XULDescriptionIterator&);
RelatedAccIterator mRelIter;
};
/**
* Used to iterate through IDs, elements or accessibles pointed by IDRefs
* attribute. Note, any method used to iterate through IDs, elements, or
* accessibles moves iterator to next position.
*/
class IDRefsIterator : public AccIterable
{
public:
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsIAtom* aIDRefsAttr);
virtual ~IDRefsIterator() { }
/**
* Return next ID.
*/
const nsDependentSubstring NextID();
/**
* Return next element.
*/
nsIContent* NextElem();
/**
* Return the element with the given ID.
*/
nsIContent* GetElem(const nsDependentSubstring& aID);
// AccIterable
virtual Accessible* Next() override;
private:
IDRefsIterator();
IDRefsIterator(const IDRefsIterator&);
IDRefsIterator operator = (const IDRefsIterator&);
nsString mIDs;
nsIContent* mContent;
DocAccessible* mDoc;
nsAString::index_type mCurrIdx;
};
/**
* Iterator that points to a single accessible returning it on the first call
* to Next().
*/
class SingleAccIterator : public AccIterable
{
public:
explicit SingleAccIterator(Accessible* aTarget): mAcc(aTarget) { }
virtual ~SingleAccIterator() { }
virtual Accessible* Next() override;
private:
SingleAccIterator();
SingleAccIterator(const SingleAccIterator&);
SingleAccIterator& operator = (const SingleAccIterator&);
RefPtr<Accessible> mAcc;
};
/**
* Used to iterate items of the given item container.
*/
class ItemIterator : public AccIterable
{
public:
explicit ItemIterator(Accessible* aItemContainer) :
mContainer(aItemContainer), mAnchor(nullptr) { }
virtual ~ItemIterator() { }
virtual Accessible* Next() override;
private:
ItemIterator() = delete;
ItemIterator(const ItemIterator&) = delete;
ItemIterator& operator = (const ItemIterator&) = delete;
Accessible* mContainer;
Accessible* mAnchor;
};
/**
* Used to iterate through XUL tree items of the same level.
*/
class XULTreeItemIterator : public AccIterable
{
public:
XULTreeItemIterator(XULTreeAccessible* aXULTree, nsITreeView* aTreeView,
int32_t aRowIdx);
virtual ~XULTreeItemIterator() { }
virtual Accessible* Next() override;
private:
XULTreeItemIterator() = delete;
XULTreeItemIterator(const XULTreeItemIterator&) = delete;
XULTreeItemIterator& operator = (const XULTreeItemIterator&) = delete;
XULTreeAccessible* mXULTree;
nsITreeView* mTreeView;
int32_t mRowCount;
int32_t mContainerLevel;
int32_t mCurrRowIdx;
};
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_AccTypes_h
#define mozilla_a11y_AccTypes_h
namespace mozilla {
namespace a11y {
/**
* Accessible object types. Each accessible class can have own type.
*/
enum AccType {
/**
* This set of types is used for accessible creation, keep them together in
* alphabetical order since they are used in switch statement.
*/
eNoType,
eHTMLBRType,
eHTMLButtonType,
eHTMLCanvasType,
eHTMLCaptionType,
eHTMLCheckboxType,
eHTMLComboboxType,
eHTMLFileInputType,
eHTMLGroupboxType,
eHTMLHRType,
eHTMLImageMapType,
eHTMLLiType,
eHTMLSelectListType,
eHTMLMediaType,
eHTMLRadioButtonType,
eHTMLRangeType,
eHTMLSpinnerType,
eHTMLTableType,
eHTMLTableCellType,
eHTMLTableRowType,
eHTMLTextFieldType,
eHyperTextType,
eImageType,
eOuterDocType,
ePluginType,
eTextLeafType,
/**
* Other accessible types.
*/
eApplicationType,
eHTMLOptGroupType,
eImageMapType,
eMenuPopupType,
eProxyType,
eProgressType,
eRootType,
eXULLabelType,
eXULListItemType,
eXULTabpanelsType,
eXULTreeType,
eLastAccType = eXULTreeType
};
/**
* Generic accessible type, different accessible classes can share the same
* type, the same accessible class can have several types.
*/
enum AccGenericType {
eAlert = 1 << 0,
eAutoComplete = 1 << 1,
eAutoCompletePopup = 1 << 2,
eButton = 1 << 3,
eCombobox = 1 << 4,
eDocument = 1 << 5,
eHyperText = 1 << 6,
eLandmark = 1 << 7,
eList = 1 << 8,
eListControl = 1 << 9,
eMenuButton = 1 << 10,
eSelect = 1 << 11,
eTable = 1 << 12,
eTableCell = 1 << 13,
eTableRow = 1 << 14,
eText = 1 << 15,
eLastAccGenericType = eText
};
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11y_AccTypes_h

View file

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AccessibleOrProxy.h"
AccessibleOrProxy
AccessibleOrProxy::Parent() const
{
if (IsAccessible()) {
return AsAccessible()->Parent();
}
ProxyAccessible* proxy = AsProxy();
if (!proxy) {
return nullptr;
}
if (ProxyAccessible* parent = proxy->Parent()) {
return parent;
}
// Otherwise this should be the proxy for the tab's top level document.
return proxy->OuterDocOfRemoteBrowser();
}

View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_AccessibleOrProxy_h
#define mozilla_a11y_AccessibleOrProxy_h
#include "mozilla/a11y/Accessible.h"
#include "mozilla/a11y/ProxyAccessible.h"
#include "mozilla/a11y/Role.h"
#include <stdint.h>
namespace mozilla {
namespace a11y {
/**
* This class stores an Accessible* or a ProxyAccessible* in a safe manner
* with size sizeof(void*).
*/
class AccessibleOrProxy
{
public:
MOZ_IMPLICIT AccessibleOrProxy(Accessible* aAcc) :
mBits(reinterpret_cast<uintptr_t>(aAcc)) {}
MOZ_IMPLICIT AccessibleOrProxy(ProxyAccessible* aProxy) :
mBits(aProxy ? (reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY) : 0) {}
MOZ_IMPLICIT AccessibleOrProxy(decltype(nullptr)) : mBits(0) {}
bool IsProxy() const { return mBits & IS_PROXY; }
ProxyAccessible* AsProxy() const
{
if (IsProxy()) {
return reinterpret_cast<ProxyAccessible*>(mBits & ~IS_PROXY);
}
return nullptr;
}
bool IsAccessible() const { return !IsProxy(); }
Accessible* AsAccessible() const
{
if (IsAccessible()) {
return reinterpret_cast<Accessible*>(mBits);
}
return nullptr;
}
bool IsNull() const { return mBits == 0; }
uint32_t ChildCount() const
{
if (IsProxy()) {
return AsProxy()->ChildrenCount();
}
return AsAccessible()->ChildCount();
}
/**
* Return the child object either an accessible or a proxied accessible at
* the given index.
*/
AccessibleOrProxy ChildAt(uint32_t aIdx)
{
if (IsProxy()) {
return AsProxy()->ChildAt(aIdx);
}
return AsAccessible()->GetChildAt(aIdx);
}
/**
* Return the first child object.
*/
AccessibleOrProxy FirstChild()
{
if (IsProxy()) {
return AsProxy()->FirstChild();
}
return AsAccessible()->FirstChild();
}
/**
* Return the first child object.
*/
AccessibleOrProxy LastChild()
{
if (IsProxy()) {
return AsProxy()->LastChild();
}
return AsAccessible()->LastChild();
}
role Role() const
{
if (IsProxy()) {
return AsProxy()->Role();
}
return AsAccessible()->Role();
}
AccessibleOrProxy Parent() const;
// XXX these are implementation details that ideally would not be exposed.
uintptr_t Bits() const { return mBits; }
void SetBits(uintptr_t aBits) { mBits = aBits; }
private:
uintptr_t mBits;
static const uintptr_t IS_PROXY = 0x1;
};
}
}
#endif

View file

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIAccessibleRelation.h"
#include "nsIAccessibleRole.h"
#include "RelationType.h"
#include "Role.h"
using namespace mozilla::a11y;
#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
static_assert(static_cast<uint32_t>(roles::geckoRole) \
== static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \
"internal and xpcom roles differ!");
#include "RoleMap.h"
#undef ROLE
#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
static_assert(static_cast<uint32_t>(RelationType::geckoType) \
== static_cast<uint32_t>(nsIAccessibleRelation::RELATION_ ## geckoType), \
"internal and xpcom relations differ!");
#include "RelationTypeMap.h"
#undef RELATIONTYPE

View file

@ -0,0 +1,594 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocManager.h"
#include "ApplicationAccessible.h"
#include "ARIAMap.h"
#include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "DocAccessibleParent.h"
#include "nsAccessibilityService.h"
#include "Platform.h"
#include "RootAccessibleWrap.h"
#include "xpcAccessibleDocument.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
#include "mozilla/EventListenerManager.h"
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsCURILoader.h"
#include "nsDocShellLoadTypes.h"
#include "nsIChannel.h"
#include "nsIDOMDocument.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIWebNavigation.h"
#include "nsServiceManagerUtils.h"
#include "nsIWebProgress.h"
#include "nsCoreUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/dom/TabChild.h"
using namespace mozilla;
using namespace mozilla::a11y;
using namespace mozilla::dom;
StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments;
nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
DocManager::sRemoteXPCDocumentCache = nullptr;
////////////////////////////////////////////////////////////////////////////////
// DocManager
////////////////////////////////////////////////////////////////////////////////
DocManager::DocManager()
: mDocAccessibleCache(2), mXPCDocumentCache(0)
{
}
////////////////////////////////////////////////////////////////////////////////
// DocManager public
DocAccessible*
DocManager::GetDocAccessible(nsIDocument* aDocument)
{
if (!aDocument)
return nullptr;
DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
if (docAcc)
return docAcc;
return CreateDocOrRootAccessible(aDocument);
}
Accessible*
DocManager::FindAccessibleInCache(nsINode* aNode) const
{
for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
DocAccessible* docAccessible = iter.UserData();
NS_ASSERTION(docAccessible,
"No doc accessible for the object in doc accessible cache!");
if (docAccessible) {
Accessible* accessible = docAccessible->GetAccessible(aNode);
if (accessible) {
return accessible;
}
}
}
return nullptr;
}
void
DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
nsIDocument* aDOMDocument)
{
// We need to remove listeners in both cases, when document is being shutdown
// or when accessibility service is being shut down as well.
RemoveListeners(aDOMDocument);
// Document will already be removed when accessibility service is shutting
// down so we do not need to remove it twice.
if (nsAccessibilityService::IsShutdown()) {
return;
}
xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
if (xpcDoc) {
xpcDoc->Shutdown();
mXPCDocumentCache.Remove(aDocument);
}
mDocAccessibleCache.Remove(aDOMDocument);
}
void
DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc)
{
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
if (doc) {
doc->Shutdown();
sRemoteXPCDocumentCache->Remove(aDoc);
}
}
xpcAccessibleDocument*
DocManager::GetXPCDocument(DocAccessible* aDocument)
{
if (!aDocument)
return nullptr;
xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
if (!xpcDoc) {
xpcDoc = new xpcAccessibleDocument(aDocument);
mXPCDocumentCache.Put(aDocument, xpcDoc);
}
return xpcDoc;
}
xpcAccessibleDocument*
DocManager::GetXPCDocument(DocAccessibleParent* aDoc)
{
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
if (doc) {
return doc;
}
if (!sRemoteXPCDocumentCache) {
sRemoteXPCDocumentCache =
new nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>;
}
doc =
new xpcAccessibleDocument(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
sRemoteXPCDocumentCache->Put(aDoc, doc);
return doc;
}
#ifdef DEBUG
bool
DocManager::IsProcessingRefreshDriverNotification() const
{
for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
DocAccessible* docAccessible = iter.UserData();
NS_ASSERTION(docAccessible,
"No doc accessible for the object in doc accessible cache!");
if (docAccessible && docAccessible->mNotificationController &&
docAccessible->mNotificationController->IsUpdating()) {
return true;
}
}
return false;
}
#endif
////////////////////////////////////////////////////////////////////////////////
// DocManager protected
bool
DocManager::Init()
{
nsCOMPtr<nsIWebProgress> progress =
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
if (!progress)
return false;
progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
return true;
}
void
DocManager::Shutdown()
{
nsCOMPtr<nsIWebProgress> progress =
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
if (progress)
progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
ClearDocCache();
}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
NS_IMPL_ISUPPORTS(DocManager,
nsIWebProgressListener,
nsIDOMEventListener,
nsISupportsWeakReference)
////////////////////////////////////////////////////////////////////////////////
// nsIWebProgressListener
NS_IMETHODIMP
DocManager::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, uint32_t aStateFlags,
nsresult aStatus)
{
NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
if (nsAccessibilityService::IsShutdown() || !aWebProgress ||
(aStateFlags & (STATE_START | STATE_STOP)) == 0)
return NS_OK;
nsCOMPtr<mozIDOMWindowProxy> DOMWindow;
aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));
NS_ENSURE_STATE(DOMWindow);
nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(DOMWindow);
MOZ_ASSERT(piWindow);
nsCOMPtr<nsIDocument> document = piWindow->GetDoc();
NS_ENSURE_STATE(document);
// Document was loaded.
if (aStateFlags & STATE_STOP) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocLoad))
logging::DocLoad("document loaded", aWebProgress, aRequest, aStateFlags);
#endif
// Figure out an event type to notify the document has been loaded.
uint32_t eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
// Some XUL documents get start state and then stop state with failure
// status when everything is ok. Fire document load complete event in this
// case.
if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document))
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
// If end consumer has been retargeted for loaded content then do not fire
// any event because it means no new document has been loaded, for example,
// it happens when user clicks on file link.
if (aRequest) {
uint32_t loadFlags = 0;
aRequest->GetLoadFlags(&loadFlags);
if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
eventType = 0;
}
HandleDOMDocumentLoad(document, eventType);
return NS_OK;
}
// Document loading was started.
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocLoad))
logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags);
#endif
DocAccessible* docAcc = GetExistingDocAccessible(document);
if (!docAcc)
return NS_OK;
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
NS_ENSURE_STATE(docShell);
bool isReloading = false;
uint32_t loadType;
docShell->GetLoadType(&loadType);
if (loadType == LOAD_RELOAD_NORMAL ||
loadType == LOAD_RELOAD_BYPASS_CACHE ||
loadType == LOAD_RELOAD_BYPASS_PROXY ||
loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
loadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
isReloading = true;
}
docAcc->NotifyOfLoading(isReloading);
return NS_OK;
}
NS_IMETHODIMP
DocManager::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
NS_IMETHODIMP
DocManager::OnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, nsIURI* aLocation,
uint32_t aFlags)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
NS_IMETHODIMP
DocManager::OnStatusChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, nsresult aStatus,
const char16_t* aMessage)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
NS_IMETHODIMP
DocManager::OnSecurityChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aState)
{
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsIDOMEventListener
NS_IMETHODIMP
DocManager::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
nsCOMPtr<nsIDocument> document =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!");
if (!document)
return NS_OK;
if (type.EqualsLiteral("pagehide")) {
// 'pagehide' event is registered on every DOM document we create an
// accessible for, process the event for the target. This document
// accessible and all its sub document accessible are shutdown as result of
// processing.
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocDestroy))
logging::DocDestroy("received 'pagehide' event", document);
#endif
// Shutdown this one and sub document accessibles.
// We're allowed to not remove listeners when accessible document is
// shutdown since we don't keep strong reference on chrome event target and
// listeners are removed automatically when chrome event target goes away.
DocAccessible* docAccessible = GetExistingDocAccessible(document);
if (docAccessible)
docAccessible->Shutdown();
return NS_OK;
}
// XXX: handle error pages loading separately since they get neither
// webprogress notifications nor 'pageshow' event.
if (type.EqualsLiteral("DOMContentLoaded") &&
nsCoreUtils::IsErrorPage(document)) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocLoad))
logging::DocLoad("handled 'DOMContentLoaded' event", document);
#endif
HandleDOMDocumentLoad(document,
nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// DocManager private
void
DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument,
uint32_t aLoadEventType)
{
// Document accessible can be created before we were notified the DOM document
// was loaded completely. However if it's not created yet then create it.
DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
if (!docAcc) {
docAcc = CreateDocOrRootAccessible(aDocument);
if (!docAcc)
return;
}
docAcc->NotifyOfLoad(aLoadEventType);
}
void
DocManager::AddListeners(nsIDocument* aDocument,
bool aAddDOMContentLoadedListener)
{
nsPIDOMWindowOuter* window = aDocument->GetWindow();
EventTarget* target = window->GetChromeEventHandler();
EventListenerManager* elm = target->GetOrCreateListenerManager();
elm->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
TrustedEventsAtCapture());
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocCreate))
logging::Text("added 'pagehide' listener");
#endif
if (aAddDOMContentLoadedListener) {
elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
TrustedEventsAtCapture());
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocCreate))
logging::Text("added 'DOMContentLoaded' listener");
#endif
}
}
void
DocManager::RemoveListeners(nsIDocument* aDocument)
{
nsPIDOMWindowOuter* window = aDocument->GetWindow();
if (!window)
return;
EventTarget* target = window->GetChromeEventHandler();
if (!target)
return;
EventListenerManager* elm = target->GetOrCreateListenerManager();
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
TrustedEventsAtCapture());
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
TrustedEventsAtCapture());
}
DocAccessible*
DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
{
// Ignore hiding, resource documents and documents without docshell.
if (!aDocument->IsVisibleConsideringAncestors() ||
aDocument->IsResourceDoc() || !aDocument->IsActive())
return nullptr;
// Ignore documents without presshell and not having root frame.
nsIPresShell* presShell = aDocument->GetShell();
if (!presShell || presShell->IsDestroying())
return nullptr;
bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
DocAccessible* parentDocAcc = nullptr;
if (!isRootDoc) {
// XXXaaronl: ideally we would traverse the presshell chain. Since there's
// no easy way to do that, we cheat and use the document hierarchy.
parentDocAcc = GetDocAccessible(aDocument->GetParentDocument());
NS_ASSERTION(parentDocAcc,
"Can't create an accessible for the document!");
if (!parentDocAcc)
return nullptr;
}
// We only create root accessibles for the true root, otherwise create a
// doc accessible.
RefPtr<DocAccessible> docAcc = isRootDoc ?
new RootAccessibleWrap(aDocument, presShell) :
new DocAccessibleWrap(aDocument, presShell);
// Cache the document accessible into document cache.
mDocAccessibleCache.Put(aDocument, docAcc);
// Initialize the document accessible.
docAcc->Init();
// Bind the document to the tree.
if (isRootDoc) {
if (!ApplicationAcc()->AppendChild(docAcc)) {
docAcc->Shutdown();
return nullptr;
}
// Fire reorder event to notify new accessible document has been attached to
// the tree. The reorder event is delivered after the document tree is
// constructed because event processing and tree construction are done by
// the same document.
// Note: don't use AccReorderEvent to avoid coalsecense and special reorder
// events processing.
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
ApplicationAcc());
if (IPCAccessibilityActive()) {
nsIDocShell* docShell = aDocument->GetDocShell();
if (docShell) {
nsCOMPtr<nsITabChild> tabChild = docShell->GetTabChild();
// XXX We may need to handle the case that we don't have a tab child
// differently. It may be that this will cause us to fail to notify
// the parent process about important accessible documents.
if (tabChild) {
DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
docAcc->SetIPCDoc(ipcDoc);
#if defined(XP_WIN)
IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
#endif
static_cast<TabChild*>(tabChild.get())->
SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
#if defined(XP_WIN)
AccessibleWrap::GetChildIDFor(docAcc),
holder
#else
0, 0
#endif
);
}
}
}
} else {
parentDocAcc->BindChildDocument(docAcc);
}
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocCreate)) {
logging::DocCreate("document creation finished", aDocument);
logging::Stack();
}
#endif
AddListeners(aDocument, isRootDoc);
return docAcc;
}
////////////////////////////////////////////////////////////////////////////////
// DocManager static
void
DocManager::ClearDocCache()
{
while (mDocAccessibleCache.Count() > 0) {
auto iter = mDocAccessibleCache.Iter();
MOZ_ASSERT(!iter.Done());
DocAccessible* docAcc = iter.UserData();
NS_ASSERTION(docAcc,
"No doc accessible for the object in doc accessible cache!");
if (docAcc) {
docAcc->Shutdown();
}
iter.Remove();
}
// Ensure that all xpcom accessible documents are shut down as well.
while (mXPCDocumentCache.Count() > 0) {
auto iter = mXPCDocumentCache.Iter();
MOZ_ASSERT(!iter.Done());
xpcAccessibleDocument* xpcDoc = iter.UserData();
NS_ASSERTION(xpcDoc, "No xpc doc for the object in xpc doc cache!");
if (xpcDoc) {
xpcDoc->Shutdown();
}
iter.Remove();
}
}
void
DocManager::RemoteDocAdded(DocAccessibleParent* aDoc)
{
if (!sRemoteDocuments) {
sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
ClearOnShutdown(&sRemoteDocuments);
}
MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc),
"How did we already have the doc!");
sRemoteDocuments->AppendElement(aDoc);
ProxyCreated(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
}

View file

@ -0,0 +1,189 @@
/* 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_a11_DocManager_h_
#define mozilla_a11_DocManager_h_
#include "mozilla/ClearOnShutdown.h"
#include "nsIDocument.h"
#include "nsIDOMEventListener.h"
#include "nsRefPtrHashtable.h"
#include "nsIWebProgressListener.h"
#include "nsWeakReference.h"
#include "nsIPresShell.h"
#include "mozilla/StaticPtr.h"
namespace mozilla {
namespace a11y {
class Accessible;
class DocAccessible;
class xpcAccessibleDocument;
class DocAccessibleParent;
/**
* Manage the document accessible life cycle.
*/
class DocManager : public nsIWebProgressListener,
public nsIDOMEventListener,
public nsSupportsWeakReference
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWEBPROGRESSLISTENER
NS_DECL_NSIDOMEVENTLISTENER
/**
* Return document accessible for the given DOM node.
*/
DocAccessible* GetDocAccessible(nsIDocument* aDocument);
/**
* Return document accessible for the given presshell.
*/
DocAccessible* GetDocAccessible(const nsIPresShell* aPresShell)
{
if (!aPresShell)
return nullptr;
DocAccessible* doc = aPresShell->GetDocAccessible();
if (doc)
return doc;
return GetDocAccessible(aPresShell->GetDocument());
}
/**
* Search through all document accessibles for an accessible with the given
* unique id.
*/
Accessible* FindAccessibleInCache(nsINode* aNode) const;
/**
* Called by document accessible when it gets shutdown.
*/
void NotifyOfDocumentShutdown(DocAccessible* aDocument,
nsIDocument* aDOMDocument);
/**
* Return XPCOM accessible document.
*/
xpcAccessibleDocument* GetXPCDocument(DocAccessible* aDocument);
xpcAccessibleDocument* GetCachedXPCDocument(DocAccessible* aDocument) const
{ return mXPCDocumentCache.GetWeak(aDocument); }
/*
* Notification that a top level document in a content process has gone away.
*/
static void RemoteDocShutdown(DocAccessibleParent* aDoc)
{
DebugOnly<bool> result = sRemoteDocuments->RemoveElement(aDoc);
MOZ_ASSERT(result, "Why didn't we find the document!");
}
/*
* Notify of a new top level document in a content process.
*/
static void RemoteDocAdded(DocAccessibleParent* aDoc);
static const nsTArray<DocAccessibleParent*>* TopLevelRemoteDocs()
{ return sRemoteDocuments; }
/**
* Remove the xpc document for a remote document if there is one.
*/
static void NotifyOfRemoteDocShutdown(DocAccessibleParent* adoc);
/**
* Get a XPC document for a remote document.
*/
static xpcAccessibleDocument* GetXPCDocument(DocAccessibleParent* aDoc);
static xpcAccessibleDocument* GetCachedXPCDocument(const DocAccessibleParent* aDoc)
{
return sRemoteXPCDocumentCache ? sRemoteXPCDocumentCache->GetWeak(aDoc)
: nullptr;
}
#ifdef DEBUG
bool IsProcessingRefreshDriverNotification() const;
#endif
protected:
DocManager();
virtual ~DocManager() { }
/**
* Initialize the manager.
*/
bool Init();
/**
* Shutdown the manager.
*/
void Shutdown();
private:
DocManager(const DocManager&);
DocManager& operator =(const DocManager&);
private:
/**
* Create an accessible document if it was't created and fire accessibility
* events if needed.
*
* @param aDocument [in] loaded DOM document
* @param aLoadEventType [in] specifies the event type to fire load event,
* if 0 then no event is fired
*/
void HandleDOMDocumentLoad(nsIDocument* aDocument,
uint32_t aLoadEventType);
/**
* Add/remove 'pagehide' and 'DOMContentLoaded' event listeners.
*/
void AddListeners(nsIDocument *aDocument, bool aAddPageShowListener);
void RemoveListeners(nsIDocument* aDocument);
/**
* Create document or root accessible.
*/
DocAccessible* CreateDocOrRootAccessible(nsIDocument* aDocument);
/**
* Clear the cache and shutdown the document accessibles.
*/
void ClearDocCache();
typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, DocAccessible>
DocAccessibleHashtable;
DocAccessibleHashtable mDocAccessibleCache;
typedef nsRefPtrHashtable<nsPtrHashKey<const DocAccessible>, xpcAccessibleDocument>
XPCDocumentHashtable;
XPCDocumentHashtable mXPCDocumentCache;
static nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
sRemoteXPCDocumentCache;
/*
* The list of remote top level documents.
*/
static StaticAutoPtr<nsTArray<DocAccessibleParent*>> sRemoteDocuments;
};
/**
* Return the existing document accessible for the document if any.
* Note this returns the doc accessible for the primary pres shell if there is
* more than one.
*/
inline DocAccessible*
GetExistingDocAccessible(const nsIDocument* aDocument)
{
nsIPresShell* ps = aDocument->GetShell();
return ps ? ps->GetDocAccessible() : nullptr;
}
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11_DocManager_h_

View file

@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EmbeddedObjCollector.h"
#include "Accessible.h"
using namespace mozilla::a11y;
uint32_t
EmbeddedObjCollector::Count()
{
EnsureNGetIndex(nullptr);
return mObjects.Length();
}
Accessible*
EmbeddedObjCollector::GetAccessibleAt(uint32_t aIndex)
{
Accessible* accessible = mObjects.SafeElementAt(aIndex, nullptr);
if (accessible)
return accessible;
return EnsureNGetObject(aIndex);
}
Accessible*
EmbeddedObjCollector::EnsureNGetObject(uint32_t aIndex)
{
uint32_t childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (child->IsText())
continue;
AppendObject(child);
if (mObjects.Length() - 1 == aIndex)
return mObjects[aIndex];
}
return nullptr;
}
int32_t
EmbeddedObjCollector::EnsureNGetIndex(Accessible* aAccessible)
{
uint32_t childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (child->IsText())
continue;
AppendObject(child);
if (child == aAccessible)
return mObjects.Length() - 1;
}
return -1;
}
int32_t
EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
{
if (aAccessible->mParent != mRoot)
return -1;
MOZ_ASSERT(!aAccessible->IsProxy());
if (aAccessible->mInt.mIndexOfEmbeddedChild != -1)
return aAccessible->mInt.mIndexOfEmbeddedChild;
return !aAccessible->IsText() ? EnsureNGetIndex(aAccessible) : -1;
}
void
EmbeddedObjCollector::AppendObject(Accessible* aAccessible)
{
MOZ_ASSERT(!aAccessible->IsProxy());
aAccessible->mInt.mIndexOfEmbeddedChild = mObjects.Length();
mObjects.AppendElement(aAccessible);
}

View file

@ -0,0 +1,69 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_EmbeddedObjCollector_h__
#define mozilla_a11y_EmbeddedObjCollector_h__
#include "nsTArray.h"
namespace mozilla {
namespace a11y {
class Accessible;
/**
* Collect embedded objects. Provide quick access to accessible by index and
* vice versa.
*/
class EmbeddedObjCollector final
{
public:
~EmbeddedObjCollector() { }
/**
* Return index of the given accessible within the collection.
*/
int32_t GetIndexAt(Accessible* aAccessible);
/**
* Return accessible count within the collection.
*/
uint32_t Count();
/**
* Return an accessible from the collection at the given index.
*/
Accessible* GetAccessibleAt(uint32_t aIndex);
protected:
/**
* Ensure accessible at the given index is stored and return it.
*/
Accessible* EnsureNGetObject(uint32_t aIndex);
/**
* Ensure index for the given accessible is stored and return it.
*/
int32_t EnsureNGetIndex(Accessible* aAccessible);
// Make sure it's used by Accessible class only.
explicit EmbeddedObjCollector(Accessible* aRoot) :
mRoot(aRoot), mRootChildIdx(0) {}
/**
* Append the object to collection.
*/
void AppendObject(Accessible* aAccessible);
friend class Accessible;
Accessible* mRoot;
uint32_t mRootChildIdx;
nsTArray<Accessible*> mObjects;
};
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,344 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EventQueue.h"
#include "Accessible-inl.h"
#include "nsEventShell.h"
#include "DocAccessible.h"
#include "DocAccessibleChild.h"
#include "nsAccessibilityService.h"
#include "nsTextEquivUtils.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
using namespace mozilla;
using namespace mozilla::a11y;
// Defines the number of selection add/remove events in the queue when they
// aren't packed into single selection within event.
const unsigned int kSelChangeCountToPack = 5;
////////////////////////////////////////////////////////////////////////////////
// EventQueue
////////////////////////////////////////////////////////////////////////////////
bool
EventQueue::PushEvent(AccEvent* aEvent)
{
NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
aEvent->Document() == mDocument,
"Queued event belongs to another document!");
if (!mEvents.AppendElement(aEvent))
return false;
// Filter events.
CoalesceEvents();
if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
(aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED)) {
PushNameChange(aEvent->mAccessible);
}
return true;
}
bool
EventQueue::PushNameChange(Accessible* aTarget)
{
// Fire name change event on parent given that this event hasn't been
// coalesced, the parent's name was calculated from its subtree, and the
// subtree was changed.
if (aTarget->HasNameDependentParent()) {
// Only continue traversing up the tree if it's possible that the parent
// accessible's name can depend on this accessible's name.
Accessible* parent = aTarget->Parent();
while (parent &&
nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) {
// Test possible name dependent parent.
if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) {
nsAutoString name;
ENameValueFlag nameFlag = parent->Name(name);
// If name is obtained from subtree, fire name change event.
if (nameFlag == eNameFromSubtree) {
RefPtr<AccEvent> nameChangeEvent =
new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent);
return PushEvent(nameChangeEvent);
}
break;
}
parent = parent->Parent();
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// EventQueue: private
void
EventQueue::CoalesceEvents()
{
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
uint32_t tail = mEvents.Length() - 1;
AccEvent* tailEvent = mEvents[tail];
switch(tailEvent->mEventRule) {
case AccEvent::eCoalesceReorder:
{
DebugOnly<Accessible*> target = tailEvent->mAccessible.get();
MOZ_ASSERT(target->IsApplication() ||
target->IsOuterDoc() ||
target->IsXULTree(),
"Only app or outerdoc accessible reorder events are in the queue");
MOZ_ASSERT(tailEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER, "only reorder events should be queued");
break; // case eCoalesceReorder
}
case AccEvent::eCoalesceOfSameType:
{
// Coalesce old events by newer event.
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* accEvent = mEvents[index];
if (accEvent->mEventType == tailEvent->mEventType &&
accEvent->mEventRule == tailEvent->mEventRule) {
accEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
}
} break; // case eCoalesceOfSameType
case AccEvent::eCoalesceSelectionChange:
{
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* thisEvent = mEvents[index];
if (thisEvent->mEventRule == tailEvent->mEventRule) {
AccSelChangeEvent* thisSelChangeEvent =
downcast_accEvent(thisEvent);
// Coalesce selection change events within same control.
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
return;
}
}
}
} break; // eCoalesceSelectionChange
case AccEvent::eCoalesceStateChange:
{
// If state change event is duped then ignore previous event. If state
// change event is opposite to previous event then no event is emitted
// (accessible state wasn't changed).
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* thisEvent = mEvents[index];
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
thisEvent->mEventType == tailEvent->mEventType &&
thisEvent->mAccessible == tailEvent->mAccessible) {
AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
if (thisSCEvent->mState == tailSCEvent->mState) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
tailEvent->mEventRule = AccEvent::eDoNotEmit;
}
}
}
break; // eCoalesceStateChange
}
case AccEvent::eCoalesceTextSelChange:
{
// Coalesce older event by newer event for the same selection or target.
// Events for same selection may have different targets and vice versa one
// target may be pointed by different selections (for latter see
// bug 927159).
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* thisEvent = mEvents[index];
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
thisEvent->mEventType == tailEvent->mEventType) {
AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
thisEvent->mAccessible == tailEvent->mAccessible)
thisEvent->mEventRule = AccEvent::eDoNotEmit;
}
}
} break; // eCoalesceTextSelChange
case AccEvent::eRemoveDupes:
{
// Check for repeat events, coalesce newly appended event by more older
// event.
for (uint32_t index = tail - 1; index < tail; index--) {
AccEvent* accEvent = mEvents[index];
if (accEvent->mEventType == tailEvent->mEventType &&
accEvent->mEventRule == tailEvent->mEventRule &&
accEvent->mAccessible == tailEvent->mAccessible) {
tailEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
}
} break; // case eRemoveDupes
default:
break; // case eAllowDupes, eDoNotEmit
} // switch
}
void
EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
AccSelChangeEvent* aThisEvent,
uint32_t aThisIndex)
{
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
// Pack all preceding events into single selection within event
// when we receive too much selection add/remove events.
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
aTailEvent->mAccessible = aTailEvent->mWidget;
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
// Do not emit any preceding selection events for same widget if they
// weren't coalesced yet.
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
AccEvent* prevEvent = mEvents[jdx];
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
AccSelChangeEvent* prevSelChangeEvent =
downcast_accEvent(prevEvent);
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
}
}
}
return;
}
// Pack sequential selection remove and selection add events into
// single selection change event.
if (aTailEvent->mPreceedingCount == 1 &&
aTailEvent->mItem != aThisEvent->mItem) {
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
aTailEvent->mPackedEvent = aThisEvent;
return;
}
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
aThisEvent->mPackedEvent = aTailEvent;
return;
}
}
// Unpack the packed selection change event because we've got one
// more selection add/remove.
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
if (aThisEvent->mPackedEvent) {
aThisEvent->mPackedEvent->mEventType =
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
nsIAccessibleEvent::EVENT_SELECTION_ADD :
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
aThisEvent->mPackedEvent->mEventRule =
AccEvent::eCoalesceSelectionChange;
aThisEvent->mPackedEvent = nullptr;
}
aThisEvent->mEventType =
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
nsIAccessibleEvent::EVENT_SELECTION_ADD :
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
return;
}
// Convert into selection add since control has single selection but other
// selection events for this control are queued.
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
}
////////////////////////////////////////////////////////////////////////////////
// EventQueue: event queue
void
EventQueue::ProcessEventQueue()
{
// Process only currently queued events.
nsTArray<RefPtr<AccEvent> > events;
events.SwapElements(mEvents);
uint32_t eventCount = events.Length();
#ifdef A11Y_LOG
if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
logging::MsgBegin("EVENTS", "events processing");
logging::Address("document", mDocument);
logging::MsgEnd();
}
#endif
for (uint32_t idx = 0; idx < eventCount; idx++) {
AccEvent* event = events[idx];
if (event->mEventRule != AccEvent::eDoNotEmit) {
Accessible* target = event->GetAccessible();
if (!target || target->IsDefunct())
continue;
// Dispatch the focus event if target is still focused.
if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
FocusMgr()->ProcessFocusEvent(event);
continue;
}
// Dispatch caret moved and text selection change events.
if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
SelectionMgr()->ProcessTextSelChangeEvent(event);
continue;
}
// Fire selected state change events in support to selection events.
if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
true, event->mIsFromUserInput);
} else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
false, event->mIsFromUserInput);
} else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
(selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
event->mIsFromUserInput);
if (selChangeEvent->mPackedEvent) {
nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
states::SELECTED,
(selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
selChangeEvent->mPackedEvent->mIsFromUserInput);
}
}
nsEventShell::FireEvent(event);
}
if (!mDocument)
return;
}
}

View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_EventQueue_h_
#define mozilla_a11y_EventQueue_h_
#include "AccEvent.h"
namespace mozilla {
namespace a11y {
class DocAccessible;
/**
* Used to organize and coalesce pending events.
*/
class EventQueue
{
protected:
explicit EventQueue(DocAccessible* aDocument) : mDocument(aDocument) { }
/**
* Put an accessible event into the queue to process it later.
*/
bool PushEvent(AccEvent* aEvent);
/**
* Puts a name change event into the queue, if needed.
*/
bool PushNameChange(Accessible* aTarget);
/**
* Process events from the queue and fires events.
*/
void ProcessEventQueue();
private:
EventQueue(const EventQueue&) = delete;
EventQueue& operator = (const EventQueue&) = delete;
// Event queue processing
/**
* Coalesce redundant events from the queue.
*/
void CoalesceEvents();
/**
* Coalesce events from the same subtree.
*/
void CoalesceReorderEvents(AccEvent* aTailEvent);
/**
* Coalesce two selection change events within the same select control.
*/
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
AccSelChangeEvent* aThisEvent,
uint32_t aThisIndex);
protected:
/**
* The document accessible reference owning this queue.
*/
DocAccessible* mDocument;
/**
* Pending events array. Don't make this an AutoTArray; we use
* SwapElements() on it.
*/
nsTArray<RefPtr<AccEvent>> mEvents;
};
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11y_EventQueue_h_

View file

@ -0,0 +1,618 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "EventTree.h"
#include "Accessible-inl.h"
#include "nsEventShell.h"
#include "DocAccessible.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
#include "mozilla/UniquePtr.h"
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// TreeMutation class
EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
mParent(aParent), mStartIdx(UINT32_MAX),
mStateFlagsCopy(mParent->mStateFlags),
mQueueEvents(!aNoEvents)
{
#ifdef DEBUG
mIsDone = false;
#endif
#ifdef A11Y_LOG
if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
logging::MsgBegin("EVENTS_TREE", "reordering tree before");
logging::AccessibleInfo("reordering for", mParent);
Controller()->RootEventTree().Log();
logging::MsgEnd();
if (logging::IsEnabled(logging::eVerbose)) {
logging::Tree("EVENTS_TREE", "Container tree", mParent->Document(),
PrefixLog, static_cast<void*>(this));
}
}
#endif
mParent->mStateFlags |= Accessible::eKidsMutating;
}
TreeMutation::~TreeMutation()
{
MOZ_ASSERT(mIsDone, "Done() must be called explicitly");
}
void
TreeMutation::AfterInsertion(Accessible* aChild)
{
MOZ_ASSERT(aChild->Parent() == mParent);
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
mStartIdx = aChild->mIndexInParent + 1;
}
if (!mQueueEvents) {
return;
}
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
DebugOnly<bool> added = Controller()->QueueMutationEvent(ev);
MOZ_ASSERT(added);
aChild->SetShowEventTarget(true);
}
void
TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
{
MOZ_ASSERT(aChild->Parent() == mParent);
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
mStartIdx = aChild->mIndexInParent;
}
if (!mQueueEvents) {
return;
}
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, !aNoShutdown);
if (Controller()->QueueMutationEvent(ev)) {
aChild->SetHideEventTarget(true);
}
}
void
TreeMutation::Done()
{
MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating);
mParent->mStateFlags &= ~Accessible::eKidsMutating;
uint32_t length = mParent->mChildren.Length();
#ifdef DEBUG
for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
"Wrong index detected");
}
#endif
for (uint32_t idx = mStartIdx; idx < length; idx++) {
mParent->mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
}
mParent->mEmbeddedObjCollector = nullptr;
mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
#ifdef DEBUG
mIsDone = true;
#endif
#ifdef A11Y_LOG
if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
logging::MsgBegin("EVENTS_TREE", "reordering tree after");
logging::AccessibleInfo("reordering for", mParent);
Controller()->RootEventTree().Log();
logging::MsgEnd();
}
#endif
}
#ifdef A11Y_LOG
const char*
TreeMutation::PrefixLog(void* aData, Accessible* aAcc)
{
TreeMutation* thisObj = reinterpret_cast<TreeMutation*>(aData);
if (thisObj->mParent == aAcc) {
return "_X_";
}
const EventTree& ret = thisObj->Controller()->RootEventTree();
if (ret.Find(aAcc)) {
return "_с_";
}
return "";
}
#endif
////////////////////////////////////////////////////////////////////////////////
// EventTree
void
EventTree::Shown(Accessible* aChild)
{
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
Controller(aChild)->WithdrawPrecedingEvents(&ev->mPrecedingEvents);
Mutated(ev);
}
void
EventTree::Hidden(Accessible* aChild, bool aNeedsShutdown)
{
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, aNeedsShutdown);
if (!aNeedsShutdown) {
Controller(aChild)->StorePrecedingEvent(ev);
}
Mutated(ev);
}
void
EventTree::Process(const RefPtr<DocAccessible>& aDeathGrip)
{
while (mFirst) {
// Skip a node and its subtree if its container is not in the document.
if (mFirst->mContainer->IsInDocument()) {
mFirst->Process(aDeathGrip);
if (aDeathGrip->IsDefunct()) {
return;
}
}
mFirst = Move(mFirst->mNext);
}
MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
"No container, no events");
MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(),
"Processing events for defunct container");
MOZ_ASSERT(!mFireReorder || mContainer, "No target for reorder event");
// Fire mutation events.
uint32_t eventsCount = mDependentEvents.Length();
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
AccMutationEvent* mtEvent = mDependentEvents[jdx];
MOZ_ASSERT(mtEvent->Document(), "No document for event target");
// Fire all hide events that has to be fired before this show event.
if (mtEvent->IsShow()) {
AccShowEvent* showEv = downcast_accEvent(mtEvent);
for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
nsEventShell::FireEvent(showEv->mPrecedingEvents[i]);
if (aDeathGrip->IsDefunct()) {
return;
}
}
}
nsEventShell::FireEvent(mtEvent);
if (aDeathGrip->IsDefunct()) {
return;
}
if (mtEvent->mTextChangeEvent) {
nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
if (aDeathGrip->IsDefunct()) {
return;
}
}
if (mtEvent->IsHide()) {
// Fire menupopup end event before a hide event if a menu goes away.
// XXX: We don't look into children of hidden subtree to find hiding
// menupopup (as we did prior bug 570275) because we don't do that when
// menu is showing (and that's impossible until bug 606924 is fixed).
// Nevertheless we should do this at least because layout coalesces
// the changes before our processing and we may miss some menupopup
// events. Now we just want to be consistent in content insertion/removal
// handling.
if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
mtEvent->mAccessible);
if (aDeathGrip->IsDefunct()) {
return;
}
}
AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
if (hideEvent->NeedsShutdown()) {
aDeathGrip->ShutdownChildrenInSubtree(mtEvent->mAccessible);
}
}
}
// Fire reorder event at last.
if (mFireReorder) {
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
mContainer->Document()->MaybeNotifyOfValueChange(mContainer);
}
mDependentEvents.Clear();
}
EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
if (!mFirst) {
mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
return mFirst.get();
}
EventTree* prevNode = nullptr;
EventTree* node = mFirst.get();
do {
MOZ_ASSERT(!node->mContainer->IsApplication(),
"No event for application accessible is expected here");
MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");
// Case of same target.
if (node->mContainer == aContainer) {
return node;
}
// Check if the given container is contained by a current node
Accessible* top = mContainer ? mContainer : aContainer->Document();
Accessible* parent = aContainer;
while (parent) {
// Reached a top, no match for a current event.
if (parent == top) {
break;
}
// We got a match.
if (parent->Parent() == node->mContainer) {
// Reject the node if it's contained by a show/hide event target
uint32_t evCount = node->mDependentEvents.Length();
for (uint32_t idx = 0; idx < evCount; idx++) {
AccMutationEvent* ev = node->mDependentEvents[idx];
if (ev->GetAccessible() == parent) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eEventTree)) {
logging::MsgBegin("EVENTS_TREE",
"Rejecting node contained by show/hide");
logging::AccessibleInfo("Node", aContainer);
logging::MsgEnd();
}
#endif
// If the node is rejected, then check if it has related hide event
// on stack, and if so, then connect it to the parent show event.
if (ev->IsShow()) {
AccShowEvent* showEv = downcast_accEvent(ev);
Controller(aContainer)->
WithdrawPrecedingEvents(&showEv->mPrecedingEvents);
}
return nullptr;
}
}
return node->FindOrInsert(aContainer);
}
parent = parent->Parent();
MOZ_ASSERT(parent, "Wrong tree");
}
// If the given container contains a current node
// then
// if show or hide of the given node contains a grand parent of the current node
// then ignore the current node and its show and hide events
// otherwise ignore the current node, but not its show and hide events
Accessible* curParent = node->mContainer;
while (curParent && !curParent->IsDoc()) {
if (curParent->Parent() != aContainer) {
curParent = curParent->Parent();
continue;
}
// Insert the tail node into the hierarchy between the current node and
// its parent.
node->mFireReorder = false;
UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
newNode->mFirst = Move(nodeOwnerRef);
nodeOwnerRef = Move(newNode);
nodeOwnerRef->mNext = Move(node->mNext);
// Check if a next node is contained by the given node too, and move them
// under the given node if so.
prevNode = nodeOwnerRef.get();
node = nodeOwnerRef->mNext.get();
UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
EventTree* insNode = nodeOwnerRef->mFirst.get();
while (node) {
Accessible* curParent = node->mContainer;
while (curParent && !curParent->IsDoc()) {
if (curParent->Parent() != aContainer) {
curParent = curParent->Parent();
continue;
}
MOZ_ASSERT(!insNode->mNext);
node->mFireReorder = false;
insNode->mNext = Move(*nodeRef);
insNode = insNode->mNext.get();
prevNode->mNext = Move(node->mNext);
node = prevNode;
break;
}
prevNode = node;
nodeRef = &node->mNext;
node = node->mNext.get();
}
return nodeOwnerRef.get();
}
prevNode = node;
} while ((node = node->mNext.get()));
MOZ_ASSERT(prevNode, "Nowhere to insert");
MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
// If 'this' node contains the given container accessible, then
// do not emit a reorder event for the container
// if a dependent show event target contains the given container then do not
// emit show / hide events (see Process() method)
prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
return prevNode->mNext.get();
}
void
EventTree::Clear()
{
mFirst = nullptr;
mNext = nullptr;
mContainer = nullptr;
uint32_t eventsCount = mDependentEvents.Length();
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
mDependentEvents[jdx]->mEventType = AccEvent::eDoNotEmit;
AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
if (ev && ev->NeedsShutdown()) {
ev->Document()->ShutdownChildrenInSubtree(ev->mAccessible);
}
}
mDependentEvents.Clear();
}
const EventTree*
EventTree::Find(const Accessible* aContainer) const
{
const EventTree* et = this;
while (et) {
if (et->mContainer == aContainer) {
return et;
}
if (et->mFirst) {
et = et->mFirst.get();
const EventTree* cet = et->Find(aContainer);
if (cet) {
return cet;
}
}
et = et->mNext.get();
const EventTree* cet = et->Find(aContainer);
if (cet) {
return cet;
}
}
return nullptr;
}
#ifdef A11Y_LOG
void
EventTree::Log(uint32_t aLevel) const
{
if (aLevel == UINT32_MAX) {
if (mFirst) {
mFirst->Log(0);
}
return;
}
for (uint32_t i = 0; i < aLevel; i++) {
printf(" ");
}
logging::AccessibleInfo("container", mContainer);
for (uint32_t i = 0; i < mDependentEvents.Length(); i++) {
AccMutationEvent* ev = mDependentEvents[i];
if (ev->IsShow()) {
for (uint32_t i = 0; i < aLevel + 1; i++) {
printf(" ");
}
logging::AccessibleInfo("shown", ev->mAccessible);
AccShowEvent* showEv = downcast_accEvent(ev);
for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
for (uint32_t j = 0; j < aLevel + 1; j++) {
printf(" ");
}
logging::AccessibleInfo("preceding",
showEv->mPrecedingEvents[i]->mAccessible);
}
}
else {
for (uint32_t i = 0; i < aLevel + 1; i++) {
printf(" ");
}
logging::AccessibleInfo("hidden", ev->mAccessible);
}
}
if (mFirst) {
mFirst->Log(aLevel + 1);
}
if (mNext) {
mNext->Log(aLevel);
}
}
#endif
void
EventTree::Mutated(AccMutationEvent* aEv)
{
// If shown or hidden node is a root of previously mutated subtree, then
// discard those subtree mutations as we are no longer interested in them.
UniquePtr<EventTree>* node = &mFirst;
while (*node) {
Accessible* cntr = (*node)->mContainer;
while (cntr != mContainer) {
if (cntr == aEv->mAccessible) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eEventTree)) {
logging::MsgBegin("EVENTS_TREE", "Trim subtree");
logging::AccessibleInfo("Show/hide container", aEv->mAccessible);
logging::AccessibleInfo("Trimmed subtree root", (*node)->mContainer);
logging::MsgEnd();
}
#endif
// If the new hide is part of a move and it contains existing child
// shows, then move preceding events from the child shows to the buffer,
// so the ongoing show event will pick them up.
if (aEv->IsHide()) {
AccHideEvent* hideEv = downcast_accEvent(aEv);
if (!hideEv->mNeedsShutdown) {
for (uint32_t i = 0; i < (*node)->mDependentEvents.Length(); i++) {
AccMutationEvent* childEv = (*node)->mDependentEvents[i];
if (childEv->IsShow()) {
AccShowEvent* childShowEv = downcast_accEvent(childEv);
if (childShowEv->mPrecedingEvents.Length() > 0) {
Controller(mContainer)->StorePrecedingEvents(
mozilla::Move(childShowEv->mPrecedingEvents));
}
}
}
}
}
// If the new show contains existing child shows, then move preceding
// events from the child shows to the new show.
else if (aEv->IsShow()) {
AccShowEvent* showEv = downcast_accEvent(aEv);
for (uint32_t i = 0; (*node)->mDependentEvents.Length(); i++) {
AccMutationEvent* childEv = (*node)->mDependentEvents[i];
if (childEv->IsShow()) {
AccShowEvent* showChildEv = downcast_accEvent(childEv);
if (showChildEv->mPrecedingEvents.Length() > 0) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eEventTree)) {
logging::MsgBegin("EVENTS_TREE", "Adopt preceding events");
logging::AccessibleInfo("Parent", aEv->mAccessible);
for (uint32_t j = 0; j < showChildEv->mPrecedingEvents.Length(); j++) {
logging::AccessibleInfo("Adoptee",
showChildEv->mPrecedingEvents[i]->mAccessible);
}
logging::MsgEnd();
}
#endif
showEv->mPrecedingEvents.AppendElements(showChildEv->mPrecedingEvents);
}
}
}
}
*node = Move((*node)->mNext);
break;
}
cntr = cntr->Parent();
}
if (cntr == aEv->mAccessible) {
continue;
}
node = &(*node)->mNext;
}
AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
mDependentEvents.AppendElement(aEv);
// Coalesce text change events from this hide/show event and the previous one.
if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
if (aEv->IsHide()) {
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
// affect the text within the hypertext.
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
if (prevTextEvent) {
AccHideEvent* hideEvent = downcast_accEvent(aEv);
AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent);
if (prevHideEvent->mNextSibling == hideEvent->mAccessible) {
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
}
else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) {
uint32_t oldLen = prevTextEvent->GetLength();
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen;
}
hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
}
}
else {
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
if (prevTextEvent) {
if (aEv->mAccessible->IndexInParent() ==
prevEvent->mAccessible->IndexInParent() + 1) {
// If tail target was inserted after this target, i.e. tail target is next
// sibling of this target.
aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
}
else if (aEv->mAccessible->IndexInParent() ==
prevEvent->mAccessible->IndexInParent() - 1) {
// If tail target was inserted before this target, i.e. tail target is
// previous sibling of this target.
nsAutoString startText;
aEv->mAccessible->AppendTextTo(startText);
prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText;
prevTextEvent->mStart -= startText.Length();
}
aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
}
}
}
// Create a text change event caused by this hide/show event. When a node is
// hidden/removed or shown/appended, the text in an ancestor hyper text will
// lose or get new characters.
if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) {
return;
}
nsAutoString text;
aEv->mAccessible->AppendTextTo(text);
if (text.IsEmpty()) {
return;
}
int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible);
aEv->mTextChangeEvent =
new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(),
aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}

121
accessible/base/EventTree.h Normal file
View file

@ -0,0 +1,121 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_EventTree_h_
#define mozilla_a11y_EventTree_h_
#include "AccEvent.h"
#include "Accessible.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
namespace mozilla {
namespace a11y {
/**
* This class makes sure required tasks are done before and after tree
* mutations. Currently this only includes group info invalidation. You must
* have an object of this class on the stack when calling methods that mutate
* the accessible tree.
*/
class TreeMutation final
{
public:
static const bool kNoEvents = true;
static const bool kNoShutdown = true;
explicit TreeMutation(Accessible* aParent, bool aNoEvents = false);
~TreeMutation();
void AfterInsertion(Accessible* aChild);
void BeforeRemoval(Accessible* aChild, bool aNoShutdown = false);
void Done();
private:
NotificationController* Controller() const
{ return mParent->Document()->Controller(); }
static EventTree* const kNoEventTree;
#ifdef A11Y_LOG
static const char* PrefixLog(void* aData, Accessible*);
#endif
Accessible* mParent;
uint32_t mStartIdx;
uint32_t mStateFlagsCopy;
/*
* True if mutation events should be queued.
*/
bool mQueueEvents;
#ifdef DEBUG
bool mIsDone;
#endif
};
/**
* A mutation events coalescence structure.
*/
class EventTree final {
public:
EventTree() :
mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(false) { }
explicit EventTree(Accessible* aContainer, bool aFireReorder) :
mFirst(nullptr), mNext(nullptr), mContainer(aContainer),
mFireReorder(aFireReorder) { }
~EventTree() { Clear(); }
void Shown(Accessible* aTarget);
void Hidden(Accessible*, bool);
/**
* Return an event tree node for the given accessible.
*/
const EventTree* Find(const Accessible* aContainer) const;
/**
* Add a mutation event to this event tree.
*/
void Mutated(AccMutationEvent* aEv);
#ifdef A11Y_LOG
void Log(uint32_t aLevel = UINT32_MAX) const;
#endif
private:
/**
* Processes the event queue and fires events.
*/
void Process(const RefPtr<DocAccessible>& aDeathGrip);
/**
* Return an event subtree for the given accessible.
*/
EventTree* FindOrInsert(Accessible* aContainer);
void Clear();
UniquePtr<EventTree> mFirst;
UniquePtr<EventTree> mNext;
Accessible* mContainer;
nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
bool mFireReorder;
static NotificationController* Controller(Accessible* aAcc)
{ return aAcc->Document()->Controller(); }
friend class NotificationController;
};
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11y_EventQueue_h_

View file

@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Filters.h"
#include "Accessible-inl.h"
#include "nsAccUtils.h"
#include "Role.h"
#include "States.h"
using namespace mozilla::a11y;
using namespace mozilla::a11y::filters;
uint32_t
filters::GetSelected(Accessible* aAccessible)
{
if (aAccessible->State() & states::SELECTED)
return eMatch | eSkipSubtree;
return eSkip;
}
uint32_t
filters::GetSelectable(Accessible* aAccessible)
{
if (aAccessible->InteractiveState() & states::SELECTABLE)
return eMatch | eSkipSubtree;
return eSkip;
}
uint32_t
filters::GetRow(Accessible* aAccessible)
{
a11y::role role = aAccessible->Role();
if (role == roles::ROW)
return eMatch | eSkipSubtree;
// Look for rows inside rowgroup.
if (role == roles::GROUPING)
return eSkip;
return eSkipSubtree;
}
uint32_t
filters::GetCell(Accessible* aAccessible)
{
return aAccessible->IsTableCell() ? eMatch : eSkipSubtree;
}

50
accessible/base/Filters.h Normal file
View file

@ -0,0 +1,50 @@
/* 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_Filters_h__
#define mozilla_a11y_Filters_h__
#include <stdint.h>
/**
* Predefined filters used for nsAccIterator and nsAccCollector.
*/
namespace mozilla {
namespace a11y {
class Accessible;
namespace filters {
enum EResult {
eSkip = 0,
eMatch = 1,
eSkipSubtree = 2
};
/**
* Return true if the traversed accessible complies with filter.
*/
typedef uint32_t (*FilterFuncPtr) (Accessible*);
/**
* Matches selected/selectable accessibles in subtree.
*/
uint32_t GetSelected(Accessible* aAccessible);
uint32_t GetSelectable(Accessible* aAccessible);
/**
* Matches row accessibles in subtree.
*/
uint32_t GetRow(Accessible* aAccessible);
/**
* Matches cell accessibles in children.
*/
uint32_t GetCell(Accessible* aAccessible);
} // namespace filters
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,404 @@
/* 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 "FocusManager.h"
#include "Accessible-inl.h"
#include "AccIterator.h"
#include "DocAccessible-inl.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
#include "nsEventShell.h"
#include "Role.h"
#include "nsFocusManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/dom/Element.h"
namespace mozilla {
namespace a11y {
FocusManager::FocusManager()
{
}
FocusManager::~FocusManager()
{
}
Accessible*
FocusManager::FocusedAccessible() const
{
if (mActiveItem)
return mActiveItem;
nsINode* focusedNode = FocusedDOMNode();
if (focusedNode) {
DocAccessible* doc =
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
return doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr;
}
return nullptr;
}
bool
FocusManager::IsFocused(const Accessible* aAccessible) const
{
if (mActiveItem)
return mActiveItem == aAccessible;
nsINode* focusedNode = FocusedDOMNode();
if (focusedNode) {
// XXX: Before getting an accessible for node having a DOM focus make sure
// they belong to the same document because it can trigger unwanted document
// accessible creation for temporary about:blank document. Without this
// peculiarity we would end up with plain implementation based on
// FocusedAccessible() method call. Make sure this issue is fixed in
// bug 638465.
if (focusedNode->OwnerDoc() == aAccessible->GetNode()->OwnerDoc()) {
DocAccessible* doc =
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
return aAccessible ==
(doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr);
}
}
return false;
}
bool
FocusManager::IsFocusWithin(const Accessible* aContainer) const
{
Accessible* child = FocusedAccessible();
while (child) {
if (child == aContainer)
return true;
child = child->Parent();
}
return false;
}
FocusManager::FocusDisposition
FocusManager::IsInOrContainsFocus(const Accessible* aAccessible) const
{
Accessible* focus = FocusedAccessible();
if (!focus)
return eNone;
// If focused.
if (focus == aAccessible)
return eFocused;
// If contains the focus.
Accessible* child = focus->Parent();
while (child) {
if (child == aAccessible)
return eContainsFocus;
child = child->Parent();
}
// If contained by focus.
child = aAccessible->Parent();
while (child) {
if (child == focus)
return eContainedByFocus;
child = child->Parent();
}
return eNone;
}
void
FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
{
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusNotificationTarget("DOM focus", "Target", aTarget);
#endif
mActiveItem = nullptr;
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
if (targetNode) {
DocAccessible* document =
GetAccService()->GetDocAccessible(targetNode->OwnerDoc());
if (document) {
// Set selection listener for focused element.
if (targetNode->IsElement())
SelectionMgr()->SetControlSelectionListener(targetNode->AsElement());
document->HandleNotification<FocusManager, nsINode>
(this, &FocusManager::ProcessDOMFocus, targetNode);
}
}
}
void
FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
{
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusNotificationTarget("DOM blur", "Target", aTarget);
#endif
mActiveItem = nullptr;
// If DOM document stays focused then fire accessible focus event to process
// the case when no element within this DOM document will be focused.
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
if (targetNode && targetNode->OwnerDoc() == FocusedDOMDocument()) {
nsIDocument* DOMDoc = targetNode->OwnerDoc();
DocAccessible* document =
GetAccService()->GetDocAccessible(DOMDoc);
if (document) {
// Clear selection listener for previously focused element.
if (targetNode->IsElement())
SelectionMgr()->ClearControlSelectionListener();
document->HandleNotification<FocusManager, nsINode>
(this, &FocusManager::ProcessDOMFocus, DOMDoc);
}
}
}
void
FocusManager::ActiveItemChanged(Accessible* aItem, bool aCheckIfActive)
{
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusNotificationTarget("active item changed", "Item", aItem);
#endif
// Nothing changed, happens for XUL trees and HTML selects.
if (aItem && aItem == mActiveItem)
return;
mActiveItem = nullptr;
if (aItem && aCheckIfActive) {
Accessible* widget = aItem->ContainerWidget();
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::ActiveWidget(widget);
#endif
if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
return;
}
mActiveItem = aItem;
// If active item is changed then fire accessible focus event on it, otherwise
// if there's no an active item then fire focus event to accessible having
// DOM focus.
Accessible* target = FocusedAccessible();
if (target)
DispatchFocusEvent(target->Document(), target);
}
void
FocusManager::ForceFocusEvent()
{
nsINode* focusedNode = FocusedDOMNode();
if (focusedNode) {
DocAccessible* document =
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
if (document) {
document->HandleNotification<FocusManager, nsINode>
(this, &FocusManager::ProcessDOMFocus, focusedNode);
}
}
}
void
FocusManager::DispatchFocusEvent(DocAccessible* aDocument,
Accessible* aTarget)
{
NS_PRECONDITION(aDocument, "No document for focused accessible!");
if (aDocument) {
RefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
eAutoDetect, AccEvent::eCoalesceOfSameType);
aDocument->FireDelayedEvent(event);
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusDispatched(aTarget);
#endif
}
}
void
FocusManager::ProcessDOMFocus(nsINode* aTarget)
{
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusNotificationTarget("process DOM focus", "Target", aTarget);
#endif
DocAccessible* document =
GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
if (!document)
return;
Accessible* target = document->GetAccessibleEvenIfNotInMapOrContainer(aTarget);
if (target) {
// Check if still focused. Otherwise we can end up with storing the active
// item for control that isn't focused anymore.
nsINode* focusedNode = FocusedDOMNode();
if (!focusedNode)
return;
Accessible* DOMFocus =
document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
if (target != DOMFocus)
return;
Accessible* activeItem = target->CurrentItem();
if (activeItem) {
mActiveItem = activeItem;
target = activeItem;
}
DispatchFocusEvent(document, target);
}
}
void
FocusManager::ProcessFocusEvent(AccEvent* aEvent)
{
NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
"Focus event is expected!");
// Emit focus event if event target is the active item. Otherwise then check
// if it's still focused and then update active item and emit focus event.
Accessible* target = aEvent->GetAccessible();
if (target != mActiveItem) {
// Check if still focused. Otherwise we can end up with storing the active
// item for control that isn't focused anymore.
DocAccessible* document = aEvent->Document();
nsINode* focusedNode = FocusedDOMNode();
if (!focusedNode)
return;
Accessible* DOMFocus =
document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
if (target != DOMFocus)
return;
Accessible* activeItem = target->CurrentItem();
if (activeItem) {
mActiveItem = activeItem;
target = activeItem;
}
}
// Fire menu start/end events for ARIA menus.
if (target->IsARIARole(nsGkAtoms::menuitem)) {
// The focus was moved into menu.
Accessible* ARIAMenubar = nullptr;
for (Accessible* parent = target->Parent(); parent; parent = parent->Parent()) {
if (parent->IsARIARole(nsGkAtoms::menubar)) {
ARIAMenubar = parent;
break;
}
// Go up in the parent chain of the menu hierarchy.
if (!parent->IsARIARole(nsGkAtoms::menuitem) &&
!parent->IsARIARole(nsGkAtoms::menu)) {
break;
}
}
if (ARIAMenubar != mActiveARIAMenubar) {
// Leaving ARIA menu. Fire menu_end event on current menubar.
if (mActiveARIAMenubar) {
RefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
aEvent->FromUserInput());
nsEventShell::FireEvent(menuEndEvent);
}
mActiveARIAMenubar = ARIAMenubar;
// Entering ARIA menu. Fire menu_start event.
if (mActiveARIAMenubar) {
RefPtr<AccEvent> menuStartEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
mActiveARIAMenubar, aEvent->FromUserInput());
nsEventShell::FireEvent(menuStartEvent);
}
}
} else if (mActiveARIAMenubar) {
// Focus left a menu. Fire menu_end event.
RefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
aEvent->FromUserInput());
nsEventShell::FireEvent(menuEndEvent);
mActiveARIAMenubar = nullptr;
}
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::FocusNotificationTarget("fire focus event", "Target", target);
#endif
// Reset cached caret value. The cache will be updated upon processing the
// next caret move event. This ensures that we will return the correct caret
// offset before the caret move event is handled.
SelectionMgr()->ResetCaretOffset();
RefPtr<AccEvent> focusEvent =
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
nsEventShell::FireEvent(focusEvent);
// Fire scrolling_start event when the document receives the focus if it has
// an anchor jump. If an accessible within the document receive the focus
// then null out the anchor jump because it no longer applies.
DocAccessible* targetDocument = target->Document();
Accessible* anchorJump = targetDocument->AnchorJump();
if (anchorJump) {
if (target == targetDocument) {
// XXX: bug 625699, note in some cases the node could go away before we
// we receive focus event, for example if the node is removed from DOM.
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
anchorJump, aEvent->FromUserInput());
}
targetDocument->SetAnchorJump(nullptr);
}
}
nsINode*
FocusManager::FocusedDOMNode() const
{
nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
nsIContent* focusedElm = DOMFocusManager->GetFocusedContent();
// No focus on remote target elements like xul:browser having DOM focus and
// residing in chrome process because it means an element in content process
// keeps the focus.
if (focusedElm) {
if (EventStateManager::IsRemoteTarget(focusedElm)) {
return nullptr;
}
return focusedElm;
}
// Otherwise the focus can be on DOM document.
nsPIDOMWindowOuter* focusedWnd = DOMFocusManager->GetFocusedWindow();
return focusedWnd ? focusedWnd->GetExtantDoc() : nullptr;
}
nsIDocument*
FocusManager::FocusedDOMDocument() const
{
nsINode* focusedNode = FocusedDOMNode();
return focusedNode ? focusedNode->OwnerDoc() : nullptr;
}
} // namespace a11y
} // namespace mozilla

View file

@ -0,0 +1,132 @@
/* 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_FocusManager_h_
#define mozilla_a11y_FocusManager_h_
class nsINode;
class nsIDocument;
class nsISupports;
namespace mozilla {
namespace a11y {
class AccEvent;
class Accessible;
class DocAccessible;
/**
* Manage the accessible focus. Used to fire and process accessible events.
*/
class FocusManager
{
public:
virtual ~FocusManager();
/**
* Return a focused accessible.
*/
Accessible* FocusedAccessible() const;
/**
* Return true if given accessible is focused.
*/
bool IsFocused(const Accessible* aAccessible) const;
/**
* Return true if the given accessible is an active item, i.e. an item that
* is current within the active widget.
*/
inline bool IsActiveItem(const Accessible* aAccessible)
{ return aAccessible == mActiveItem; }
/**
* Return true if given DOM node has DOM focus.
*/
inline bool HasDOMFocus(const nsINode* aNode) const
{ return aNode == FocusedDOMNode(); }
/**
* Return true if focused accessible is within the given container.
*/
bool IsFocusWithin(const Accessible* aContainer) const;
/**
* Return whether the given accessible is focused or contains the focus or
* contained by focused accessible.
*/
enum FocusDisposition {
eNone,
eFocused,
eContainsFocus,
eContainedByFocus
};
FocusDisposition IsInOrContainsFocus(const Accessible* aAccessible) const;
//////////////////////////////////////////////////////////////////////////////
// Focus notifications and processing (don't use until you know what you do).
/**
* Called when DOM focus event is fired.
*/
void NotifyOfDOMFocus(nsISupports* aTarget);
/**
* Called when DOM blur event is fired.
*/
void NotifyOfDOMBlur(nsISupports* aTarget);
/**
* Called when active item is changed. Note: must be called when accessible
* tree is up to date.
*/
void ActiveItemChanged(Accessible* aItem, bool aCheckIfActive = true);
/**
* Dispatch delayed focus event for the current focus accessible.
*/
void ForceFocusEvent();
/**
* Dispatch delayed focus event for the given target.
*/
void DispatchFocusEvent(DocAccessible* aDocument, Accessible* aTarget);
/**
* Process DOM focus notification.
*/
void ProcessDOMFocus(nsINode* aTarget);
/**
* Process the delayed accessible event.
* do.
*/
void ProcessFocusEvent(AccEvent* aEvent);
protected:
FocusManager();
private:
FocusManager(const FocusManager&);
FocusManager& operator =(const FocusManager&);
/**
* Return DOM node having DOM focus.
*/
nsINode* FocusedDOMNode() const;
/**
* Return DOM document having DOM focus.
*/
nsIDocument* FocusedDOMDocument() const;
private:
RefPtr<Accessible> mActiveItem;
RefPtr<Accessible> mActiveARIAMenubar;
};
} // namespace a11y
} // namespace mozilla
#endif

1039
accessible/base/Logging.cpp Normal file

File diff suppressed because it is too large Load diff

225
accessible/base/Logging.h Normal file
View file

@ -0,0 +1,225 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_logs_h__
#define mozilla_a11y_logs_h__
#include "nscore.h"
#include "nsStringFwd.h"
class nsIDocument;
class nsINode;
class nsIRequest;
class nsISelection;
class nsISupports;
class nsIWebProgress;
namespace mozilla {
namespace a11y {
class AccEvent;
class Accessible;
class DocAccessible;
class OuterDocAccessible;
namespace logging {
enum EModules {
eDocLoad = 1 << 0,
eDocCreate = 1 << 1,
eDocDestroy = 1 << 2,
eDocLifeCycle = eDocLoad | eDocCreate | eDocDestroy,
eEvents = 1 << 3,
eEventTree = 1 << 4,
ePlatforms = 1 << 5,
eText = 1 << 6,
eTree = 1 << 7,
eDOMEvents = 1 << 8,
eFocus = 1 << 9,
eSelection = 1 << 10,
eNotifications = eDOMEvents | eSelection | eFocus,
// extras
eStack = 1 << 11,
eVerbose = 1 << 12
};
/**
* Return true if any of the given modules is logged.
*/
bool IsEnabled(uint32_t aModules);
/**
* Return true if all of the given modules are logged.
*/
bool IsEnabledAll(uint32_t aModules);
/**
* Return true if the given module is logged.
*/
bool IsEnabled(const nsAString& aModules);
/**
* Log the document loading progress.
*/
void DocLoad(const char* aMsg, nsIWebProgress* aWebProgress,
nsIRequest* aRequest, uint32_t aStateFlags);
void DocLoad(const char* aMsg, nsIDocument* aDocumentNode);
void DocCompleteLoad(DocAccessible* aDocument, bool aIsLoadEventTarget);
/**
* Log that document load event was fired.
*/
void DocLoadEventFired(AccEvent* aEvent);
/**
* Log that document laod event was handled.
*/
void DocLoadEventHandled(AccEvent* aEvent);
/**
* Log the document was created.
*/
void DocCreate(const char* aMsg, nsIDocument* aDocumentNode,
DocAccessible* aDocument = nullptr);
/**
* Log the document was destroyed.
*/
void DocDestroy(const char* aMsg, nsIDocument* aDocumentNode,
DocAccessible* aDocument = nullptr);
/**
* Log the outer document was destroyed.
*/
void OuterDocDestroy(OuterDocAccessible* OuterDoc);
/**
* Log the focus notification target.
*/
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
Accessible* aTarget);
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
nsINode* aTargetNode);
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
nsISupports* aTargetThing);
/**
* Log a cause of active item descendant change (submessage).
*/
void ActiveItemChangeCausedBy(const char* aMsg, Accessible* aTarget);
/**
* Log the active widget (submessage).
*/
void ActiveWidget(Accessible* aWidget);
/**
* Log the focus event was dispatched (submessage).
*/
void FocusDispatched(Accessible* aTarget);
/**
* Log the selection change.
*/
void SelChange(nsISelection* aSelection, DocAccessible* aDocument,
int16_t aReason);
/**
* Log the given accessible elements info.
*/
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...);
void TreeInfo(const char* aMsg, uint32_t aExtraFlags,
const char* aMsg1, Accessible* aAcc,
const char* aMsg2, nsINode* aNode);
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent);
/**
* Log the accessible/DOM tree.
*/
typedef const char* (*GetTreePrefix)(void* aData, Accessible*);
void Tree(const char* aTitle, const char* aMsgText, Accessible* aRoot,
GetTreePrefix aPrefixFunc = nullptr, void* aGetTreePrefixData = nullptr);
void DOMTree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc);
/**
* Log the message ('title: text' format) on new line. Print the start and end
* boundaries of the message body designated by '{' and '}' (2 spaces indent for
* body).
*/
void MsgBegin(const char* aTitle, const char* aMsgText, ...);
void MsgEnd();
/**
* Print start and end boundaries of the message body designated by '{' and '}'
* (2 spaces indent for body).
*/
void SubMsgBegin();
void SubMsgEnd();
/**
* Log the entry into message body (4 spaces indent).
*/
void MsgEntry(const char* aEntryText, ...);
/**
* Log the text, two spaces offset is used.
*/
void Text(const char* aText);
/**
* Log the accessible object address as message entry (4 spaces indent).
*/
void Address(const char* aDescr, Accessible* aAcc);
/**
* Log the DOM node info as message entry.
*/
void Node(const char* aDescr, nsINode* aNode);
/**
* Log the document accessible info as message entry.
*/
void Document(DocAccessible* aDocument);
/**
* Log the accessible and its DOM node as a message entry.
*/
void AccessibleInfo(const char* aDescr, Accessible* aAccessible);
void AccessibleNNode(const char* aDescr, Accessible* aAccessible);
void AccessibleNNode(const char* aDescr, nsINode* aNode);
/**
* Log the DOM event.
*/
void DOMEvent(const char* aDescr, nsINode* aOrigTarget,
const nsAString& aEventType);
/**
* Log the call stack, two spaces offset is used.
*/
void Stack();
/**
* Enable logging of the specified modules, all other modules aren't logged.
*/
void Enable(const nsAFlatCString& aModules);
/**
* Enable logging of modules specified by A11YLOG environment variable,
* all other modules aren't logged.
*/
void CheckEnv();
} // namespace logging
} // namespace a11y
} // namespace mozilla
#endif

340
accessible/base/MarkupMap.h Normal file
View file

@ -0,0 +1,340 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
MARKUPMAP(a,
New_HTMLLink,
roles::LINK)
MARKUPMAP(abbr,
New_HyperText,
0)
MARKUPMAP(acronym,
New_HyperText,
0)
MARKUPMAP(article,
New_HyperText,
roles::DOCUMENT,
Attr(xmlroles, article))
MARKUPMAP(aside,
New_HyperText,
roles::NOTE)
MARKUPMAP(blockquote,
New_HyperText,
roles::SECTION)
MARKUPMAP(dd,
New_HTMLDefinition,
roles::DEFINITION)
MARKUPMAP(details,
New_HyperText,
roles::DETAILS)
MARKUPMAP(div,
nullptr,
roles::SECTION)
MARKUPMAP(dl,
New_HTMLList,
roles::DEFINITION_LIST)
MARKUPMAP(dt,
New_HTMLListitem,
roles::TERM)
MARKUPMAP(figcaption,
New_HTMLFigcaption,
roles::CAPTION)
MARKUPMAP(figure,
New_HTMLFigure,
roles::FIGURE,
Attr(xmlroles, figure))
MARKUPMAP(form,
New_HyperText,
roles::FORM)
MARKUPMAP(footer,
New_HyperText,
roles::FOOTER)
MARKUPMAP(header,
New_HyperText,
roles::HEADER)
MARKUPMAP(h1,
New_HyperText,
roles::HEADING)
MARKUPMAP(h2,
New_HyperText,
roles::HEADING)
MARKUPMAP(h3,
New_HyperText,
roles::HEADING)
MARKUPMAP(h4,
New_HyperText,
roles::HEADING)
MARKUPMAP(h5,
New_HyperText,
roles::HEADING)
MARKUPMAP(h6,
New_HyperText,
roles::HEADING)
MARKUPMAP(label,
New_HTMLLabel,
roles::LABEL)
MARKUPMAP(legend,
New_HTMLLegend,
roles::LABEL)
MARKUPMAP(li,
New_HTMLListitem,
0)
MARKUPMAP(map,
nullptr,
roles::TEXT_CONTAINER)
MARKUPMAP(math,
New_HyperText,
roles::MATHML_MATH)
MARKUPMAP(mi_,
New_HyperText,
roles::MATHML_IDENTIFIER)
MARKUPMAP(mn_,
New_HyperText,
roles::MATHML_NUMBER)
MARKUPMAP(mo_,
New_HyperText,
roles::MATHML_OPERATOR,
AttrFromDOM(accent_, accent_),
AttrFromDOM(fence_, fence_),
AttrFromDOM(separator_, separator_),
AttrFromDOM(largeop_, largeop_))
MARKUPMAP(mtext_,
New_HyperText,
roles::MATHML_TEXT)
MARKUPMAP(ms_,
New_HyperText,
roles::MATHML_STRING_LITERAL)
MARKUPMAP(mglyph_,
New_HyperText,
roles::MATHML_GLYPH)
MARKUPMAP(mrow_,
New_HyperText,
roles::MATHML_ROW)
MARKUPMAP(mfrac_,
New_HyperText,
roles::MATHML_FRACTION,
AttrFromDOM(bevelled_, bevelled_),
AttrFromDOM(linethickness_, linethickness_))
MARKUPMAP(msqrt_,
New_HyperText,
roles::MATHML_SQUARE_ROOT)
MARKUPMAP(mroot_,
New_HyperText,
roles::MATHML_ROOT)
MARKUPMAP(mfenced_,
New_HyperText,
roles::MATHML_FENCED,
AttrFromDOM(close, close),
AttrFromDOM(open, open),
AttrFromDOM(separators_, separators_))
MARKUPMAP(menclose_,
New_HyperText,
roles::MATHML_ENCLOSED,
AttrFromDOM(notation_, notation_))
MARKUPMAP(mstyle_,
New_HyperText,
roles::MATHML_STYLE)
MARKUPMAP(msub_,
New_HyperText,
roles::MATHML_SUB)
MARKUPMAP(msup_,
New_HyperText,
roles::MATHML_SUP)
MARKUPMAP(msubsup_,
New_HyperText,
roles::MATHML_SUB_SUP)
MARKUPMAP(munder_,
New_HyperText,
roles::MATHML_UNDER,
AttrFromDOM(accentunder_, accentunder_),
AttrFromDOM(align, align))
MARKUPMAP(mover_,
New_HyperText,
roles::MATHML_OVER,
AttrFromDOM(accent_, accent_),
AttrFromDOM(align, align))
MARKUPMAP(munderover_,
New_HyperText,
roles::MATHML_UNDER_OVER,
AttrFromDOM(accent_, accent_),
AttrFromDOM(accentunder_, accentunder_),
AttrFromDOM(align, align))
MARKUPMAP(mmultiscripts_,
New_HyperText,
roles::MATHML_MULTISCRIPTS)
MARKUPMAP(mtable_,
New_HTMLTableAccessible,
roles::MATHML_TABLE,
AttrFromDOM(align, align),
AttrFromDOM(columnlines_, columnlines_),
AttrFromDOM(rowlines_, rowlines_))
MARKUPMAP(mlabeledtr_,
New_HTMLTableRowAccessible,
roles::MATHML_LABELED_ROW)
MARKUPMAP(mtr_,
New_HTMLTableRowAccessible,
roles::MATHML_TABLE_ROW)
MARKUPMAP(mtd_,
New_HTMLTableCellAccessible,
roles::MATHML_CELL)
MARKUPMAP(maction_,
New_HyperText,
roles::MATHML_ACTION,
AttrFromDOM(actiontype_, actiontype_),
AttrFromDOM(selection_, selection_))
MARKUPMAP(merror_,
New_HyperText,
roles::MATHML_ERROR)
MARKUPMAP(mstack_,
New_HyperText,
roles::MATHML_STACK,
AttrFromDOM(align, align),
AttrFromDOM(position, position))
MARKUPMAP(mlongdiv_,
New_HyperText,
roles::MATHML_LONG_DIVISION,
AttrFromDOM(longdivstyle_, longdivstyle_))
MARKUPMAP(msgroup_,
New_HyperText,
roles::MATHML_STACK_GROUP,
AttrFromDOM(position, position),
AttrFromDOM(shift_, shift_))
MARKUPMAP(msrow_,
New_HyperText,
roles::MATHML_STACK_ROW,
AttrFromDOM(position, position))
MARKUPMAP(mscarries_,
New_HyperText,
roles::MATHML_STACK_CARRIES,
AttrFromDOM(location_, location_),
AttrFromDOM(position, position))
MARKUPMAP(mscarry_,
New_HyperText,
roles::MATHML_STACK_CARRY,
AttrFromDOM(crossout_, crossout_))
MARKUPMAP(msline_,
New_HyperText,
roles::MATHML_STACK_LINE,
AttrFromDOM(position, position))
MARKUPMAP(nav,
New_HyperText,
roles::SECTION)
MARKUPMAP(ol,
New_HTMLList,
roles::LIST)
MARKUPMAP(option,
New_HTMLOption,
0)
MARKUPMAP(optgroup,
New_HTMLOptgroup,
0)
MARKUPMAP(output,
New_HTMLOutput,
roles::SECTION,
Attr(live, polite))
MARKUPMAP(p,
nullptr,
roles::PARAGRAPH)
MARKUPMAP(progress,
New_HTMLProgress,
0)
MARKUPMAP(q,
New_HyperText,
0)
MARKUPMAP(section,
New_HyperText,
roles::SECTION,
Attr(xmlroles, region))
MARKUPMAP(summary,
New_HTMLSummary,
roles::SUMMARY)
MARKUPMAP(time,
New_HyperText,
0,
Attr(xmlroles, time),
AttrFromDOM(datetime, datetime))
MARKUPMAP(td,
New_HTMLTableHeaderCellIfScope,
0)
MARKUPMAP(th,
New_HTMLTableHeaderCell,
0)
MARKUPMAP(ul,
New_HTMLList,
roles::LIST)

View file

@ -0,0 +1,947 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "NotificationController.h"
#include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "TextLeafAccessible.h"
#include "TextUpdater.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Telemetry.h"
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector
////////////////////////////////////////////////////////////////////////////////
NotificationController::NotificationController(DocAccessible* aDocument,
nsIPresShell* aPresShell) :
EventQueue(aDocument), mObservingState(eNotObservingRefresh),
mPresShell(aPresShell), mEventGeneration(0)
{
#ifdef DEBUG
mMoveGuardOnStack = false;
#endif
// Schedule initial accessible tree construction.
ScheduleProcessing();
}
NotificationController::~NotificationController()
{
NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
if (mDocument)
Shutdown();
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: AddRef/Release and cycle collection
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
if (tmp->mDocument)
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
for (auto it = tmp->mContentInsertions.ConstIter(); !it.Done(); it.Next()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions key");
cb.NoteXPCOMChild(it.Key());
nsTArray<nsCOMPtr<nsIContent>>* list = it.UserData();
for (uint32_t i = 0; i < list->Length(); i++) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mContentInsertions value item");
cb.NoteXPCOMChild(list->ElementAt(i));
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: public
void
NotificationController::Shutdown()
{
if (mObservingState != eNotObservingRefresh &&
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
mObservingState = eNotObservingRefresh;
}
// Shutdown handling child documents.
int32_t childDocCount = mHangingChildDocuments.Length();
for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
if (!mHangingChildDocuments[idx]->IsDefunct())
mHangingChildDocuments[idx]->Shutdown();
}
mHangingChildDocuments.Clear();
mDocument = nullptr;
mPresShell = nullptr;
mTextHash.Clear();
mContentInsertions.Clear();
mNotifications.Clear();
mEvents.Clear();
mRelocations.Clear();
mEventTree.Clear();
}
EventTree*
NotificationController::QueueMutation(Accessible* aContainer)
{
EventTree* tree = mEventTree.FindOrInsert(aContainer);
if (tree) {
ScheduleProcessing();
}
return tree;
}
bool
NotificationController::QueueMutationEvent(AccTreeMutationEvent* aEvent)
{
// We have to allow there to be a hide and then a show event for a target
// because of targets getting moved. However we need to coalesce a show and
// then a hide for a target which means we need to check for that here.
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
aEvent->GetAccessible()->ShowEventTarget()) {
AccTreeMutationEvent* showEvent = mMutationMap.GetEvent(aEvent->GetAccessible(), EventMap::ShowEvent);
DropMutationEvent(showEvent);
return false;
}
AccMutationEvent* mutEvent = downcast_accEvent(aEvent);
mEventGeneration++;
mutEvent->SetEventGeneration(mEventGeneration);
if (!mFirstMutationEvent) {
mFirstMutationEvent = aEvent;
ScheduleProcessing();
}
if (mLastMutationEvent) {
NS_ASSERTION(!mLastMutationEvent->NextEvent(), "why isn't the last event the end?");
mLastMutationEvent->SetNextEvent(aEvent);
}
aEvent->SetPrevEvent(mLastMutationEvent);
mLastMutationEvent = aEvent;
mMutationMap.PutEvent(aEvent);
// Because we could be hiding the target of a show event we need to get rid
// of any such events. It may be possible to do less than coallesce all
// events, however that is easiest.
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
CoalesceMutationEvents();
// mLastMutationEvent will point to something other than aEvent if and only
// if aEvent was just coalesced away. In that case a parent accessible
// must already have the required reorder and text change events so we are
// done here.
if (mLastMutationEvent != aEvent) {
return false;
}
}
// We need to fire a reorder event after all of the events targeted at shown or
// hidden children of a container. So either queue a new one, or move an
// existing one to the end of the queue if the container already has a
// reorder event.
Accessible* target = aEvent->GetAccessible();
Accessible* container = aEvent->GetAccessible()->Parent();
RefPtr<AccReorderEvent> reorder;
if (!container->ReorderEventTarget()) {
reorder = new AccReorderEvent(container);
container->SetReorderEventTarget(true);
mMutationMap.PutEvent(reorder);
// Since this is the first child of container that is changing, the name of
// container may be changing.
QueueNameChange(target);
} else {
AccReorderEvent* event = downcast_accEvent(mMutationMap.GetEvent(container, EventMap::ReorderEvent));
reorder = event;
if (mFirstMutationEvent == event) {
mFirstMutationEvent = event->NextEvent();
} else {
event->PrevEvent()->SetNextEvent(event->NextEvent());
}
event->NextEvent()->SetPrevEvent(event->PrevEvent());
event->SetNextEvent(nullptr);
}
reorder->SetEventGeneration(mEventGeneration);
reorder->SetPrevEvent(mLastMutationEvent);
mLastMutationEvent->SetNextEvent(reorder);
mLastMutationEvent = reorder;
// It is not possible to have a text change event for something other than a
// hyper text accessible.
if (!container->IsHyperText()) {
return true;
}
MOZ_ASSERT(mutEvent);
nsString text;
aEvent->GetAccessible()->AppendTextTo(text);
if (text.IsEmpty()) {
return true;
}
int32_t offset = container->AsHyperText()->GetChildOffset(target);
AccTreeMutationEvent* prevEvent = aEvent->PrevEvent();
while (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
prevEvent = prevEvent->PrevEvent();
}
if (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
mutEvent->IsHide()) {
AccHideEvent* prevHide = downcast_accEvent(prevEvent);
AccTextChangeEvent* prevTextChange = prevHide->mTextChangeEvent;
if (prevTextChange && prevHide->Parent() == mutEvent->Parent()) {
if (prevHide->mNextSibling == target) {
target->AppendTextTo(prevTextChange->mModifiedText);
prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
} else if (prevHide->mPrevSibling == target) {
nsString temp;
target->AppendTextTo(temp);
uint32_t extraLen = temp.Length();
temp += prevTextChange->mModifiedText;;
prevTextChange->mModifiedText = temp;
prevTextChange->mStart -= extraLen;
prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
}
}
} else if (prevEvent && mutEvent->IsShow() &&
prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
AccShowEvent* prevShow = downcast_accEvent(prevEvent);
AccTextChangeEvent* prevTextChange = prevShow->mTextChangeEvent;
if (prevTextChange && prevShow->Parent() == target->Parent()) {
int32_t index = target->IndexInParent();
int32_t prevIndex = prevShow->GetAccessible()->IndexInParent();
if (prevIndex + 1 == index) {
target->AppendTextTo(prevTextChange->mModifiedText);
prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
} else if (index + 1 == prevIndex) {
nsString temp;
target->AppendTextTo(temp);
prevTextChange->mStart -= temp.Length();
temp += prevTextChange->mModifiedText;
prevTextChange->mModifiedText = temp;
prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
}
}
}
if (!mutEvent->mTextChangeEvent) {
mutEvent->mTextChangeEvent =
new AccTextChangeEvent(container, offset, text, mutEvent->IsShow(),
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}
return true;
}
void
NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent)
{
// unset the event bits since the event isn't being fired any more.
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
aEvent->GetAccessible()->SetReorderEventTarget(false);
} else if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
aEvent->GetAccessible()->SetShowEventTarget(false);
} else {
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
MOZ_ASSERT(hideEvent);
if (hideEvent->NeedsShutdown()) {
mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
}
}
// Do the work to splice the event out of the list.
if (mFirstMutationEvent == aEvent) {
mFirstMutationEvent = aEvent->NextEvent();
} else {
aEvent->PrevEvent()->SetNextEvent(aEvent->NextEvent());
}
if (mLastMutationEvent == aEvent) {
mLastMutationEvent = aEvent->PrevEvent();
} else {
aEvent->NextEvent()->SetPrevEvent(aEvent->PrevEvent());
}
aEvent->SetPrevEvent(nullptr);
aEvent->SetNextEvent(nullptr);
mMutationMap.RemoveEvent(aEvent);
}
void
NotificationController::CoalesceMutationEvents()
{
AccTreeMutationEvent* event = mFirstMutationEvent;
while (event) {
AccTreeMutationEvent* nextEvent = event->NextEvent();
uint32_t eventType = event->GetEventType();
if (event->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
Accessible* acc = event->GetAccessible();
while (acc) {
if (acc->IsDoc()) {
break;
}
// if a parent of the reorder event's target is being hidden that
// hide event's target must have a parent that is also a reorder event
// target. That means we don't need this reorder event.
if (acc->HideEventTarget()) {
DropMutationEvent(event);
break;
}
Accessible* parent = acc->Parent();
if (parent->ReorderEventTarget()) {
AccReorderEvent* reorder = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ReorderEvent));
// We want to make sure that a reorder event comes after any show or
// hide events targeted at the children of its target. We keep the
// invariant that event generation goes up as you are farther in the
// queue, so we want to use the spot of the event with the higher
// generation number, and keep that generation number.
if (reorder && reorder->EventGeneration() < event->EventGeneration()) {
reorder->SetEventGeneration(event->EventGeneration());
// It may be true that reorder was before event, and we coalesced
// away all the show / hide events between them. In that case
// event is already immediately after reorder in the queue and we
// do not need to rearrange the list of events.
if (event != reorder->NextEvent()) {
// There really should be a show or hide event before the first
// reorder event.
if (reorder->PrevEvent()) {
reorder->PrevEvent()->SetNextEvent(reorder->NextEvent());
} else {
mFirstMutationEvent = reorder->NextEvent();
}
reorder->NextEvent()->SetPrevEvent(reorder->PrevEvent());
event->PrevEvent()->SetNextEvent(reorder);
reorder->SetPrevEvent(event->PrevEvent());
event->SetPrevEvent(reorder);
reorder->SetNextEvent(event);
}
}
DropMutationEvent(event);
break;
}
acc = parent;
}
} else if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
Accessible* parent = event->GetAccessible()->Parent();
while (parent) {
if (parent->IsDoc()) {
break;
}
// if the parent of a show event is being either shown or hidden then
// we don't need to fire a show event for a subtree of that change.
if (parent->ShowEventTarget() || parent->HideEventTarget()) {
DropMutationEvent(event);
break;
}
parent = parent->Parent();
}
} else {
MOZ_ASSERT(eventType == nsIAccessibleEvent::EVENT_HIDE, "mutation event list has an invalid event");
AccHideEvent* hideEvent = downcast_accEvent(event);
Accessible* parent = hideEvent->Parent();
while (parent) {
if (parent->IsDoc()) {
break;
}
if (parent->HideEventTarget()) {
DropMutationEvent(event);
break;
}
if (parent->ShowEventTarget()) {
AccShowEvent* showEvent = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ShowEvent));
if (showEvent->EventGeneration() < hideEvent->EventGeneration()) {
DropMutationEvent(hideEvent);
break;
}
}
parent = parent->Parent();
}
}
event = nextEvent;
}
}
void
NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
{
// Schedule child document binding to the tree.
mHangingChildDocuments.AppendElement(aDocument);
ScheduleProcessing();
}
void
NotificationController::ScheduleContentInsertion(Accessible* aContainer,
nsIContent* aStartChildNode,
nsIContent* aEndChildNode)
{
nsTArray<nsCOMPtr<nsIContent>>* list =
mContentInsertions.LookupOrAdd(aContainer);
bool needsProcessing = false;
nsIContent* node = aStartChildNode;
while (node != aEndChildNode) {
// Notification triggers for content insertion even if no content was
// actually inserted, check if the given content has a frame to discard
// this case early.
if (node->GetPrimaryFrame()) {
if (list->AppendElement(node))
needsProcessing = true;
}
node = node->GetNextSibling();
}
if (needsProcessing) {
ScheduleProcessing();
}
}
void
NotificationController::ScheduleProcessing()
{
// If notification flush isn't planed yet start notification flush
// asynchronously (after style and layout).
if (mObservingState == eNotObservingRefresh) {
if (mPresShell->AddRefreshObserver(this, Flush_Display))
mObservingState = eRefreshObserving;
}
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: protected
bool
NotificationController::IsUpdatePending()
{
return mPresShell->IsLayoutFlushObserver() ||
mObservingState == eRefreshProcessingForUpdate ||
mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
mTextHash.Count() != 0 ||
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
}
void
NotificationController::ProcessMutationEvents()
{
// there is no reason to fire a hide event for a child of a show event
// target. That can happen if something is inserted into the tree and
// removed before the next refresh driver tick, but it should not be
// observable outside gecko so it should be safe to coalesce away any such
// events. This means that it should be fine to fire all of the hide events
// first, and then deal with any shown subtrees.
for (AccTreeMutationEvent* event = mFirstMutationEvent;
event; event = event->NextEvent()) {
if (event->GetEventType() != nsIAccessibleEvent::EVENT_HIDE) {
continue;
}
nsEventShell::FireEvent(event);
if (!mDocument) {
return;
}
AccMutationEvent* mutEvent = downcast_accEvent(event);
if (mutEvent->mTextChangeEvent) {
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
if (!mDocument) {
return;
}
}
// Fire menupopup end event before a hide event if a menu goes away.
// XXX: We don't look into children of hidden subtree to find hiding
// menupopup (as we did prior bug 570275) because we don't do that when
// menu is showing (and that's impossible until bug 606924 is fixed).
// Nevertheless we should do this at least because layout coalesces
// the changes before our processing and we may miss some menupopup
// events. Now we just want to be consistent in content insertion/removal
// handling.
if (event->mAccessible->ARIARole() == roles::MENUPOPUP) {
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
event->mAccessible);
if (!mDocument) {
return;
}
}
AccHideEvent* hideEvent = downcast_accEvent(event);
if (hideEvent->NeedsShutdown()) {
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
}
}
// Group the show events by the parent of their target.
nsDataHashtable<nsPtrHashKey<Accessible>, nsTArray<AccTreeMutationEvent*>> showEvents;
for (AccTreeMutationEvent* event = mFirstMutationEvent;
event; event = event->NextEvent()) {
if (event->GetEventType() != nsIAccessibleEvent::EVENT_SHOW) {
continue;
}
Accessible* parent = event->GetAccessible()->Parent();
showEvents.GetOrInsert(parent).AppendElement(event);
}
// We need to fire show events for the children of an accessible in the order
// of their indices at this point. So sort each set of events for the same
// container by the index of their target.
for (auto iter = showEvents.Iter(); !iter.Done(); iter.Next()) {
struct AccIdxComparator {
bool LessThan(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
{
int32_t aIdx = a->GetAccessible()->IndexInParent();
int32_t bIdx = b->GetAccessible()->IndexInParent();
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
return aIdx < bIdx;
}
bool Equals(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
{
DebugOnly<int32_t> aIdx = a->GetAccessible()->IndexInParent();
DebugOnly<int32_t> bIdx = b->GetAccessible()->IndexInParent();
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
return false;
}
};
nsTArray<AccTreeMutationEvent*>& events = iter.Data();
events.Sort(AccIdxComparator());
for (AccTreeMutationEvent* event: events) {
nsEventShell::FireEvent(event);
if (!mDocument) {
return;
}
AccMutationEvent* mutEvent = downcast_accEvent(event);
if (mutEvent->mTextChangeEvent) {
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
if (!mDocument) {
return;
}
}
}
}
// Now we can fire the reorder events after all the show and hide events.
for (AccTreeMutationEvent* event = mFirstMutationEvent;
event; event = event->NextEvent()) {
if (event->GetEventType() != nsIAccessibleEvent::EVENT_REORDER) {
continue;
}
nsEventShell::FireEvent(event);
if (!mDocument) {
return;
}
Accessible* target = event->GetAccessible();
target->Document()->MaybeNotifyOfValueChange(target);
if (!mDocument) {
return;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: private
void
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
// If the document accessible that notification collector was created for is
// now shut down, don't process notifications anymore.
NS_ASSERTION(mDocument,
"The document was shut down while refresh observer is attached!");
if (!mDocument)
return;
if (mObservingState == eRefreshProcessing ||
mObservingState == eRefreshProcessingForUpdate)
return;
// Any generic notifications should be queued if we're processing content
// insertions or generic notifications.
mObservingState = eRefreshProcessingForUpdate;
// Initial accessible tree construction.
if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
// If document is not bound to parent at this point then the document is not
// ready yet (process notifications later).
if (!mDocument->IsBoundToParent()) {
mObservingState = eRefreshObserving;
return;
}
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin("TREE", "initial tree created");
logging::Address("document", mDocument);
logging::MsgEnd();
}
#endif
mDocument->DoInitialUpdate();
NS_ASSERTION(mContentInsertions.Count() == 0,
"Pending content insertions while initial accessible tree isn't created!");
}
// Initialize scroll support if needed.
if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
mDocument->AddScrollListener();
// Process rendered text change notifications.
for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtrHashKey<nsIContent>* entry = iter.Get();
nsIContent* textNode = entry->GetKey();
Accessible* textAcc = mDocument->GetAccessible(textNode);
// If the text node is not in tree or doesn't have frame then this case should
// have been handled already by content removal notifications.
nsINode* containerNode = textNode->GetParentNode();
if (!containerNode) {
NS_ASSERTION(!textAcc,
"Text node was removed but accessible is kept alive!");
continue;
}
nsIFrame* textFrame = textNode->GetPrimaryFrame();
if (!textFrame) {
NS_ASSERTION(!textAcc,
"Text node isn't rendered but accessible is kept alive!");
continue;
}
nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nullptr;
nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
// Remove text accessible if rendered text is empty.
if (textAcc) {
if (text.mString.IsEmpty()) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree | logging::eText)) {
logging::MsgBegin("TREE", "text node lost its content; doc: %p", mDocument);
logging::Node("container", containerElm);
logging::Node("content", textNode);
logging::MsgEnd();
}
#endif
mDocument->ContentRemoved(containerElm, textNode);
continue;
}
// Update text of the accessible and fire text change events.
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eText)) {
logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
logging::Node("container", containerElm);
logging::Node("content", textNode);
logging::MsgEntry("old text '%s'",
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
logging::MsgEntry("new text: '%s'",
NS_ConvertUTF16toUTF8(text.mString).get());
logging::MsgEnd();
}
#endif
TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString);
continue;
}
// Append an accessible if rendered text is not empty.
if (!text.mString.IsEmpty()) {
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree | logging::eText)) {
logging::MsgBegin("TREE", "text node gains new content; doc: %p", mDocument);
logging::Node("container", containerElm);
logging::Node("content", textNode);
logging::MsgEnd();
}
#endif
Accessible* container = mDocument->AccessibleOrTrueContainer(containerNode);
MOZ_ASSERT(container,
"Text node having rendered text hasn't accessible document!");
if (container) {
nsTArray<nsCOMPtr<nsIContent>>* list =
mContentInsertions.LookupOrAdd(container);
list->AppendElement(textNode);
}
}
}
mTextHash.Clear();
// Process content inserted notifications to update the tree.
for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
if (!mDocument) {
return;
}
}
mContentInsertions.Clear();
// Bind hanging child documents.
uint32_t hangingDocCnt = mHangingChildDocuments.Length();
nsTArray<RefPtr<DocAccessible>> newChildDocs;
for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
DocAccessible* childDoc = mHangingChildDocuments[idx];
if (childDoc->IsDefunct())
continue;
nsIContent* ownerContent = mDocument->DocumentNode()->
FindContentForSubDocument(childDoc->DocumentNode());
if (ownerContent) {
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
if (mDocument->AppendChildDocument(childDoc)) {
newChildDocs.AppendElement(Move(mHangingChildDocuments[idx]));
continue;
}
outerDocAcc->RemoveChild(childDoc);
}
// Failed to bind the child document, destroy it.
childDoc->Shutdown();
}
}
mHangingChildDocuments.Clear();
// If the document is ready and all its subdocuments are completely loaded
// then process the document load.
if (mDocument->HasLoadState(DocAccessible::eReady) &&
!mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
hangingDocCnt == 0) {
uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
for (; childDocIdx < childDocCnt; childDocIdx++) {
DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
break;
}
if (childDocIdx == childDocCnt) {
mDocument->ProcessLoad();
if (!mDocument)
return;
}
}
// 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.
mDocument->ProcessInvalidationList();
// We cannot rely on DOM tree to keep aria-owns relations updated. Make
// a validation to remove dead links.
mDocument->ValidateARIAOwned();
// Process relocation list.
for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
if (mRelocations[idx]->IsInDocument()) {
mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
}
}
mRelocations.Clear();
// 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.
mObservingState = eRefreshProcessing;
CoalesceMutationEvents();
ProcessMutationEvents();
mEventGeneration = 0;
// Now that we are done with them get rid of the events we fired.
RefPtr<AccTreeMutationEvent> mutEvent = Move(mFirstMutationEvent);
mLastMutationEvent = nullptr;
mFirstMutationEvent = nullptr;
while (mutEvent) {
RefPtr<AccTreeMutationEvent> nextEvent = mutEvent->NextEvent();
Accessible* target = mutEvent->GetAccessible();
// We need to be careful here, while it may seem that we can simply 0 all
// the pending event bits that is not true. Because accessibles may be
// reparented they may be the target of both a hide event and a show event
// at the same time.
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
target->SetShowEventTarget(false);
}
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
target->SetHideEventTarget(false);
}
// However it is not possible for a reorder event target to also be the
// target of a show or hide, so we can just zero that.
target->SetReorderEventTarget(false);
mutEvent->SetPrevEvent(nullptr);
mutEvent->SetNextEvent(nullptr);
mMutationMap.RemoveEvent(mutEvent);
mutEvent = nextEvent;
}
ProcessEventQueue();
if (IPCAccessibilityActive()) {
size_t newDocCount = newChildDocs.Length();
for (size_t i = 0; i < newDocCount; i++) {
DocAccessible* childDoc = newChildDocs[i];
if (childDoc->IsDefunct()) {
continue;
}
Accessible* parent = childDoc->Parent();
DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
MOZ_ASSERT(id);
DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
if (ipcDoc) {
parentIPCDoc->SendBindChildDoc(ipcDoc, id);
continue;
}
ipcDoc = new DocAccessibleChild(childDoc);
childDoc->SetIPCDoc(ipcDoc);
#if defined(XP_WIN)
MOZ_ASSERT(parentIPCDoc);
parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
AccessibleWrap::GetChildIDFor(childDoc));
#else
nsCOMPtr<nsITabChild> tabChild =
do_GetInterface(mDocument->DocumentNode()->GetDocShell());
if (tabChild) {
MOZ_ASSERT(parentIPCDoc);
static_cast<TabChild*>(tabChild.get())->
SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
}
#endif
}
}
mObservingState = eRefreshObserving;
if (!mDocument)
return;
// Stop further processing if there are no new notifications of any kind or
// events and document load is processed.
if (mContentInsertions.Count() == 0 && mNotifications.IsEmpty() &&
mEvents.IsEmpty() && mTextHash.Count() == 0 &&
mHangingChildDocuments.IsEmpty() &&
mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
mObservingState = eNotObservingRefresh;
}
}
void
NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent)
{
EventType type = GetEventType(aEvent);
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
addr |= type;
mTable.Put(addr, aEvent);
}
AccTreeMutationEvent*
NotificationController::EventMap::GetEvent(Accessible* aTarget, EventType aType)
{
uint64_t addr = reinterpret_cast<uintptr_t>(aTarget);
MOZ_ASSERT((addr & 0x3) == 0, "target is not 4 byte aligned");
addr |= aType;
return mTable.GetWeak(addr);
}
void
NotificationController::EventMap::RemoveEvent(AccTreeMutationEvent* aEvent)
{
EventType type = GetEventType(aEvent);
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
addr |= type;
MOZ_ASSERT(mTable.GetWeak(addr) == aEvent, "mTable has the wrong event");
mTable.Remove(addr);
}
NotificationController::EventMap::EventType
NotificationController::EventMap::GetEventType(AccTreeMutationEvent* aEvent)
{
switch(aEvent->GetEventType())
{
case nsIAccessibleEvent::EVENT_SHOW:
return ShowEvent;
case nsIAccessibleEvent::EVENT_HIDE:
return HideEvent;
case nsIAccessibleEvent::EVENT_REORDER:
return ReorderEvent;
default:
MOZ_ASSERT_UNREACHABLE("event has invalid type");
return ShowEvent;
}
}

View file

@ -0,0 +1,439 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_NotificationController_h_
#define mozilla_a11y_NotificationController_h_
#include "EventQueue.h"
#include "EventTree.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Tuple.h"
#include "nsCycleCollectionParticipant.h"
#include "nsRefreshDriver.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
namespace mozilla {
namespace a11y {
class DocAccessible;
/**
* Notification interface.
*/
class Notification
{
public:
NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::Notification)
/**
* Process notification.
*/
virtual void Process() = 0;
protected:
Notification() { }
/**
* Protected destructor, to discourage deletion outside of Release():
*/
virtual ~Notification() { }
private:
Notification(const Notification&);
Notification& operator = (const Notification&);
};
/**
* Template class for generic notification.
*
* @note Instance is kept as a weak ref, the caller must guarantee it exists
* longer than the document accessible owning the notification controller
* that this notification is processed by.
*/
template<class Class, class ... Args>
class TNotification : public Notification
{
public:
typedef void (Class::*Callback)(Args* ...);
TNotification(Class* aInstance, Callback aCallback, Args* ... aArgs) :
mInstance(aInstance), mCallback(aCallback), mArgs(aArgs...) { }
virtual ~TNotification() { mInstance = nullptr; }
virtual void Process() override
{ ProcessHelper(typename IndexSequenceFor<Args...>::Type()); }
private:
TNotification(const TNotification&);
TNotification& operator = (const TNotification&);
template <size_t... Indices>
void ProcessHelper(IndexSequence<Indices...>)
{
(mInstance->*mCallback)(Get<Indices>(mArgs)...);
}
Class* mInstance;
Callback mCallback;
Tuple<RefPtr<Args> ...> mArgs;
};
/**
* Used to process notifications from core for the document accessible.
*/
class NotificationController final : public EventQueue,
public nsARefreshObserver
{
public:
NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
/**
* Shutdown the notification controller.
*/
void Shutdown();
/**
* Add an accessible event into the queue to process it later.
*/
void QueueEvent(AccEvent* aEvent)
{
if (PushEvent(aEvent)) {
ScheduleProcessing();
}
}
/**
* Creates and adds a name change event into the queue for a container of
* the given accessible, if the accessible is a part of name computation of
* the container.
*/
void QueueNameChange(Accessible* aChangeTarget)
{
if (PushNameChange(aChangeTarget)) {
ScheduleProcessing();
}
}
/**
* Returns existing event tree for the given the accessible or creates one if
* it doesn't exists yet.
*/
EventTree* QueueMutation(Accessible* aContainer);
class MoveGuard final {
public:
explicit MoveGuard(NotificationController* aController) :
mController(aController)
{
#ifdef DEBUG
MOZ_ASSERT(!mController->mMoveGuardOnStack,
"Move guard is on stack already!");
mController->mMoveGuardOnStack = true;
#endif
}
~MoveGuard() {
#ifdef DEBUG
MOZ_ASSERT(mController->mMoveGuardOnStack, "No move guard on stack!");
mController->mMoveGuardOnStack = false;
#endif
mController->mPrecedingEvents.Clear();
}
private:
NotificationController* mController;
};
#ifdef A11Y_LOG
const EventTree& RootEventTree() const { return mEventTree; };
#endif
/**
* Queue a mutation event to emit if not coalesced away. Returns true if the
* event was queued and has not yet been coalesced.
*/
bool QueueMutationEvent(AccTreeMutationEvent* aEvent);
/**
* Coalesce all queued mutation events.
*/
void CoalesceMutationEvents();
/**
* Schedule binding the child document to the tree of this document.
*/
void ScheduleChildDocBinding(DocAccessible* aDocument);
/**
* Schedule the accessible tree update because of rendered text changes.
*/
inline void ScheduleTextUpdate(nsIContent* aTextNode)
{
// Make sure we are not called with a node that is not in the DOM tree or
// not visible.
MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM");
MOZ_ASSERT(aTextNode->GetPrimaryFrame(), "A text node doesn't have a frame");
MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
"A text node is not visible");
mTextHash.PutEntry(aTextNode);
ScheduleProcessing();
}
/**
* Pend accessible tree update for content insertion.
*/
void ScheduleContentInsertion(Accessible* aContainer,
nsIContent* aStartChildNode,
nsIContent* aEndChildNode);
/**
* Pend an accessible subtree relocation.
*/
void ScheduleRelocation(Accessible* aOwner)
{
if (!mRelocations.Contains(aOwner) && mRelocations.AppendElement(aOwner)) {
ScheduleProcessing();
}
}
/**
* Start to observe refresh to make notifications and events processing after
* layout.
*/
void ScheduleProcessing();
/**
* Process the generic notification synchronously if there are no pending
* layout changes and no notifications are pending or being processed right
* now. Otherwise, queue it up to process asynchronously.
*
* @note The caller must guarantee that the given instance still exists when
* the notification is processed.
*/
template<class Class, class Arg>
inline void HandleNotification(Class* aInstance,
typename TNotification<Class, Arg>::Callback aMethod,
Arg* aArg)
{
if (!IsUpdatePending()) {
#ifdef A11Y_LOG
if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications))
mozilla::a11y::logging::Text("sync notification processing");
#endif
(aInstance->*aMethod)(aArg);
return;
}
RefPtr<Notification> notification =
new TNotification<Class, Arg>(aInstance, aMethod, aArg);
if (notification && mNotifications.AppendElement(notification))
ScheduleProcessing();
}
/**
* Schedule the generic notification to process asynchronously.
*
* @note The caller must guarantee that the given instance still exists when
* the notification is processed.
*/
template<class Class>
inline void ScheduleNotification(Class* aInstance,
typename TNotification<Class>::Callback aMethod)
{
RefPtr<Notification> notification =
new TNotification<Class>(aInstance, aMethod);
if (notification && mNotifications.AppendElement(notification))
ScheduleProcessing();
}
#ifdef DEBUG
bool IsUpdating() const
{ return mObservingState == eRefreshProcessingForUpdate; }
#endif
protected:
virtual ~NotificationController();
nsCycleCollectingAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
/**
* Return true if the accessible tree state update is pending.
*/
bool IsUpdatePending();
private:
NotificationController(const NotificationController&);
NotificationController& operator = (const NotificationController&);
// nsARefreshObserver
virtual void WillRefresh(mozilla::TimeStamp aTime) override;
/**
* Set and returns a hide event, paired with a show event, for the move.
*/
void WithdrawPrecedingEvents(nsTArray<RefPtr<AccHideEvent>>* aEvs)
{
if (mPrecedingEvents.Length() > 0) {
aEvs->AppendElements(mozilla::Move(mPrecedingEvents));
}
}
void StorePrecedingEvent(AccHideEvent* aEv)
{
MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
mPrecedingEvents.AppendElement(aEv);
}
void StorePrecedingEvents(nsTArray<RefPtr<AccHideEvent>>&& aEvs)
{
MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
mPrecedingEvents.InsertElementsAt(0, aEvs);
}
private:
/**
* get rid of a mutation event that is no longer necessary.
*/
void DropMutationEvent(AccTreeMutationEvent* aEvent);
/**
* Fire all necessary mutation events.
*/
void ProcessMutationEvents();
/**
* Indicates whether we're waiting on an event queue processing from our
* notification controller to flush events.
*/
enum eObservingState {
eNotObservingRefresh,
eRefreshObserving,
eRefreshProcessing,
eRefreshProcessingForUpdate
};
eObservingState mObservingState;
/**
* The presshell of the document accessible.
*/
nsIPresShell* mPresShell;
/**
* Child documents that needs to be bound to the tree.
*/
nsTArray<RefPtr<DocAccessible> > mHangingChildDocuments;
/**
* Pending accessible tree update notifications for content insertions.
*/
nsClassHashtable<nsRefPtrHashKey<Accessible>,
nsTArray<nsCOMPtr<nsIContent>>> mContentInsertions;
template<class T>
class nsCOMPtrHashKey : public PLDHashEntryHdr
{
public:
typedef T* KeyType;
typedef const T* KeyTypePointer;
explicit nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
explicit nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {}
~nsCOMPtrHashKey() { }
KeyType GetKey() const { return mKey; }
bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey)
{ return NS_PTR_TO_INT32(aKey) >> 2; }
enum { ALLOW_MEMMOVE = true };
protected:
nsCOMPtr<T> mKey;
};
/**
* Pending accessible tree update notifications for rendered text changes.
*/
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
/**
* Other notifications like DOM events. Don't make this an AutoTArray; we
* use SwapElements() on it.
*/
nsTArray<RefPtr<Notification> > mNotifications;
/**
* Holds all scheduled relocations.
*/
nsTArray<RefPtr<Accessible> > mRelocations;
/**
* Holds all mutation events.
*/
EventTree mEventTree;
/**
* A temporary collection of hide events that should be fired before related
* show event. Used by EventTree.
*/
nsTArray<RefPtr<AccHideEvent>> mPrecedingEvents;
#ifdef DEBUG
bool mMoveGuardOnStack;
#endif
friend class MoveGuard;
friend class EventTree;
/**
* A list of all mutation events we may want to emit. Ordered from the first
* event that should be emitted to the last one to emit.
*/
RefPtr<AccTreeMutationEvent> mFirstMutationEvent;
RefPtr<AccTreeMutationEvent> mLastMutationEvent;
/**
* A class to map an accessible and event type to an event.
*/
class EventMap
{
public:
enum EventType
{
ShowEvent = 0x0,
HideEvent = 0x1,
ReorderEvent = 0x2,
};
void PutEvent(AccTreeMutationEvent* aEvent);
AccTreeMutationEvent* GetEvent(Accessible* aTarget, EventType aType);
void RemoveEvent(AccTreeMutationEvent* aEvent);
void Clear() { mTable.Clear(); }
private:
EventType GetEventType(AccTreeMutationEvent* aEvent);
nsRefPtrHashtable<nsUint64HashKey, AccTreeMutationEvent> mTable;
};
EventMap mMutationMap;
uint32_t mEventGeneration;
};
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11y_NotificationController_h_

View file

@ -0,0 +1,88 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_Platform_h
#define mozilla_a11y_Platform_h
#include <stdint.h>
class nsString;
namespace mozilla {
namespace a11y {
class ProxyAccessible;
enum EPlatformDisabledState {
ePlatformIsForceEnabled = -1,
ePlatformIsEnabled = 0,
ePlatformIsDisabled = 1
};
/**
* Return the platform disabled state.
*/
EPlatformDisabledState PlatformDisabledState();
#ifdef MOZ_ACCESSIBILITY_ATK
/**
* Perform initialization that should be done as soon as possible, in order
* to minimize startup time.
* XXX: this function and the next defined in ApplicationAccessibleWrap.cpp
*/
void PreInit();
#endif
#if defined(MOZ_ACCESSIBILITY_ATK) || defined(XP_MACOSX)
/**
* Is platform accessibility enabled.
* Only used on linux with atk and MacOS for now.
*/
bool ShouldA11yBeEnabled();
#endif
/**
* Called to initialize platform specific accessibility support.
* Note this is called after internal accessibility support is initialized.
*/
void PlatformInit();
/**
* Shutdown platform accessibility.
* Note this is called before internal accessibility support is shutdown.
*/
void PlatformShutdown();
/**
* called when a new ProxyAccessible is created, so the platform may setup a
* wrapper for it, or take other action.
*/
void ProxyCreated(ProxyAccessible* aProxy, uint32_t aInterfaces);
/**
* Called just before a ProxyAccessible is destroyed so its wrapper can be
* disposed of and other action taken.
*/
void ProxyDestroyed(ProxyAccessible*);
/**
* Callied when an event is fired on a proxied accessible.
*/
void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType);
void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState,
bool aEnabled);
void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset);
void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
int32_t aStart, uint32_t aLen, bool aIsInsert,
bool aFromUser);
void ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible* aParent,
bool aInsert, bool aFromUser);
void ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible* aWidget,
uint32_t aType);
} // namespace a11y
} // namespace mozilla
#endif // mozilla_a11y_Platform_h

108
accessible/base/Relation.h Normal file
View file

@ -0,0 +1,108 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_relation_h_
#define mozilla_a11y_relation_h_
#include "AccIterator.h"
#include <memory>
namespace mozilla {
namespace a11y {
/**
* A collection of relation targets of a certain type. Targets are computed
* lazily while enumerating.
*/
class Relation
{
public:
Relation() : mFirstIter(nullptr), mLastIter(nullptr) { }
explicit Relation(AccIterable* aIter) :
mFirstIter(aIter), mLastIter(aIter) { }
explicit Relation(Accessible* aAcc) :
mFirstIter(nullptr), mLastIter(nullptr)
{ AppendTarget(aAcc); }
Relation(DocAccessible* aDocument, nsIContent* aContent) :
mFirstIter(nullptr), mLastIter(nullptr)
{ AppendTarget(aDocument, aContent); }
Relation(Relation&& aOther) :
mFirstIter(Move(aOther.mFirstIter)), mLastIter(aOther.mLastIter)
{
aOther.mLastIter = nullptr;
}
Relation& operator = (Relation&& aRH)
{
mFirstIter = Move(aRH.mFirstIter);
mLastIter = aRH.mLastIter;
aRH.mLastIter = nullptr;
return *this;
}
inline void AppendIter(AccIterable* aIter)
{
if (mLastIter)
mLastIter->mNextIter.reset(aIter);
else
mFirstIter.reset(aIter);
mLastIter = aIter;
}
/**
* Append the given accessible to the set of related accessibles.
*/
inline void AppendTarget(Accessible* aAcc)
{
if (aAcc)
AppendIter(new SingleAccIterator(aAcc));
}
/**
* Append the one accessible for this content node to the set of related
* accessibles.
*/
void AppendTarget(DocAccessible* aDocument, nsIContent* aContent)
{
if (aContent)
AppendTarget(aDocument->GetAccessible(aContent));
}
/**
* compute and return the next related accessible.
*/
inline Accessible* Next()
{
Accessible* target = nullptr;
while (mFirstIter && !(target = mFirstIter->Next()))
mFirstIter = std::move(mFirstIter->mNextIter);
if (!mFirstIter)
mLastIter = nullptr;
return target;
}
private:
Relation& operator = (const Relation&) = delete;
Relation(const Relation&) = delete;
std::unique_ptr<AccIterable> mFirstIter;
AccIterable* mLastIter;
};
} // namespace a11y
} // namespace mozilla
#endif

View file

@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_relationtype_h_
#define mozilla_a11y_relationtype_h_
namespace mozilla {
namespace a11y {
enum class RelationType {
/**
* This object is labelled by a target object.
*/
LABELLED_BY = 0x00,
/**
* This object is label for a target object.
*/
LABEL_FOR = 0x01,
/**
* This object is described by the target object.
*/
DESCRIBED_BY = 0x02,
/**
* This object is describes the target object.
*/
DESCRIPTION_FOR = 0x3,
/**
* This object is a child of a target object.
*/
NODE_CHILD_OF = 0x4,
/**
* This object is a parent of a target object. A dual relation to
* NODE_CHILD_OF.
*/
NODE_PARENT_OF = 0x5,
/**
* Some attribute of this object is affected by a target object.
*/
CONTROLLED_BY = 0x06,
/**
* This object is interactive and controls some attribute of a target object.
*/
CONTROLLER_FOR = 0x07,
/**
* Content flows from this object to a target object, i.e. has content that
* flows logically to another object in a sequential way, e.g. text flow.
*/
FLOWS_TO = 0x08,
/**
* Content flows to this object from a target object, i.e. has content that
* flows logically from another object in a sequential way, e.g. text flow.
*/
FLOWS_FROM = 0x09,
/**
* This object is a member of a group of one or more objects. When there is
* more than one object in the group each member may have one and the same
* target, e.g. a grouping object. It is also possible that each member has
* multiple additional targets, e.g. one for every other member in the group.
*/
MEMBER_OF = 0x0a,
/**
* This object is a sub window of a target object.
*/
SUBWINDOW_OF = 0x0b,
/**
* This object embeds a target object. This relation can be used on the
* OBJID_CLIENT accessible for a top level window to show where the content
* areas are.
*/
EMBEDS = 0x0c,
/**
* This object is embedded by a target object.
*/
EMBEDDED_BY = 0x0d,
/**
* This object is a transient component related to the target object. When
* this object is activated the target object doesn't lose focus.
*/
POPUP_FOR = 0x0e,
/**
* This object is a parent window of the target object.
*/
PARENT_WINDOW_OF = 0x0f,
/**
* Part of a form/dialog with a related default button. It is used for
* MSAA/XPCOM, it isn't for IA2 or ATK.
*/
DEFAULT_BUTTON = 0x10,
/**
* The target object is the containing document object.
*/
CONTAINING_DOCUMENT = 0x11,
/**
* The target object is the topmost containing document object in the tab pane.
*/
CONTAINING_TAB_PANE = 0x12,
/**
* The target object is the containing window object.
*/
CONTAINING_WINDOW = 0x13,
/**
* The target object is the containing application object.
*/
CONTAINING_APPLICATION = 0x14,
/**
* The target object provides the detailed, extended description for this
* object. It provides more detailed information than would normally be
* provided using the DESCRIBED_BY relation. A common use for this relation is
* in digital publishing where an extended description needs to be conveyed in
* a book that requires structural markup or the embedding of other technology
* to provide illustrative content.
*/
DETAILS = 0x15,
/**
* This object provides the detailed, extended description for the target
* object. See DETAILS relation.
*/
DETAILS_FOR = 0x16,
/**
* The target object is the error message for this object.
*/
ERRORMSG = 0x17,
/**
* This object is the error message for the target object.
*/
ERRORMSG_FOR = 0x18,
LAST = ERRORMSG_FOR
};
} // namespace a11y
} // namespace mozilla
#endif

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