new file mode 100644
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,4 @@
+{
+ "phabricator.uri" : "https://phabricator.services.mozilla.com/",
+ "repository.callsign": "MOZILLACENTRAL"
+}
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -1,6 +1,18 @@
+# Note: if you add more configure substitutions here with required values
+# you will also need to fix the sed commands in:
+# taskcluster/scripts/builder/build-sm-mozjs-crate.sh
+# taskcluster/scripts/builder/build-sm-rust-bindings.sh
+
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'vendored-sources'
+[source."https://github.com/gankro/serde"]
+git = "https://github.com/gankro/serde"
+branch = "deserialize_from_enums4"
+replace-with = "vendored-sources"
+
[source.vendored-sources]
directory = '@top_srcdir@/third_party/rust'
+
+@WIN64_CARGO_LINKER_CONFIG@
--- a/.clang-format
+++ b/.clang-format
@@ -1,17 +1,10 @@
BasedOnStyle: Mozilla
-# Ignore all comments because they aren't reflowed properly.
-CommentPragmas: "^"
-
-# Force pointers to the type for C++.
-DerivePointerAlignment: false
-PointerAlignment: Left
-
# Prevent the loss of indentation with these macros
MacroBlockBegin: "^\
NS_INTERFACE_MAP_BEGIN|\
NS_INTERFACE_TABLE_HEAD|\
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION|\
NS_IMPL_CYCLE_COLLECTION_.*_BEGIN|\
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED|\
NS_INTERFACE_TABLE_BEGIN|\
@@ -23,25 +16,16 @@ NS_IMPL_CYCLE_COLLECTION_.*_END|\
NS_INTERFACE_TABLE_END|\
NS_INTERFACE_MAP_END_INHERITING|\
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END_INHERITED|\
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED$"
SortIncludes: false
-# All of the following items are default
-# in the Mozila coding style from clang format 4.0
-AlwaysBreakAfterReturnType: TopLevel
-BinPackArguments: false
-BinPackParameters: false
-SpaceAfterTemplateKeyword: false
-ReflowComments: false
-
-
BreakBeforeBraces: Custom
BraceWrapping:
AfterEnum: true
AfterStruct: true
AfterFunction: true
AfterClass: true
SplitEmptyFunction: true
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -1,137 +1,137 @@
# Uses the LLVM coding style
-^build/clang-plugin/.*
+build/clang-plugin/.*
# The two templates cannot be formatted
-^config/gcc-stl-wrapper.template.h
-^config/msvc-stl-wrapper.template.h
-^dom/base/test/.*
-^dom/bindings/test/.*
-^dom/media/gtest/.*
-^gfx/testsd/.*
-^image/test/.*
-^ipc/ipdl/test/.*
-^ipc/testshell/.*
-^js/src/jsapi-tests/.*
+config/gcc-stl-wrapper.template.h
+config/msvc-stl-wrapper.template.h
+dom/base/test/.*
+dom/bindings/test/.*
+dom/media/gtest/.*
+gfx/testsd/.*
+.*/gtest/ExampleStylesheet.h
+image/test/.*
+ipc/ipdl/test/.*
+ipc/testshell/.*
+js/src/jsapi-tests/.*
# See bug 1395584
-^js/src/vm/Opcodes.h
-# Ignored because of bug 1342657
-^layout/style/nsCSSPropAliasList.h
+js/src/vm/Opcodes.h
# Ignored because of bug 1342657
-^layout/style/nsCSSPropList.h
-^media/mtransport/test/.*
-^mfbt/tests/.*
-^storage/test/.*
-^testing/gtest/.*
-^tools/profiler/tests/.*
-^uriloader/exthandler/tests/.*
+layout/style/nsCSSPropAliasList.h
+# Ignored because of bug 1342657
+layout/style/nsCSSPropList.h
+media/mtransport/test/.*
+mfbt/tests/.*
+storage/test/.*
+testing/gtest/.*
+tools/profiler/tests/.*
+uriloader/exthandler/tests/.*
# JNI code is generated
-^widget/android/GeneratedJNINatives.h
-^widget/android/GeneratedJNIWrappers.cpp
-^widget/android/GeneratedJNIWrappers.h
-^widget/android/fennec/FennecJNINatives.h
-^widget/android/fennec/FennecJNIWrappers.cpp
-^widget/android/fennec/FennecJNIWrappers.h
-^widget/tests/.*
-^xpcom/glue/tests/.*
-^xpcom/tests/.*
+widget/android/GeneratedJNINatives.h
+widget/android/GeneratedJNIWrappers.cpp
+widget/android/GeneratedJNIWrappers.h
+widget/android/fennec/FennecJNINatives.h
+widget/android/fennec/FennecJNIWrappers.cpp
+widget/android/fennec/FennecJNIWrappers.h
+widget/tests/.*
+xpcom/glue/tests/.*
+xpcom/tests/.*
# Generated from ./tools/rewriting/ThirdPartyPaths.txt
-# awk '{print "^"$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
-^browser/components/translation/cld2/.*
-^browser/extensions/mortar/ppapi/.*
-^db/sqlite3/src/.*
-^dom/media/platforms/ffmpeg/libav.*
-^extensions/spellcheck/hunspell/src/.*
-^gfx/angle/.*
-^gfx/cairo/.*
-^gfx/graphite2/.*
-^gfx/harfbuzz/.*
-^gfx/ots/.*
-^gfx/qcms/.*
-^gfx/sfntly/.*
-^gfx/skia/.*
-^gfx/vr/openvr/.*
-^gfx/webrender.*
-^gfx/webrender_api.*
-^gfx/ycbcr/.*
-^intl/hyphenation/hyphen/.*
-^intl/icu/.*
-^ipc/chromium/.*
-^js/src/ctypes/libffi/.*
-^js/src/dtoa.c.*
-^js/src/jit/arm64/vixl/.*
-^media/ffvpx/.*
-^media/gmp-clearkey/0.1/openaes/.*
-^media/kiss_fft/.*
-^media/libav/.*
-^media/libcubeb/.*
-^media/libjpeg/.*
-^media/libmkv/.*
-^media/libnestegg/.*
-^media/libogg/.*
-^media/libopus/.*
-^media/libpng/.*
-^media/libsoundtouch/.*
-^media/libspeex_resampler/.*
-^media/libstagefright/.*
-^media/libtheora/.*
-^media/libtremor/.*
-^media/libvorbis/.*
-^media/libvpx/.*
-^media/libyuv/.*
-^media/mtransport/third_party/.*
-^media/openmax_dl/.*
-^media/sphinxbase/.*
-^media/webrtc/signaling/src/sdp/sipcc/.*
-^media/webrtc/trunk/.*
-^mfbt/decimal/.*
-^mfbt/double-conversion/source/.*
-^mfbt/lz4.*
-^mobile/android/geckoview/src/thirdparty/.*
-^mobile/android/thirdparty/.*
-^modules/brotli/.*
-^modules/fdlibm/.*
-^modules/freetype2/.*
-^modules/libbz2/.*
-^modules/libmar/.*
-^modules/pdfium/.*
-^modules/woff2/.*
-^modules/zlib/.*
-^netwerk/sctp/src/.*
-^netwerk/srtp/src/.*
-^nsprpub/.*
-^other-licenses/.*
-^parser/expat/.*
-^security/nss/.*
-^security/sandbox/chromium/.*
-^testing/gtest/gmock/.*
-^testing/gtest/gtest/.*
-^testing/talos/talos/tests/canvasmark/.*
-^testing/talos/talos/tests/dromaeo/.*
-^testing/talos/talos/tests/kraken/.*
-^testing/talos/talos/tests/v8_7/.*
-^third_party/aom/.*
-^third_party/python/blessings/.*
-^third_party/python/configobj/.*
-^third_party/python/futures/.*
-^third_party/python/jsmin/.*
-^third_party/python/mock-*/.*
-^third_party/python/psutil/.*
-^third_party/python/py/.*
-^third_party/python/pyasn1/.*
-^third_party/python/pyasn1-modules/.*
-^third_party/python/PyECC/.*
-^third_party/python/pytest/.*
-^third_party/python/pytoml/.*
-^third_party/python/pyyaml/.*
-^third_party/python/redo/.*
-^third_party/python/requests/.*
-^third_party/python/rsa/.*
-^third_party/python/six/.*
-^third_party/python/which/.*
-^third_party/rust/.*
-^toolkit/components/jsoncpp/.*
-^toolkit/components/protobuf/.*
-^toolkit/components/url-classifier/chromium/.*
-^toolkit/components/url-classifier/protobuf/.*
-^toolkit/crashreporter/google-breakpad/.*
-^tools/fuzzing/libfuzzer.*
+# awk '{print ""$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
+browser/components/translation/cld2/.*
+browser/extensions/mortar/ppapi/.*
+db/sqlite3/src/.*
+extensions/spellcheck/hunspell/src/.*
+gfx/angle/.*
+gfx/cairo/.*
+gfx/graphite2/.*
+gfx/harfbuzz/.*
+gfx/ots/.*
+gfx/qcms/.*
+gfx/sfntly/.*
+gfx/skia/.*
+gfx/vr/openvr/.*
+gfx/webrender.*
+gfx/webrender_api.*
+gfx/ycbcr/.*
+intl/hyphenation/hyphen/.*
+intl/icu/.*
+ipc/chromium/.*
+js/src/ctypes/libffi/.*
+js/src/dtoa.c.*
+js/src/jit/arm64/vixl/.*
+media/ffvpx/.*
+media/gmp-clearkey/0.1/openaes/.*
+media/kiss_fft/.*
+media/libav/.*
+media/libcubeb/.*
+media/libjpeg/.*
+media/libmkv/.*
+media/libnestegg/.*
+media/libogg/.*
+media/libopus/.*
+media/libpng/.*
+media/libsoundtouch/.*
+media/libspeex_resampler/.*
+media/libstagefright/.*
+media/libtheora/.*
+media/libtremor/.*
+media/libvorbis/.*
+media/libvpx/.*
+media/libyuv/.*
+media/mtransport/third_party/.*
+media/openmax_dl/.*
+media/sphinxbase/.*
+media/webrtc/signaling/src/sdp/sipcc/.*
+media/webrtc/trunk/.*
+mfbt/decimal/.*
+mfbt/double-conversion/double-conversion/.*
+mobile/android/geckoview/src/thirdparty/.*
+mobile/android/thirdparty/.*
+modules/brotli/.*
+modules/fdlibm/.*
+modules/freetype2/.*
+modules/libbz2/.*
+modules/libmar/.*
+modules/pdfium/.*
+modules/woff2/.*
+modules/zlib/.*
+netwerk/sctp/src/.*
+netwerk/srtp/src/.*
+nsprpub/.*
+other-licenses/.*
+parser/expat/.*
+security/nss/.*
+security/sandbox/chromium/.*
+testing/gtest/gmock/.*
+testing/gtest/gtest/.*
+testing/talos/talos/tests/canvasmark/.*
+testing/talos/talos/tests/dromaeo/.*
+testing/talos/talos/tests/kraken/.*
+testing/talos/talos/tests/v8_7/.*
+third_party/aom/.*
+third_party/python/blessings/.*
+third_party/python/configobj/.*
+third_party/python/futures/.*
+third_party/python/jsmin/.*
+third_party/python/mock-*/.*
+third_party/python/psutil/.*
+third_party/python/py/.*
+third_party/python/pyasn1/.*
+third_party/python/pyasn1-modules/.*
+third_party/python/PyECC/.*
+third_party/python/pytest/.*
+third_party/python/pytoml/.*
+third_party/python/pyyaml/.*
+third_party/python/redo/.*
+third_party/python/requests/.*
+third_party/python/rsa/.*
+third_party/python/six/.*
+third_party/python/which/.*
+third_party/rust/.*
+toolkit/components/jsoncpp/.*
+toolkit/components/lz4/.*
+toolkit/components/protobuf/.*
+toolkit/components/url-classifier/chromium/.*
+toolkit/components/url-classifier/protobuf/.*
+toolkit/crashreporter/google-breakpad/.*
+tools/fuzzing/libfuzzer.*
deleted file mode 100644
--- a/.clang-tidy
+++ /dev/null
@@ -1,16 +0,0 @@
-# Checks run by clang-tidy over Mozilla code.
-
-# The following checks are currently enabled:
-# * modernize-raw-string-literal -
-# Replace string literals containing escaped characters with raw string literals
-# * modernize-use-bool-literals
-# Replace integer literals which are cast to bool
-# * modernize-loop-convert
-# Converts for(...; ...; ...) loops to use the new range-based loops in C++11
-# * modernize-use-default
-# Replace default bodies of special member functions with = default;
-# * modernize-use-override
-# Use C++11's override and remove virtual where applicable
-
-Checks: '-*, modernize-raw-string-literal, modernize-use-bool-literals, modernize-loop-convert, modernize-use-default, modernize-use-override'
-
--- a/.cron.yml
+++ b/.cron.yml
@@ -93,22 +93,32 @@ jobs:
target-tasks-method: nightly_dmd
run-on-projects:
- mozilla-central
when:
by-project:
mozilla-central: [{hour: 10, minute: 0}]
# No default
+ - name: searchfox-index
+ job:
+ type: decision-task
+ treeherder-symbol: Searchfox
+ target-tasks-method: searchfox_index
+ run-on-projects:
+ - mozilla-central
+ when:
+ by-project:
+ mozilla-central: [{hour: 10, minute: 30}]
+ # No default
+
- name: periodic-update
job:
type: decision-task
treeherder-symbol: Nfile
target-tasks-method: file_update
run-on-projects:
- mozilla-central
when:
by-project:
# No default branch
mozilla-central:
- # Buildbot start time is 10:02am UTC, until we are able to
- # disable buildbot scheduling, use +12h
- - {hour: 22, minute: 0}
+ - {hour: 10, minute: 0}
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,81 +1,105 @@
# Always ignore node_modules.
**/node_modules/**/*.*
+# Always ignore crashtests - specially crafted files that originally caused a
+# crash.
+**/crashtests/**
+# Also ignore reftest - specially crafted to produce expected output.
+**/reftest/**
+
# 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.
-chrome/**
docshell/**
editor/**
-embedding/**
extensions/cookie/**
extensions/spellcheck/**
extensions/universalchardet/**
-gfx/**
+gfx/layers/**
+gfx/tests/browser/**
+gfx/tests/chrome/**
+gfx/tests/mochitest/**
+gfx/tests/unit/**
image/**
intl/**
layout/**
-media/**
-memory/**
+memory/replace/dmd/test/**
modules/**
-netwerk/**
+netwerk/base/NetUtil.jsm
+netwerk/cookie/test/browser/**
+netwerk/cookie/test/unit/**
+netwerk/protocol/**
+netwerk/dns/**
+netwerk/test/browser/**
+netwerk/test/httpserver/**
+netwerk/test/mochitests/**
+netwerk/test/unit*/**
+netwerk/wifi/**
parser/**
-python/**
rdf/**
-servo/**
tools/update-packaging/**
-uriloader/**
-view/**
-widget/**
-xpcom/**
+uriloader/exthandler/**
+uriloader/exthandler/tests/mochitest/**
+widget/headless/tests/**
+widget/tests/**
# We currently have no js files in these directories, so we ignore them by
# default to aid ESLint's performance.
build/**
config/**
db/**
+embedding/**
gradle/**
hal/**
mfbt/**
mozglue/**
nsprpub/**
other-licenses/**
probes/**
startupcache/**
xpfe/**
+# These directories only contain crashtests, but we still skip the whole
+# directory to aid performance.
+view/**
+
# browser/ exclusions
browser/app/**
browser/branding/**/firefox-branding.js
# Gzipped test file.
browser/base/content/test/general/gZipOfflineChild.html
browser/base/content/test/urlbar/file_blank_but_not_blank.html
# New tab is likely to be replaced soon.
browser/base/content/newtab/**
# Test files that are really json not js, and don't need to be linted.
browser/components/sessionstore/test/unit/data/sessionstore_valid.js
browser/components/sessionstore/test/unit/data/sessionstore_invalid.js
+# This file is split into two in order to keep it as a valid json file
+# for documentation purposes (policies.json) but to be accessed by the
+# code as a .jsm (schema.jsm)
+browser/components/enterprisepolicies/schemas/schema.jsm
# generated & special files in cld2
browser/components/translation/cld2/**
# Screenshots and Follow-on search are imported as a system add-on and have
# their own lint rules currently.
browser/extensions/followonsearch/**
browser/extensions/screenshots/**
browser/extensions/pdfjs/content/build**
browser/extensions/pdfjs/content/web**
# generated or library files in pocket
browser/extensions/pocket/content/panels/js/tmpl.js
browser/extensions/pocket/content/panels/js/vendor/**
# generated or library files in activity-stream
browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+browser/extensions/activity-stream/test/**
browser/extensions/activity-stream/vendor/**
# The only file in browser/locales/ is pre-processed.
browser/locales/**
# imported from chromium
browser/extensions/mortar/**
# Generated data files
browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm
@@ -173,37 +197,40 @@ devtools/client/inspector/markup/test/ev
devtools/client/netmonitor/test/xhr_bundle.js
devtools/client/webconsole/new-console-output/test/mochitest/code_bundle_nosource.js
devtools/client/webconsole/new-console-output/test/mochitest/code_bundle_invalidmap.js
devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
devtools/server/tests/unit/setBreakpoint*
devtools/server/tests/unit/sourcemapped.js
# dom/ exclusions
+dom/abort/**
dom/animation/**
dom/archivereader/**
dom/asmjscache/**
dom/audiochannel/**
dom/base/**
dom/battery/**
dom/bindings/**
dom/broadcastchannel/**
dom/browser-element/**
-dom/cache/**
+dom/cache/test/mochitest/**
+dom/cache/test/xpcshell/**
dom/canvas/**
dom/commandhandler/**
dom/console/**
dom/crypto/**
dom/devicestorage/**
dom/encoding/**
dom/events/**
dom/fetch/**
dom/file/**
dom/filehandle/**
dom/filesystem/**
+dom/flex/**
dom/flyweb/**
dom/gamepad/**
dom/geolocation/**
dom/grid/**
dom/html/**
dom/imptests/**
dom/interfaces/**
dom/ipc/**
@@ -211,95 +238,127 @@ dom/json/**
dom/jsurl/**
dom/locales/**
dom/manifest/**
dom/mathml/**
dom/media/**
!dom/media/*.js*
dom/messagechannel/**
dom/network/**
-dom/notification/**
+dom/notification/Notification*.*
+dom/notification/test/browser/**
+dom/notification/test/unit/**
+dom/notification/test/mochitest/**
dom/offline/**
dom/payments/**
dom/performance/**
dom/permission/**
-dom/plugins/**
+dom/plugins/test/mochitest/**
+dom/plugins/test/unit/**
dom/power/**
-dom/presentation/**
+dom/presentation/Presentation*.js
+dom/presentation/provider/**
+dom/presentation/tests/mochitest/**
+dom/presentation/tests/xpcshell/**
dom/promise/**
dom/push/**
dom/quota/**
dom/res/**
dom/secureelement/**
-dom/security/**
+dom/security/test/contentverifier/**
+dom/security/test/cors/**
+dom/security/test/csp/**
+dom/security/test/general/**
+dom/security/test/hsts/**
+dom/security/test/mixedcontentblocker/**
+dom/security/test/sri/**
+dom/security/test/unit/**
dom/smil/**
dom/storage/**
dom/svg/**
dom/system/**
-dom/tests/**
+dom/tests/browser/**
+dom/tests/html/**
+dom/tests/js/**
+dom/tests/mochitest/**
+dom/tests/unit/**
dom/time/**
dom/u2f/**
dom/url/**
dom/vr/**
dom/webauthn/**
dom/webbrowserpersist/**
dom/webidl/**
+dom/websocket/**
dom/workers/**
dom/worklet/**
dom/xbl/**
dom/xhr/**
dom/xml/**
dom/xslt/**
dom/xul/**
# Third-party
dom/media/webvtt/**
+# Third-party
+gfx/ots/**
+gfx/skia/**
+
# Exclude everything but self-hosted JS
js/ductwork/**
js/examples/**
js/ipc/**
js/public/**
js/xpconnect/**
js/src/devtools/**
js/src/octane/**
js/src/jit-test/**
+js/src/jsapi-tests/binast/**
js/src/tests/**
js/src/Y.js
+# Third-party
+media/webrtc/trunk/**
+
# mobile/android/ exclusions
mobile/android/tests/browser/chrome/tp5/**
# Uses `#filter substitution`
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
+# Only contains non-standard test files.
+python/**
+
# security/ exclusions (pref files).
security/manager/ssl/security-prefs.js
# NSS / taskcluster only.
security/nss/**
# services/ exclusions
# Uses `#filter substitution`
services/sync/modules/constants.js
services/sync/services-sync.js
+# Servo is imported.
+servo/**
+
# Remote protocol exclusions
testing/marionette/test_*.js
testing/marionette/atom.js
testing/marionette/legacyaction.js
testing/marionette/client
testing/marionette/doc
testing/marionette/harness
@@ -320,17 +379,16 @@ testing/talos/talos/tests/kraken/**
testing/web-platform/**
testing/xpcshell/moz-http2/**
testing/xpcshell/node-http2/**
# Third party services
services/common/kinto-http-client.js
services/common/kinto-offline-client.js
-services/sync/tps/extensions/mozmill
# toolkit/ exclusions
# Not part of the default build
toolkit/components/help/**
# Intentionally invalid JS
toolkit/components/workerloader/tests/moduleF-syntax-error.js
@@ -338,16 +396,17 @@ toolkit/components/workerloader/tests/mo
# Tests old non-star function generators
toolkit/modules/tests/xpcshell/test_task.js
# External code:
toolkit/components/microformats/test/**
toolkit/components/microformats/microformat-shiv.js
toolkit/components/reader/Readability.js
toolkit/components/reader/JSDOMParser.js
+toolkit/components/payments/res/vendor/*
# Uses preprocessing
toolkit/content/widgets/wizard.xml
toolkit/components/osfile/osfile.jsm
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/modules/AppConstants.jsm
toolkit/mozapps/downloads/nsHelperAppDlg.js
toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -11,9 +11,33 @@ module.exports = {
"mozilla"
],
// The html plugin is enabled via a command line option on eslint. To avoid
// bad interactions with the xml preprocessor in eslint-plugin-mozilla, we
// turn off processing of the html plugin for .xml files.
"settings": {
"html/xml-extensions": [ ".xhtml" ]
},
+
+ "overrides": [{
+ // eslint-plugin-html handles eol-last slightly different - it applies to
+ // each set of script tags, so we turn it off here.
+ "files": "**/*.*html",
+ "rules": {
+ "eol-last": "off",
+ }
+ }, {
+ // XXX Bug 1421969. These files/directories are still being fixed,
+ // so turn off mozilla/use-services for them for now.
+ "files": [
+ // Browser: Bug 1421379
+ "browser/extensions/shield-recipe-client/test/browser/head.js",
+ "browser/modules/offlineAppCache.jsm",
+ "devtools/**",
+ "extensions/pref/**",
+ "mobile/android/**",
+ "testing/**",
+ ],
+ "rules": {
+ "mozilla/use-services": "off",
+ }
+ }]
};
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,19 @@
# .gitignore - List of filenames git should ignore
# Filenames that should be ignored wherever they appear
*~
*.pyc
*.pyo
TAGS
tags
+# Ignore ID generated by idutils and un-ignore id directory (for Indonesian locale)
ID
-!/browser/extensions/screenshots/webextension/_locales/id/
+!id/
.DS_Store*
*.pdb
*.egg-info
# Vim swap files.
.*.sw[a-z]
# Emacs directory variable files.
@@ -98,16 +99,20 @@ testing/web-platform/products/
# Android Gradle artifacts.
mobile/android/gradle/.gradle
# XCode project cruft
/*.xcodeproj/
embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
+# Rust port of mozbase are libraries
+testing/mozbase/rust/*/target
+testing/mozbase/rust/*/Cargo.lock
+
# Ignore mozharness execution files
testing/mozharness/.tox/
testing/mozharness/build/
testing/mozharness/logs/
testing/mozharness/.coverage
testing/mozharness/nosetests.xml
# Ignore ESLint node_modules
--- a/.hgignore
+++ b/.hgignore
@@ -106,16 +106,20 @@ GPATH
# Android Gradle artifacts.
^mobile/android/gradle/.gradle
# XCode project cruft
^[^/]*\.xcodeproj/
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
+# Rust port of mozbase are Rust libraries
+^testing/mozbase/rust/.*/target
+^testing/mozbase/rust/.*/Cargo.lock
+
# Ignore mozharness execution files
^testing/mozharness/.tox/
^testing/mozharness/build/
^testing/mozharness/logs/
^testing/mozharness/.coverage
^testing/mozharness/nosetests.xml
# Ignore tox generated dir
@@ -140,16 +144,24 @@ GPATH
^testing/talos/talos/tests/tp5n
^testing/talos/talos/tests/devtools/damp.manifest.develop
^talos-venv
^py3venv
^testing/talos/talos/mitmproxy/mitmdump
^testing/talos/talos/mitmproxy/mitmproxy
^testing/talos/talos/mitmproxy/mitmweb
+# Ignore talos webkit benchmark files; source is copied from in-tree /third_party
+# into testing/talos/talos/tests/webkit/PerformanceTests/ when run locally
+# i.e. speedometer, motionmark, stylebench
+^testing/talos/talos/tests/webkit/PerformanceTests
+
+# Ignore toolchains.json created by tooltool.
+^toolchains\.json
+
# Ignore files created when running a reftest.
^lextab.py$
# tup database
^\.tup
# Ignore sync tps logs and reports
tps\.log
@@ -157,8 +169,11 @@ tps_result\.json
# Ignore Visual Studio Code workspace files.
\.vscode/(?!extensions\.json|tasks\.json)
subinclude:servo/.hgignore
# Ignore Infer output
^infer-out/
+
+# https://bz.mercurial-scm.org/show_bug.cgi?id=5322
+^comm/
--- a/.hgtags
+++ b/.hgtags
@@ -131,8 +131,11 @@ 465d150bc8be5bbf9f02a8607d4552b6a5e1697c
fc69febcbf6c0dcc4b3dfc7a346d8d348798a65f FIREFOX_AURORA_51_BASE
1196bf3032e1bce1fb07a01fd9082a767426c5fb FIREFOX_AURORA_52_BASE
f80dc9fc34680105b714a49b4704bb843f5f7004 FIREFOX_AURORA_53_BASE
6583496f169cd8a13c531ed16e98e8bf313eda8e FIREFOX_AURORA_54_BASE
f9605772a0c9098ed1bcaa98089b2c944ed69e9b FIREFOX_BETA_55_BASE
320642944e42a889db13c6c55b404e32319d4de6 FIREFOX_BETA_56_BASE
8e818b5e9b6bef0fc1a5c527ecf30b0d56a02f14 FIREFOX_BETA_57_BASE
f7e9777221a34f9f23c2e4933307eb38b621b679 FIREFOX_NIGHTLY_57_END
+40a14ca1cf04499f398e4cb8ba359b39eae4e216 FIREFOX_BETA_58_BASE
+1f91961bb79ad06fd4caef9e5dfd546afd5bf42c FIREFOX_NIGHTLY_58_END
+5faab9e619901b1513fd4ca137747231be550def FIREFOX_NIGHTLY_59_END
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -1,139 +1,191 @@
# This file is rendered via JSON-e by
# - mozilla-taskcluster - https://docs.taskcluster.net/reference/integrations/mozilla-taskcluster/docs/taskcluster-yml
# - cron tasks - taskcluster/taskgraph/cron/decision.py
+# - action tasks - taskcluster/taskgraph/actions/registry.py
version: 1
tasks:
- $let:
- # sometimes the push user is just `ffxbld` or the like, but we want an email-like field..
- ownerEmail: {$if: '"@" in push.owner', then: '${push.owner}', else: '${push.owner}@noreply.mozilla.org'}
- # ensure there's no trailing `/` on the repo URL
- repoUrl: {$if: 'repository.url[-1] == "/"', then: {$eval: 'repository.url[:-1]'}, else: {$eval: 'repository.url'}}
- in:
- - taskId: '${as_slugid("decision")}'
- taskGroupId: '${as_slugid("decision")}' # same as tsakId; this is how automation identifies a decision tsak
- schedulerId: 'gecko-level-${repository.level}'
-
- created: {$fromNow: ''}
- deadline: {$fromNow: '1 day'}
- expires: {$fromNow: '1 year 1 second'} # 1 second so artifacts expire first, despite rounding errors
- metadata:
- $merge:
- - owner: "${ownerEmail}"
- source: "${repoUrl}/raw-file/${push.revision}/.taskcluster.yml"
- - $if: 'tasks_for == "hg-push"'
- then:
- name: "Gecko Decision Task"
- description: 'The task that creates all of the other tasks in the task graph'
- else:
- name: "Decision Task for cron job ${cron.job_name}"
- description: 'Created by a [cron task](https://tools.taskcluster.net/tasks/${cron.task_id})'
-
- provisionerId: "aws-provisioner-v1"
- workerType: "gecko-${repository.level}-decision"
-
- tags:
- $if: 'tasks_for == "hg-push"'
- then: {createdForUser: "${ownerEmail}"}
-
- routes:
- $if: 'tasks_for == "hg-push"'
- then:
- - "index.gecko.v2.${repository.project}.latest.firefox.decision"
- - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
- - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
- - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
- - "notify.email.${ownerEmail}.on-failed"
- - "notify.email.${ownerEmail}.on-exception"
- else:
- - "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
- - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
- - "tc-treeherder-stage.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-
- scopes:
- $if: 'tasks_for == "hg-push"'
- then:
- - 'assume:repo:${repoUrl[8:]}:*'
- - 'queue:route:notify.email.${ownerEmail}.*'
- else:
- - 'assume:repo:${repoUrl[8:]}:cron:${cron.job_name}'
-
- dependencies: []
- requires: all-completed
-
- priority: lowest
- retries: 5
+ - $let:
+ # sometimes the push user is just `ffxbld` or the like, but we want an email-like field..
+ ownerEmail: {$if: '"@" in push.owner', then: '${push.owner}', else: '${push.owner}@noreply.mozilla.org'}
+ # ensure there's no trailing `/` on the repo URL
+ repoUrl: {$if: 'repository.url[-1] == "/"', then: {$eval: 'repository.url[:-1]'}, else: {$eval: 'repository.url'}}
+ in:
+ taskId: {$if: 'tasks_for != "action"', then: '${as_slugid("decision")}'}
+ taskGroupId:
+ $if: 'tasks_for == "action"'
+ then:
+ '${action.taskGroupId}'
+ else:
+ '${as_slugid("decision")}' # same as taskId; this is how automation identifies a decision tsak
+ schedulerId: 'gecko-level-${repository.level}'
- 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: '${repoUrl}'
- GECKO_HEAD_REF: '${push.revision}'
- GECKO_HEAD_REV: '${push.revision}'
- GECKO_COMMIT_MSG: '${push.comment}'
- HG_STORE_PATH: /builds/worker/checkouts/hg-store
- TASKCLUSTER_CACHES: /builds/worker/checkouts
-
- cache:
- level-${repository.level}-checkouts-sparse-v1: /builds/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 hash
- # XXX Changing this will break Chain of Trust without an associated puppet and
- # scriptworker patch!
- image: 'taskcluster/decision:2.0.0@sha256:4039fd878e5700b326d4a636e28c595c053fbcb53909c1db84ad1f513cf644ef'
-
- 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:
- - /builds/worker/bin/run-task
- - '--vcs-checkout=/builds/worker/checkouts/gecko'
- - '--sparse-profile=build/sparse-profiles/taskgraph'
- - '--'
- - bash
- - -cx
- - $let:
- extraArgs: {$if: 'tasks_for == "hg-push"', then: '', else: '${cron.quoted_args}'}
- # NOTE: the explicit reference to mozilla-central below is required because android-stuff
- # still uses tc-vcs, which does not support mozilla-unified
- # https://bugzilla.mozilla.org/show_bug.cgi?id=1383973
- in: >
- cd /builds/worker/checkouts/gecko &&
- ln -s /builds/worker/artifacts artifacts &&
- ./mach --log-no-times taskgraph decision
- --pushlog-id='${push.pushlog_id}'
- --pushdate='${push.pushdate}'
- --project='${repository.project}'
- --message="$GECKO_COMMIT_MSG"
- --owner='${ownerEmail}'
- --level='${repository.level}'
- --base-repository='https://hg.mozilla.org/mozilla-central'
- --head-repository="$GECKO_HEAD_REPOSITORY"
- --head-ref="$GECKO_HEAD_REF"
- --head-rev="$GECKO_HEAD_REV"
- ${extraArgs}
-
- artifacts:
- 'public':
- type: 'directory'
- path: '/builds/worker/artifacts'
- expires: {$fromNow: '1 year'}
-
- extra:
- treeherder:
+ created: {$fromNow: ''}
+ deadline: {$fromNow: '1 day'}
+ expires: {$fromNow: '1 year 1 second'} # 1 second so artifacts expire first, despite rounding errors
+ metadata:
$merge:
- - machine:
- platform: gecko-decision
+ - owner: "${ownerEmail}"
+ source: "${repoUrl}/raw-file/${push.revision}/.taskcluster.yml"
- $if: 'tasks_for == "hg-push"'
then:
- symbol: D
+ name: "Gecko Decision Task"
+ description: 'The task that creates all of the other tasks in the task graph'
else:
- groupSymbol: cron
- symbol: "${cron.job_symbol}"
+ $if: 'tasks_for == "action"'
+ then:
+ name: "Action: ${action.title}"
+ description: '${action.description}'
+ else:
+ name: "Decision Task for cron job ${cron.job_name}"
+ description: 'Created by a [cron task](https://tools.taskcluster.net/tasks/${cron.task_id})'
+
+ provisionerId: "aws-provisioner-v1"
+ workerType: "gecko-${repository.level}-decision"
+
+ tags:
+ $if: 'tasks_for == "hg-push"'
+ then: {createdForUser: "${ownerEmail}"}
+ else:
+ $if: 'tasks_for == "action"'
+ then:
+ createdForUser: '${ownerEmail}'
+ kind: 'action-callback'
+
+ routes:
+ $if: 'tasks_for == "hg-push"'
+ then:
+ - "index.gecko.v2.${repository.project}.latest.firefox.decision"
+ - "index.gecko.v2.${repository.project}.revision.${push.revision}.firefox.decision"
+ - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
+ - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+ - "notify.email.${ownerEmail}.on-failed"
+ - "notify.email.${ownerEmail}.on-exception"
+ else:
+ - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+ - $if: 'tasks_for == "action"'
+ then: "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.${ownTaskId}"
+ else: "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
+
+ scopes:
+ $if: 'tasks_for == "hg-push"'
+ then:
+ - 'assume:repo:${repoUrl[8:]}:*'
+ - 'queue:route:notify.email.${ownerEmail}.*'
+ else:
+ $if: 'tasks_for == "action"'
+ then:
+ - '${action.repo_scope}'
+ else:
+ - 'assume:repo:${repoUrl[8:]}:cron:${cron.job_name}'
+
+ dependencies: []
+ requires: all-completed
+
+ priority: lowest
+ retries: 5
+
+ payload:
+ env:
+ # checkout-gecko uses these to check out the source; the inputs
+ # to `mach taskgraph decision` are all on the command line.
+ $merge:
+ - GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
+ GECKO_HEAD_REPOSITORY: '${repoUrl}'
+ GECKO_HEAD_REF: '${push.revision}'
+ GECKO_HEAD_REV: '${push.revision}'
+ GECKO_COMMIT_MSG: {$if: 'tasks_for != "action"', then: '${push.comment}'}
+ HG_STORE_PATH: /builds/worker/checkouts/hg-store
+ TASKCLUSTER_CACHES: /builds/worker/checkouts
+ - $if: 'tasks_for == "action"'
+ then:
+ ACTION_TASK_GROUP_ID: '${ownTaskId}'
+ ACTION_TASK_ID: {$json: {$eval: 'taskId'}}
+ ACTION_TASK: {$json: {$eval: 'task'}}
+ ACTION_INPUT: {$json: {$eval: 'input'}}
+ ACTION_CALLBACK: '${action.cb_name}'
+ ACTION_PARAMETERS: {$json: {$eval: 'parameters'}}
+
+ cache:
+ level-${repository.level}-checkouts-sparse-v1: /builds/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 hash
+ # XXX Changing this will break Chain of Trust without an associated puppet and
+ # scriptworker patch!
+ image: 'taskcluster/decision:2.0.0@sha256:4039fd878e5700b326d4a636e28c595c053fbcb53909c1db84ad1f513cf644ef'
+
+ maxRunTime: 1800
+
+ command:
+ - /builds/worker/bin/run-task
+ - '--vcs-checkout=/builds/worker/checkouts/gecko'
+ - '--sparse-profile=build/sparse-profiles/taskgraph'
+ - '--'
+ - bash
+ - -cx
+ - $let:
+ extraArgs: {$if: 'tasks_for == "cron"', then: '${cron.quoted_args}', else: ''}
+ in:
+ $if: 'tasks_for == "action"'
+ then: >
+ cd /builds/worker/checkouts/gecko &&
+ ln -s /builds/worker/artifacts artifacts &&
+ ./mach --log-no-times taskgraph action-callback
+ else: >
+ cd /builds/worker/checkouts/gecko &&
+ ln -s /builds/worker/artifacts artifacts &&
+ ./mach --log-no-times taskgraph decision
+ --pushlog-id='${push.pushlog_id}'
+ --pushdate='${push.pushdate}'
+ --project='${repository.project}'
+ --message="$GECKO_COMMIT_MSG"
+ --owner='${ownerEmail}'
+ --level='${repository.level}'
+ --base-repository="$GECKO_BASE_REPOSITORY"
+ --head-repository="$GECKO_HEAD_REPOSITORY"
+ --head-ref="$GECKO_HEAD_REF"
+ --head-rev="$GECKO_HEAD_REV"
+ ${extraArgs}
+
+ artifacts:
+ 'public':
+ type: 'directory'
+ path: '/builds/worker/artifacts'
+ expires: {$fromNow: '1 year'}
+
+ extra:
+ $merge:
+ - treeherder:
+ $merge:
+ - machine:
+ platform: gecko-decision
+ - $if: 'tasks_for == "hg-push"'
+ then:
+ symbol: D
+ else:
+ $if: 'tasks_for == "action"'
+ then:
+ groupName: 'action-callback'
+ groupSymbol: AC
+ symbol: "${action.symbol}"
+ else:
+ groupSymbol: cron
+ symbol: "${cron.job_symbol}"
+ - $if: 'tasks_for == "action"'
+ then:
+ parent: '${action.taskGroupId}'
+ action:
+ name: '${action.name}'
+ context:
+ taskGroupId: '${action.taskGroupId}'
+ taskId: {$eval: 'taskId'}
+ input: {$eval: 'input'}
+ parameters: {$eval: 'parameters'}
+ - $if: 'tasks_for == "cron"'
+ then:
+ cron: {$json: {$eval: 'cron'}}
+ - tasks_for: '${tasks_for}'
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,152 +1,186 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
+ "command": "${workspaceRoot}/mach",
"windows": {
"command": "\"\\mozilla-build\\start-shell.bat mach\""
},
- "osx": {
- "command": "${workspaceRoot}/mach"
- },
- "linux": {
- "command": "${workspaceRoot}/mach"
- },
- "isShellCommand": true,
"args": ["--log-no-times"],
- "showOutput": "silent",
"echoCommand": true,
- "suppressTaskName": false,
"tasks": [
{
- "taskName": "clobber"
+ "label": "clobber-python",
+ "type":"shell",
+ "command": "${workspaceRoot}/mach",
+ "windows": {
+ "command": "\"\\mozilla-build\\start-shell.bat mach\""
+ },
+ "args": ["clobber", "python"],
+ "problemMatcher": []
},
{
- "taskName": "clobber-python",
- "suppressTaskName": true,
- "args": ["clobber", "python"]
+ "label": "configure",
+ "type":"shell",
+ "problemMatcher": []
},
{
- "taskName": "configure"
- },
- {
- "taskName": "build",
- "isBuildCommand": true,
+ "label": "build",
+ "type":"shell",
"problemMatcher": {
"owner": "cpp",
"fileLocation": "absolute",
"pattern": {
- "regexp": "^.*?tools([^\\s]*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
+ "regexp": "^.*?([^\\s]*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
}
},
{
- "taskName": "build-binaries",
- "suppressTaskName": true,
- "args": ["build", "binaries"],
+ "label": "build-binaries",
+ "type":"shell",
+ "command": "${workspaceRoot}/mach",
+ "windows": {
+ "command": "\"\\mozilla-build\\start-shell.bat mach\""
+ },
+ "args": ["--log-no-times", "build", "binaries"],
+ "problemMatcher": {
+ "owner": "cpp",
+ "fileLocation": "absolute",
+ "pattern": {
+ "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
+ "file": 1,
+ "line": 2,
+ "column": 3,
+ "severity": 4,
+ "message": 5
+ }
+ },
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ },
+ {
+ "label": "build-faster",
+ "type":"shell",
+ "command": "${workspaceRoot}/mach",
+ "windows": {
+ "command": "\"\\mozilla-build\\start-shell.bat mach\""
+ },
+ "args": ["--log-no-times", "build", "faster"],
"problemMatcher": {
"owner": "cpp",
"fileLocation": "absolute",
"pattern": {
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
}
},
{
- "taskName": "build-faster",
- "suppressTaskName": true,
- "args": ["build", "faster"],
- "problemMatcher": {
- "owner": "cpp",
- "fileLocation": "absolute",
- "pattern": {
- "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
- "file": 1,
- "line": 2,
- "column": 3,
- "severity": 4,
- "message": 5
- }
- }
+ "label": "run",
+ "type":"shell",
+ "args": ["-purgecaches"],
+ "problemMatcher": []
},
{
- "taskName": "run",
- "args": ["-purgecaches"],
- "showOutput": "always"
- },
- {
- "taskName": "lint-wo",
- "suppressTaskName": true,
+ "label": "lint-wo",
+ "type":"shell",
+ "command": "${workspaceRoot}/mach",
+ "windows": {
+ "command": "\"\\mozilla-build\\start-shell.bat mach\""
+ },
"args": ["lint", "-wo"],
"problemMatcher": ["$eslint-stylish"]
},
{
- "taskName": "eslint",
+ "label": "eslint",
+ "type": "shell",
"problemMatcher": ["$eslint-stylish"]
},
{
- "taskName": "eslint-fix",
- "suppressTaskName": true,
+ "label": "eslint-fix",
+ "type":"shell",
+ "command": "${workspaceRoot}/mach",
+ "windows": {
+ "command": "\"\\mozilla-build\\start-shell.bat mach\""
+ },
"args": ["eslint", "--fix", "${file}"],
"problemMatcher": ["$eslint-stylish"]
},
{
- "taskName": "test",
+ "label": "test",
+ "type":"shell",
"args": ["${relativeFile}"],
- "isTestCommand": true,
- "showOutput": "always"
+ "group":"test",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
},
{
- "taskName": "mochitest",
+ "label": "mochitest",
+ "type":"shell",
"args": ["${relativeFile}"],
- "showOutput": "always",
"problemMatcher": {
"fileLocation": ["relative", "${workspaceRoot}"],
"pattern": {
"regexp": "^.*\\s+(TEST-UNEXPECTED-FAIL|TEST-UNEXPECTED-PASS)\\s+\\|\\s+([^\\s]*)\\s+\\|\\s+(.*)$",
"severity": 1,
"file": 2,
"message": 3
}
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
}
},
{
- "taskName": "reftest",
+ "label": "reftest",
+ "type":"shell",
"args": ["${relativeFile}"],
- "showOutput": "always",
"problemMatcher": {
"fileLocation": ["absolute"],
"pattern": {
"regexp": "^.*\\s+(TEST-UNEXPECTED-FAIL|TEST-UNEXPECTED-PASS)\\s+\\|\\s+file:\/\/([^\\s]*)\\s+==\\s+[^\\s]*\\s+\\|\\s+(.*)$",
"severity": 1,
"file": 2,
"message": 3
}
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
}
},
{
- "taskName": "xpcshell-test",
+ "label": "xpcshell-test",
+ "type":"shell",
"args": ["${relativeFile}", "--sequential"],
- "showOutput": "always",
"problemMatcher": {
"fileLocation": ["relative", "${workspaceRoot}"],
"pattern": {
"regexp": "^.*\\s+(FAIL|ERROR)\\s+\\[([^\\s]*)\\s+:\\s+(\\d+)\\]\\s+(.*)$",
"severity": 1,
"file": 2,
"location": 3,
"message": 4
}
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
}
}
]
}
--- a/Makefile.in
+++ b/Makefile.in
@@ -56,18 +56,17 @@ 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.'
+ @echo 'Please run the build through "mach build".'
@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.'
@@ -218,16 +217,41 @@ 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)))
+# PGO build target.
+profiledbuild::
+ $(call BUILDSTATUS,TIERS pgo_profile_generate pgo_package pgo_profile pgo_clobber pgo_profile_use)
+ $(call BUILDSTATUS,TIER_START pgo_profile_generate)
+ $(MAKE) default MOZ_PROFILE_GENERATE=1 MOZ_PGO_INSTRUMENTED=1
+ $(call BUILDSTATUS,TIER_FINISH pgo_profile_generate)
+ $(call BUILDSTATUS,TIER_START pgo_package)
+ $(MAKE) package MOZ_PGO_INSTRUMENTED=1 MOZ_INTERNAL_SIGNING_FORMAT= MOZ_EXTERNAL_SIGNING_FORMAT=
+ rm -f jarlog/en-US.log
+ $(call BUILDSTATUS,TIER_FINISH pgo_package)
+ $(call BUILDSTATUS,TIER_START pgo_profile)
+ MOZ_PGO_INSTRUMENTED=1 JARLOG_FILE=jarlog/en-US.log $(PYTHON) $(topsrcdir)/build/pgo/profileserver.py 10
+ $(call BUILDSTATUS,TIER_FINISH pgo_profile)
+ $(call BUILDSTATUS,TIER_START pgo_clobber)
+ $(MAKE) maybe_clobber_profiledbuild
+ $(call BUILDSTATUS,TIER_FINISH pgo_clobber)
+ $(call BUILDSTATUS,TIER_START pgo_profile_use)
+ $(MAKE) default MOZ_PROFILE_USE=1
+ $(call BUILDSTATUS,TIER_FINISH pgo_profile_use)
+
+# Change default target to PGO build if PGO is enabled.
+ifdef MOZ_PGO
+OVERRIDE_DEFAULT_GOAL := profiledbuild
+endif
+
include $(topsrcdir)/config/rules.mk
ifdef SCCACHE_VERBOSE_STATS
default::
-$(CCACHE) --show-stats --stats-format=json > sccache-stats.json
@echo "===SCCACHE STATS==="
-$(CCACHE) --show-stats
@echo "==================="
@@ -274,20 +298,18 @@ symbolsarchive: prepsymbolsarchive
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: package-generated-sources
package-generated-sources:
$(call py_action,package_generated_sources,'$(DIST)/$(PKG_PATH)$(GENERATED_SOURCE_FILE_PACKAGE)')
--- a/accessible/.eslintrc.js
+++ b/accessible/.eslintrc.js
@@ -4,14 +4,13 @@ module.exports = {
"rules": {
// Warn about cyclomatic complexity in functions.
"complexity": ["error", 42],
// XXX These are rules that are enabled in the recommended configuration, but
// disabled here due to failures when initially implemented. They should be
// removed (and hence enabled) at some stage.
"consistent-return": "off",
- "object-shorthand": "off",
"no-unexpected-multiline": "off",
"no-unsafe-finally": "off",
"no-useless-call": "off",
}
};
--- a/accessible/aom/AccessibleNode.cpp
+++ b/accessible/aom/AccessibleNode.cpp
@@ -25,18 +25,22 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
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());
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ if (!accService) {
+ return;
+ }
+
+ DocAccessible* doc = accService->GetDocAccessible(mDOMNode->OwnerDoc());
if (doc) {
mIntl = doc->GetAccessible(mDOMNode);
}
}
AccessibleNode::~AccessibleNode()
{
}
@@ -52,35 +56,42 @@ AccessibleNode::GetParentObject() const
{
return mDOMNode->GetParentObject();
}
void
AccessibleNode::GetRole(nsAString& aRole)
{
if (mIntl) {
- GetOrCreateAccService()->GetStringRole(mIntl->Role(), aRole);
- return;
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ if (accService) {
+ accService->GetStringRole(mIntl->Role(), aRole);
+ return;
+ }
}
aRole.AssignLiteral("unknown");
}
void
AccessibleNode::GetStates(nsTArray<nsString>& aStates)
{
- if (mIntl) {
- if (!mStates) {
- mStates = GetOrCreateAccService()->GetStringStates(mIntl->State());
- }
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ if (!mIntl || !accService) {
+ aStates.AppendElement(NS_LITERAL_STRING("defunct"));
+ return;
+ }
+
+ if (mStates) {
aStates = mStates->StringArray();
return;
}
- aStates.AppendElement(NS_LITERAL_STRING("defunct"));
+ mStates = accService->GetStringStates(mIntl->State());
+ aStates = mStates->StringArray();
}
void
AccessibleNode::GetAttributes(nsTArray<nsString>& aAttributes)
{
if (!mIntl) {
return;
}
@@ -101,30 +112,31 @@ AccessibleNode::GetAttributes(nsTArray<n
prop->GetKey(attr);
aAttributes.AppendElement(NS_ConvertUTF8toUTF16(attr));
}
}
bool
AccessibleNode::Is(const Sequence<nsString>& aFlavors)
{
- if (!mIntl) {
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ if (!mIntl || !accService) {
for (const auto& flavor : aFlavors) {
if (!flavor.EqualsLiteral("unknown") && !flavor.EqualsLiteral("defunct")) {
return false;
}
}
return true;
}
nsAutoString role;
- GetOrCreateAccService()->GetStringRole(mIntl->Role(), role);
+ accService->GetStringRole(mIntl->Role(), role);
if (!mStates) {
- mStates = GetOrCreateAccService()->GetStringStates(mIntl->State());
+ mStates = accService->GetStringStates(mIntl->State());
}
for (const auto& flavor : aFlavors) {
if (!flavor.Equals(role) && !mStates->Contains(flavor)) {
return false;
}
}
return true;
--- a/accessible/atk/Platform.cpp
+++ b/accessible/atk/Platform.cpp
@@ -14,17 +14,17 @@
#include "prenv.h"
#include "prlink.h"
#ifdef MOZ_ENABLE_DBUS
#include <dbus/dbus.h>
#endif
#include <gtk/gtk.h>
-#if (MOZ_WIDGET_GTK == 3)
+#ifdef MOZ_WIDGET_GTK
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, atkMicroVersion = 0;
@@ -64,24 +64,16 @@ static GnomeAccessibilityModule sAtkBrid
"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();
@@ -97,21 +89,17 @@ LoadGtkModule(GnomeAccessibilityModule&
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)
@@ -171,29 +159,22 @@ a11y::PlatformInit()
if (atkMajorVersion != 0L) {
atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
if (atkMinorVersion != 0L)
atkMicroVersion = 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)
+#ifdef MOZ_WIDGET_GTK
if (atk_bridge_adaptor_init) {
atk_bridge_adaptor_init(nullptr, nullptr);
} else
#endif
{
nsresult rv = LoadGtkModule(sAtkBridge);
if (NS_SUCCEEDED(rv)) {
(*sAtkBridge.init)();
@@ -231,29 +212,16 @@ a11y::PlatformShutdown()
// 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
--- a/accessible/atk/moz.build
+++ b/accessible/atk/moz.build
@@ -49,15 +49,15 @@ 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']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
# Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib /
# gobject headers. See bug 1243331 comment 3.
CXXFLAGS += [
'-Wno-error=shadow',
'-Wno-unused-local-typedefs',
]
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -612,16 +612,47 @@ static const nsRoleMapEntry sWAIRoleMaps
roles::FORM,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
eLandmark,
kNoReqStates
},
+ { // graphics-document
+ &nsGkAtoms::graphicsDocument,
+ roles::DOCUMENT,
+ kUseMapRole,
+ eNoValue,
+ eNoAction,
+ eNoLiveAttr,
+ kGenericAccType,
+ kNoReqStates,
+ eReadonlyUntilEditable
+ },
+ { // graphics-object
+ &nsGkAtoms::graphicsObject,
+ roles::GROUPING,
+ kUseMapRole,
+ eNoValue,
+ eNoAction,
+ eNoLiveAttr,
+ kGenericAccType,
+ kNoReqStates
+ },
+ { // graphics-symbol
+ &nsGkAtoms::graphicsSymbol,
+ roles::GRAPHIC,
+ kUseMapRole,
+ eNoValue,
+ eNoAction,
+ eNoLiveAttr,
+ kGenericAccType,
+ kNoReqStates
+ },
{ // grid
&nsGkAtoms::grid,
roles::TABLE,
kUseMapRole,
eNoValue,
eNoAction,
eNoLiveAttr,
eSelect | eTable,
@@ -1225,17 +1256,17 @@ static const EStateRule sWAIUnivStateMap
/**
* ARIA attribute map for attribute characteristics.
* @note ARIA attributes that don't have any flags are not included here.
*/
struct AttrCharacteristics
{
- nsAtom** attributeName;
+ nsStaticAtom** attributeName;
const uint8_t characteristics;
};
static const AttrCharacteristics gWAIUnivAttrMap[] = {
{&nsGkAtoms::aria_activedescendant, ATTR_BYPASSOBJ },
{&nsGkAtoms::aria_atomic, ATTR_BYPASSOBJ_IF_FALSE | ATTR_VALTOKEN | ATTR_GLOBAL },
{&nsGkAtoms::aria_busy, ATTR_VALTOKEN | ATTR_GLOBAL },
{&nsGkAtoms::aria_checked, ATTR_BYPASSOBJ | ATTR_VALTOKEN }, /* exposes checkable obj attr */
@@ -1370,51 +1401,53 @@ aria::AttrCharacteristicsFor(nsAtom* aAt
return 0;
}
bool
aria::HasDefinedARIAHidden(nsIContent* aContent)
{
return aContent &&
nsAccUtils::HasDefinedARIAToken(aContent, nsGkAtoms::aria_hidden) &&
- !aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_hidden,
- nsGkAtoms::_false, eCaseMatters);
+ !aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::aria_hidden,
+ nsGkAtoms::_false,
+ eCaseMatters);
}
////////////////////////////////////////////////////////////////////////////////
// AttrIterator class
bool
AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue)
{
while (mAttrIdx < mAttrCount) {
- const nsAttrName* attr = mContent->GetAttrNameAt(mAttrIdx);
+ const nsAttrName* attr = mElement->GetAttrNameAt(mAttrIdx);
mAttrIdx++;
if (attr->NamespaceEquals(kNameSpaceID_None)) {
nsAtom* attrAtom = attr->Atom();
nsDependentAtomString attrStr(attrAtom);
if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
continue; // Not ARIA
uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
if (attrFlags & ATTR_BYPASSOBJ)
continue; // No need to handle exposing as obj attribute here
if ((attrFlags & ATTR_VALTOKEN) &&
- !nsAccUtils::HasDefinedARIAToken(mContent, attrAtom))
+ !nsAccUtils::HasDefinedARIAToken(mElement, attrAtom))
continue; // only expose token based attributes if they are defined
if ((attrFlags & ATTR_BYPASSOBJ_IF_FALSE) &&
- mContent->AttrValueIs(kNameSpaceID_None, attrAtom,
+ mElement->AttrValueIs(kNameSpaceID_None, attrAtom,
nsGkAtoms::_false, eCaseMatters)) {
continue; // only expose token based attribute if value is not 'false'.
}
nsAutoString value;
- if (mContent->GetAttr(kNameSpaceID_None, attrAtom, value)) {
+ if (mElement->GetAttr(kNameSpaceID_None, attrAtom, value)) {
aAttrName.Assign(Substring(attrStr, 5));
aAttrValue.Assign(value);
return true;
}
}
}
return false;
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -9,16 +9,17 @@
#define mozilla_a11y_aria_ARIAMap_h_
#include "ARIAStateMap.h"
#include "mozilla/a11y/AccTypes.h"
#include "mozilla/a11y/Role.h"
#include "nsAtom.h"
#include "nsIContent.h"
+#include "mozilla/dom/Element.h"
class nsINode;
////////////////////////////////////////////////////////////////////////////////
// Value constants
/**
* Used to define if role requires to expose Value interface.
@@ -151,17 +152,17 @@ struct nsRoleMapEntry
/**
* Return ARIA role.
*/
const nsDependentAtomString ARIARoleString() const
{ return nsDependentAtomString(*roleAtom); }
// ARIA role: string representation such as "button"
- nsAtom** roleAtom;
+ nsStaticAtom** 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
@@ -283,30 +284,31 @@ bool HasDefinedARIAHidden(nsIContent* aC
/**
* 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)
+ explicit AttrIterator(nsIContent* aContent)
+ : mElement(aContent->IsElement() ? aContent->AsElement() : nullptr)
+ , mAttrIdx(0)
{
- mAttrCount = mContent->GetAttrCount();
+ mAttrCount = mElement ? mElement->GetAttrCount() : 0;
}
bool Next(nsAString& aAttrName, nsAString& aAttrValue);
private:
AttrIterator() = delete;
AttrIterator(const AttrIterator&) = delete;
AttrIterator& operator= (const AttrIterator&) = delete;
- nsIContent* mContent;
+ dom::Element* mElement;
uint32_t mAttrIdx;
uint32_t mAttrCount;
};
} // namespace aria
} // namespace a11y
} // namespace mozilla
--- a/accessible/base/ARIAStateMap.cpp
+++ b/accessible/base/ARIAStateMap.cpp
@@ -15,21 +15,21 @@ 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.
- nsAtom* const mAttrName;
+ nsStaticAtom* const mAttrName;
// States if the attribute value is matched to the enum value. Used as
- // nsIContent::AttrValuesArray, last item must be nullptr.
- nsAtom* const* const mValues[4];
+ // Element::AttrValuesArray, last item must be nullptr.
+ nsStaticAtom* 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;
};
--- a/accessible/base/AccIterator.cpp
+++ b/accessible/base/AccIterator.cpp
@@ -80,17 +80,18 @@ RelatedAccIterator::
mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nullptr),
mBindingParent(nullptr), mIndex(0)
{
mBindingParent = aDependentContent->GetBindingParent();
nsAtom* IDAttr = mBindingParent ?
nsGkAtoms::anonid : nsGkAtoms::id;
nsAutoString id;
- if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
+ if (aDependentContent->IsElement() &&
+ aDependentContent->AsElement()->GetAttr(kNameSpaceID_None, IDAttr, id))
mProviders = mDocument->mDependentIDsHash.Get(id);
}
Accessible*
RelatedAccIterator::Next()
{
if (!mProviders)
return nullptr;
@@ -160,17 +161,17 @@ HTMLLabelIterator::Next()
// 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)) {
+ !walkUpEl->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
return walkUp;
}
if (walkUpEl->IsHTMLElement(nsGkAtoms::form))
break;
walkUp = walkUp->Parent();
@@ -252,18 +253,18 @@ XULDescriptionIterator::Next()
// IDRefsIterator
////////////////////////////////////////////////////////////////////////////////
IDRefsIterator::
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
nsAtom* aIDRefsAttr) :
mContent(aContent), mDoc(aDoc), mCurrIdx(0)
{
- if (mContent->IsInUncomposedDoc())
- mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
+ if (mContent->IsInUncomposedDoc() && mContent->IsElement())
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
}
const nsDependentSubstring
IDRefsIterator::NextID()
{
for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
break;
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -753,17 +753,17 @@ logging::MsgBegin(const char* aTitle, co
va_start(argptr, aMsgText);
vprintf(aMsgText, argptr);
va_end(argptr);
PRIntervalTime time = PR_IntervalNow();
uint32_t mins = (PR_IntervalToSeconds(time) / 60) % 60;
uint32_t secs = PR_IntervalToSeconds(time) % 60;
uint32_t msecs = PR_IntervalToMilliseconds(time) % 1000;
- printf("; %02d:%02d.%03d", mins, secs, msecs);
+ printf("; %02u:%02u.%03u", mins, secs, msecs);
printf("\n {\n");
}
void
logging::MsgEnd()
{
printf(" }\n");
@@ -829,17 +829,17 @@ logging::Node(const char* aDescr, nsINod
}
if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
printf("%s: %p, document\n", aDescr, static_cast<void*>(aNode));
return;
}
nsINode* parentNode = aNode->GetParentNode();
- int32_t idxInParent = parentNode ? parentNode->IndexOf(aNode) : - 1;
+ int32_t idxInParent = parentNode ? parentNode->ComputeIndexOf(aNode) : - 1;
if (aNode->IsNodeOfType(nsINode::eTEXT)) {
printf("%s: %p, text node, idx in parent: %d\n",
aDescr, static_cast<void*>(aNode), idxInParent);
return;
}
if (!aNode->IsElement()) {
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -414,33 +414,33 @@ NotificationController::ScheduleChildDoc
ScheduleProcessing();
}
void
NotificationController::ScheduleContentInsertion(Accessible* aContainer,
nsIContent* aStartChildNode,
nsIContent* aEndChildNode)
{
- nsTArray<nsCOMPtr<nsIContent>>* list =
- mContentInsertions.LookupOrAdd(aContainer);
+ nsTArray<nsCOMPtr<nsIContent>> list;
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))
+ if (list.AppendElement(node))
needsProcessing = true;
}
node = node->GetNextSibling();
}
if (needsProcessing) {
+ mContentInsertions.LookupOrAdd(aContainer)->AppendElements(list);
ScheduleProcessing();
}
}
void
NotificationController::ScheduleProcessing()
{
// If notification flush isn't planed yet start notification flush
@@ -454,21 +454,41 @@ NotificationController::ScheduleProcessi
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: protected
bool
NotificationController::IsUpdatePending()
{
return mPresShell->IsLayoutFlushObserver() ||
mObservingState == eRefreshProcessingForUpdate ||
+ WaitingForParent() ||
mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
mTextHash.Count() != 0 ||
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
}
+bool
+NotificationController::WaitingForParent()
+{
+ DocAccessible* parentdoc = mDocument->ParentDocument();
+ if (!parentdoc) {
+ return false;
+ }
+
+ NotificationController* parent = parentdoc->mNotificationController;
+ if (!parent || parent == this) {
+ // Do not wait for nothing or ourselves
+ return false;
+ }
+
+ // Wait for parent's notifications processing
+ return parent->mContentInsertions.Count() != 0 ||
+ parent->mNotifications.Length() != 0;
+}
+
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
@@ -587,16 +607,18 @@ NotificationController::ProcessMutationE
}
////////////////////////////////////////////////////////////////////////////////
// NotificationCollector: private
void
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
{
+ Telemetry::AutoTimer<Telemetry::A11Y_TREE_UPDATE_TIMING_MS> timer;
+
AUTO_PROFILER_LABEL("NotificationController::WillRefresh", OTHER);
// 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;
@@ -604,16 +626,25 @@ NotificationController::WillRefresh(mozi
// Wait until an update, we have started, or an interruptible reflow is
// finished.
if (mObservingState == eRefreshProcessing ||
mObservingState == eRefreshProcessingForUpdate ||
mPresShell->IsReflowInterrupted()) {
return;
}
+ // Process parent's notifications before ours, to get proper ordering between
+ // e.g. tab event and content event.
+ if (WaitingForParent()) {
+ mDocument->ParentDocument()->mNotificationController->WillRefresh(aTime);
+ if (!mDocument) {
+ 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).
@@ -641,29 +672,30 @@ NotificationController::WillRefresh(mozi
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!");
+ // If the text node is not in tree or doesn't have a frame, or placed in
+ // another document, then this case should have been handled already by
+ // content removal notifications.
+ nsINode* containerNode = textNode->GetFlattenedTreeParentNode();
+ if (!containerNode || textNode->OwnerDoc() != mDocument->DocumentNode()) {
+ MOZ_ASSERT(!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!");
+ MOZ_ASSERT(!textAcc,
+ "Text node isn't rendered but accessible is kept alive!");
continue;
}
#ifdef A11Y_LOG
nsIContent* containerElm = containerNode->IsElement() ?
containerNode->AsElement() : nullptr;
#endif
--- a/accessible/base/NotificationController.h
+++ b/accessible/base/NotificationController.h
@@ -269,16 +269,22 @@ protected:
nsCycleCollectingAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
/**
* Return true if the accessible tree state update is pending.
*/
bool IsUpdatePending();
+ /**
+ * Return true if we should wait for processing from the parent before we can
+ * process our own queue.
+ */
+ bool WaitingForParent();
+
private:
NotificationController(const NotificationController&);
NotificationController& operator = (const NotificationController&);
// nsARefreshObserver
virtual void WillRefresh(mozilla::TimeStamp aTime) override;
/**
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -47,18 +47,18 @@ bool ShouldA11yBeEnabled();
/*
* Do we have AccessibleHandler.dll registered.
*/
bool IsHandlerRegistered();
/*
* Name of platform service that instantiated accessibility
*/
-void SetInstantiator(const nsAString& aInstantiator);
-bool GetInstantiator(nsAString& aInstantiator);
+void SetInstantiator(const uint32_t aInstantiatorPid);
+bool GetInstantiator(nsIFile** aOutInstantiator);
#endif
/**
* Called to initialize platform specific accessibility support.
* Note this is called after internal accessibility support is initialized.
*/
void PlatformInit();
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -858,17 +858,17 @@ ROLE(DOCUMENT_FRAME,
eNoNameRule)
ROLE(HEADING,
"heading",
ATK_ROLE_HEADING,
@"AXHeading",
USE_ROLE_STRING,
IA2_ROLE_HEADING,
- eNameFromSubtreeIfReqRule)
+ eNameFromSubtreeRule)
ROLE(PAGE,
"page",
ATK_ROLE_PAGE,
NSAccessibilityGroupRole,
USE_ROLE_STRING,
IA2_ROLE_PAGE,
eNoNameRule)
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -296,23 +296,23 @@ TextAttrsMgr::InvalidTextAttr::
bool
TextAttrsMgr::InvalidTextAttr::
GetValue(nsIContent* aElm, uint32_t* aValue)
{
nsIContent* elm = aElm;
do {
if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
- static nsIContent::AttrValuesArray tokens[] =
+ static Element::AttrValuesArray tokens[] =
{ &nsGkAtoms::_false, &nsGkAtoms::grammar, &nsGkAtoms::spelling,
nullptr };
- int32_t idx = elm->FindAttrValueIn(kNameSpaceID_None,
- nsGkAtoms::aria_invalid, tokens,
- eCaseMatters);
+ int32_t idx = elm->AsElement()->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::aria_invalid,
+ tokens, eCaseMatters);
switch (idx) {
case 0:
*aValue = eFalse;
return true;
case 1:
*aValue = eGrammar;
return true;
case 2:
--- a/accessible/base/TextAttrs.h
+++ b/accessible/base/TextAttrs.h
@@ -394,17 +394,19 @@ protected:
* TextDecorTextAttr class is used for the work with
* "text-line-through-style", "text-line-through-color",
* "text-underline-style" and "text-underline-color" text attributes.
*/
class TextDecorValue
{
public:
- TextDecorValue() { }
+ TextDecorValue() :
+ mColor{0}, mLine{NS_STYLE_TEXT_DECORATION_LINE_NONE},
+ mStyle{NS_STYLE_TEXT_DECORATION_STYLE_NONE} { }
explicit TextDecorValue(nsIFrame* aFrame);
nscolor Color() const { return mColor; }
uint8_t Style() const { return mStyle; }
bool IsDefined() const
{ return IsUnderline() || IsLineThrough(); }
bool IsUnderline() const
--- a/accessible/base/TextRange.h
+++ b/accessible/base/TextRange.h
@@ -42,17 +42,17 @@ struct TextPoint final
* Represents a text range within the text control or document.
*/
class TextRange final
{
public:
TextRange(HyperTextAccessible* aRoot,
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
HyperTextAccessible* aEndContainer, int32_t aEndOffset);
- TextRange() {}
+ TextRange() : mStartOffset{0}, mEndOffset{0} {}
TextRange(TextRange&& aRange) :
mRoot(mozilla::Move(aRange.mRoot)),
mStartContainer(mozilla::Move(aRange.mStartContainer)),
mEndContainer(mozilla::Move(aRange.mEndContainer)),
mStartOffset(aRange.mStartOffset), mEndOffset(aRange.mEndOffset) {}
TextRange& operator= (TextRange&& aRange)
{
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -71,16 +71,18 @@ TreeWalker::~TreeWalker()
Accessible*
TreeWalker::Scope(nsIContent* aAnchorNode)
{
Reset();
mAnchorNode = aAnchorNode;
+ mFlags |= eScoped;
+
bool skipSubtree = false;
Accessible* acc = AccessibleFor(aAnchorNode, 0, &skipSubtree);
if (acc) {
mPhase = eAtEnd;
return acc;
}
return skipSubtree ? nullptr : Next();
@@ -107,16 +109,18 @@ TreeWalker::Seek(nsIContent* aChildNode)
if (!parentNode || !parentNode->IsElement()) {
return false;
}
// If ARIA owned child.
Accessible* child = mDoc->GetAccessible(childNode);
if (child && child->IsRelocated()) {
+ MOZ_ASSERT(!(mFlags & eScoped),
+ "Walker should not be scoped when seeking into relocated children");
if (child->Parent() != mContext) {
return false;
}
Accessible* ownedChild = nullptr;
while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
ownedChild != child);
@@ -144,22 +148,26 @@ Accessible*
TreeWalker::Next()
{
if (mStateStack.IsEmpty()) {
if (mPhase == eAtEnd) {
return nullptr;
}
if (mPhase == eAtDOM || mPhase == eAtARIAOwns) {
- mPhase = eAtARIAOwns;
- Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
- if (child) {
- mARIAOwnsIdx++;
- return child;
+ if (!(mFlags & eScoped)) {
+ mPhase = eAtARIAOwns;
+ Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
+ if (child) {
+ mARIAOwnsIdx++;
+ return child;
+ }
}
+ MOZ_ASSERT(!(mFlags & eScoped) || mPhase != eAtARIAOwns,
+ "Don't walk relocated children in scoped mode");
mPhase = eAtEnd;
return nullptr;
}
if (!mAnchorNode) {
mPhase = eAtEnd;
return nullptr;
}
@@ -225,21 +233,27 @@ TreeWalker::Prev()
{
if (mStateStack.IsEmpty()) {
if (mPhase == eAtStart || mPhase == eAtDOM) {
mPhase = eAtStart;
return nullptr;
}
if (mPhase == eAtEnd) {
- mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
- mPhase = eAtARIAOwns;
+ if (mFlags & eScoped) {
+ mPhase = eAtDOM;
+ } else {
+ mPhase = eAtARIAOwns;
+ mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
+ }
}
if (mPhase == eAtARIAOwns) {
+ MOZ_ASSERT(!(mFlags & eScoped),
+ "Should not walk relocated children in scoped mode");
if (mARIAOwnsIdx > 0) {
return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx);
}
if (!mAnchorNode) {
mPhase = eAtStart;
return nullptr;
}
--- a/accessible/base/TreeWalker.h
+++ b/accessible/base/TreeWalker.h
@@ -24,17 +24,18 @@ class DocAccessible;
*/
class TreeWalker final
{
public:
enum {
// used to walk the existing tree of the given node
eWalkCache = 1,
// used to walk the context tree starting from given node
- eWalkContextTree = 2 | eWalkCache
+ eWalkContextTree = 2 | eWalkCache,
+ eScoped = 4
};
/**
* Used to navigate and create if needed the accessible children.
*/
explicit TreeWalker(Accessible* aContext);
/**
new file mode 100644
--- /dev/null
+++ b/accessible/base/XULMap.h
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+XULMAP(image, New_MaybeImageOrToolbarButtonAccessible)
+XULMAP(statusbar, New_StatusBarAccessible)
+XULMAP(menuseparator, New_MenuSeparator)
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -107,10 +107,10 @@ else:
LOCAL_INCLUDES += [
'/accessible/other',
]
FINAL_LIBRARY = 'xul'
include('/ipc/chromium/chromium-config.mozbuild')
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -136,83 +136,90 @@ nsAccUtils::SetLiveContainerAttributes(n
{
nsAutoString live, relevant, busy;
nsIContent* ancestor = aStartContent;
while (ancestor) {
// container-relevant attribute
if (relevant.IsEmpty() &&
HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
- ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant))
+ ancestor->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_relevant, relevant))
SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
// container-live, and container-live-role attributes
if (live.IsEmpty()) {
const nsRoleMapEntry* role = nullptr;
if (ancestor->IsElement()) {
role = aria::GetRoleMap(ancestor->AsElement());
}
if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
- ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
+ ancestor->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
} else if (role) {
GetLiveAttrValue(role->liveAttRule, live);
}
if (!live.IsEmpty()) {
SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
if (role) {
SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
role->ARIARoleString());
}
}
}
// container-atomic attribute
- if (ancestor->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_atomic,
- nsGkAtoms::_true, eCaseMatters)) {
+ if (ancestor->IsElement() &&
+ ancestor->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::aria_atomic,
+ nsGkAtoms::_true, eCaseMatters)) {
SetAccAttr(aAttributes, nsGkAtoms::containerAtomic,
NS_LITERAL_STRING("true"));
}
// container-busy attribute
if (busy.IsEmpty() &&
HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
- ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
+ ancestor->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
if (ancestor == aTopEl)
break;
ancestor = ancestor->GetParent();
if (!ancestor)
ancestor = aTopEl; // Use <body>/<frameset>
}
}
bool
nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsAtom *aAtom)
{
NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
- if (!aContent->HasAttr(kNameSpaceID_None, aAtom) ||
- aContent->AttrValueIs(kNameSpaceID_None, aAtom,
- nsGkAtoms::_empty, eCaseMatters) ||
- aContent->AttrValueIs(kNameSpaceID_None, aAtom,
- nsGkAtoms::_undefined, eCaseMatters)) {
+ if (!aContent->IsElement())
+ return false;
+
+ Element* element = aContent->AsElement();
+ if (!element->HasAttr(kNameSpaceID_None, aAtom) ||
+ element->AttrValueIs(kNameSpaceID_None, aAtom, nsGkAtoms::_empty,
+ eCaseMatters) ||
+ element->AttrValueIs(kNameSpaceID_None, aAtom, nsGkAtoms::_undefined,
+ eCaseMatters)) {
return false;
}
return true;
}
nsAtom*
nsAccUtils::GetARIAToken(dom::Element* aElement, nsAtom* aAttr)
{
if (!HasDefinedARIAToken(aElement, aAttr))
return nsGkAtoms::_empty;
- static nsIContent::AttrValuesArray tokens[] =
+ static Element::AttrValuesArray tokens[] =
{ &nsGkAtoms::_false, &nsGkAtoms::_true,
&nsGkAtoms::mixed, nullptr};
int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
aAttr, tokens, eCaseMatters);
if (idx >= 0)
return *(tokens[idx]);
@@ -234,17 +241,19 @@ nsAccUtils::GetSelectableContainer(Acces
return nullptr;
}
return parent;
}
bool
nsAccUtils::IsARIASelected(Accessible* aAccessible)
{
- return aAccessible->GetContent()->
+ if (!aAccessible->GetContent()->IsElement())
+ return false;
+ return aAccessible->GetContent()->AsElement()->
AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
nsGkAtoms::_true, eCaseMatters);
}
Accessible*
nsAccUtils::TableFor(Accessible* aRow)
{
if (aRow) {
@@ -354,17 +363,17 @@ nsAccUtils::GetScreenCoordsForParent(Acc
if (!parent)
return nsIntPoint(0, 0);
nsIFrame *parentFrame = parent->GetFrame();
if (!parentFrame)
return nsIntPoint(0, 0);
nsRect rect = parentFrame->GetScreenRectInAppUnits();
- return nsPoint(rect.x, rect.y).
+ return nsPoint(rect.X(), rect.Y()).
ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel());
}
bool
nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
{
switch (aRule) {
case eOffLiveAttr:
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -20,18 +20,18 @@
#include "HTMLTableAccessibleWrap.h"
#include "HyperTextAccessibleWrap.h"
#include "RootAccessible.h"
#include "nsAccUtils.h"
#include "nsArrayUtils.h"
#include "nsAttrName.h"
#include "nsEventShell.h"
#include "nsIURI.h"
+#include "nsTextFormatter.h"
#include "OuterDocAccessible.h"
-#include "Platform.h"
#include "Role.h"
#ifdef MOZ_ACCESSIBILITY_ATK
#include "RootAccessibleWrap.h"
#endif
#include "States.h"
#include "Statistics.h"
#include "TextLeafAccessibleWrap.h"
#include "TreeWalker.h"
@@ -48,20 +48,17 @@
#include "HTMLWin32ObjectAccessible.h"
#include "mozilla/StaticPtr.h"
#endif
#ifdef A11Y_LOG
#include "Logging.h"
#endif
-#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
-#endif
-
#include "nsImageFrame.h"
#include "nsINamed.h"
#include "nsIObserverService.h"
#include "nsLayoutUtils.h"
#include "nsPluginFrame.h"
#include "SVGGeometryFrame.h"
#include "nsTreeBodyFrame.h"
#include "nsTreeColumns.h"
@@ -90,43 +87,54 @@
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
#include "nsNPAPIPluginInstance.h"
#endif
using namespace mozilla;
using namespace mozilla::a11y;
using namespace mozilla::dom;
+/**
+ * Accessibility service force enable/disable preference.
+ * Supported values:
+ * Accessibility is force enabled (accessibility should always be enabled): -1
+ * Accessibility is enabled (will be started upon a request, default value): 0
+ * Accessibility is force disabled (never enable accessibility): 1
+ */
+#define PREF_ACCESSIBILITY_FORCE_DISABLED "accessibility.force_disabled"
+
////////////////////////////////////////////////////////////////////////////////
// Statics
////////////////////////////////////////////////////////////////////////////////
/**
* Return true if the element must be accessible.
*/
static bool
MustBeAccessible(nsIContent* aContent, DocAccessible* aDocument)
{
if (aContent->GetPrimaryFrame()->IsFocusable())
return true;
- uint32_t attrCount = aContent->GetAttrCount();
- for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
- const nsAttrName* attr = aContent->GetAttrNameAt(attrIdx);
- if (attr->NamespaceEquals(kNameSpaceID_None)) {
- nsAtom* attrAtom = attr->Atom();
- nsDependentAtomString attrStr(attrAtom);
- if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
- continue; // not ARIA
+ if (aContent->IsElement()) {
+ uint32_t attrCount = aContent->AsElement()->GetAttrCount();
+ for (uint32_t attrIdx = 0; attrIdx < attrCount; attrIdx++) {
+ const nsAttrName* attr = aContent->AsElement()->GetAttrNameAt(attrIdx);
+ if (attr->NamespaceEquals(kNameSpaceID_None)) {
+ nsAtom* attrAtom = attr->Atom();
+ nsDependentAtomString attrStr(attrAtom);
+ if (!StringBeginsWith(attrStr, NS_LITERAL_STRING("aria-")))
+ continue; // not ARIA
- // A global state or a property and in case of token defined.
- uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
- if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
- nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
- return true;
+ // A global state or a property and in case of token defined.
+ uint8_t attrFlags = aria::AttrCharacteristicsFor(attrAtom);
+ if ((attrFlags & ATTR_GLOBAL) && (!(attrFlags & ATTR_VALTOKEN) ||
+ nsAccUtils::HasDefinedARIAToken(aContent, attrAtom))) {
+ return true;
+ }
}
}
}
// If the given ID is referred by relation attribute then create an accessible
// for it.
nsAutoString id;
if (nsCoreUtils::GetID(aContent, id) && !id.IsEmpty())
@@ -196,24 +204,37 @@ New_HTMLDefinition(nsIContent* aContent,
return nullptr;
}
static Accessible* New_HTMLLabel(nsIContent* aContent, Accessible* aContext)
{ return new HTMLLabelAccessible(aContent, aContext->Document()); }
static Accessible* New_HTMLInput(nsIContent* aContent, Accessible* aContext)
{
- if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::checkbox, eIgnoreCase)) {
+ if (!aContent->IsElement()) {
+ return nullptr;
+ }
+
+ Element* element = aContent->AsElement();
+ if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::checkbox, eIgnoreCase)) {
return new HTMLCheckboxAccessible(aContent, aContext->Document());
}
- if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::radio, eIgnoreCase)) {
+ if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::radio, eIgnoreCase)) {
return new HTMLRadioButtonAccessible(aContent, aContext->Document());
}
+ if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::time, eIgnoreCase)) {
+ return new EnumRoleAccessible<roles::GROUPING>(aContent, aContext->Document());
+ }
+ if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::date, eIgnoreCase)) {
+ return new EnumRoleAccessible<roles::DATE_EDITOR>(aContent, aContext->Document());
+ }
return nullptr;
}
static Accessible* New_HTMLOutput(nsIContent* aContent, Accessible* aContext)
{ return new HTMLOutputAccessible(aContent, aContext->Document()); }
static Accessible* New_HTMLProgress(nsIContent* aContent, Accessible* aContext)
{ return new HTMLProgressMeterAccessible(aContent, aContext->Document()); }
@@ -240,21 +261,54 @@ New_HTMLTableHeaderCell(nsIContent* aCon
return new HTMLTableHeaderCellAccessibleWrap(aContent, aContext->Document());
return nullptr;
}
static Accessible*
New_HTMLTableHeaderCellIfScope(nsIContent* aContent, Accessible* aContext)
{
if (aContext->IsTableRow() && aContext->GetContent() == aContent->GetParent() &&
- aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scope))
+ aContent->IsElement() &&
+ aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope))
return new HTMLTableHeaderCellAccessibleWrap(aContent, aContext->Document());
return nullptr;
}
+#ifdef MOZ_XUL
+static Accessible*
+New_MaybeImageOrToolbarButtonAccessible(nsIContent* aContent,
+ Accessible* aContext)
+{
+ if (aContent->IsElement() &&
+ aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
+ return new XULToolbarButtonAccessible(aContent, aContext->Document());
+ }
+
+ // Don't include nameless images in accessible tree.
+ if (!aContent->IsElement() ||
+ !aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext)) {
+ return nullptr;
+ }
+
+ return new ImageAccessibleWrap(aContent, aContext->Document());
+}
+static Accessible*
+New_MenuSeparator(nsIContent* aContent, Accessible* aContext)
+ { return new XULMenuSeparatorAccessible(aContent, aContext->Document()); }
+
+static Accessible*
+New_StatusBarAccessible(nsIContent* aContent, Accessible* aContext)
+ { return new XULStatusBarAccessible(aContent, aContext->Document()); }
+#endif
+
+/**
+ * Cached value of the PREF_ACCESSIBILITY_FORCE_DISABLED preference.
+ */
+static int32_t sPlatformDisabledState = 0;
+
////////////////////////////////////////////////////////////////////////////////
// Markup maps array.
#define Attr(name, value) \
{ &nsGkAtoms::name, &nsGkAtoms::value }
#define AttrFromDOM(name, DOMAttrName) \
{ &nsGkAtoms::name, nullptr, &nsGkAtoms::DOMAttrName }
@@ -264,32 +318,47 @@ New_HTMLTableHeaderCellIfScope(nsIConten
#define MARKUPMAP(atom, new_func, r, ... ) \
{ &nsGkAtoms::atom, new_func, static_cast<a11y::role>(r), { __VA_ARGS__ } },
static const MarkupMapInfo sMarkupMapList[] = {
#include "MarkupMap.h"
};
+#ifdef MOZ_XUL
+#define XULMAP(atom, new_func) \
+ { &nsGkAtoms::atom, new_func },
+
+static const XULMarkupMapInfo sXULMapList[] = {
+ #include "XULMap.h"
+};
+#endif
+
#undef Attr
#undef AttrFromDOM
#undef AttrFromDOMIf
#undef MARKUPMAP
+#ifdef MOZ_XUL
+#undef XULMAP
+#endif
////////////////////////////////////////////////////////////////////////////////
// nsAccessibilityService
////////////////////////////////////////////////////////////////////////////////
nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr;
ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
xpcAccessibleApplication* nsAccessibilityService::gXPCApplicationAccessible = nullptr;
uint32_t nsAccessibilityService::gConsumers = 0;
nsAccessibilityService::nsAccessibilityService() :
DocManager(), FocusManager(), mMarkupMaps(ArrayLength(sMarkupMapList))
+#ifdef MOZ_XUL
+ , mXULMarkupMaps(ArrayLength(sXULMapList))
+#endif
{
}
nsAccessibilityService::~nsAccessibilityService()
{
NS_ASSERTION(IsShutdown(), "Accessibility wasn't shutdown!");
gAccessibilityService = nullptr;
}
@@ -408,17 +477,17 @@ class PluginTimerCallBack final : public
{
~PluginTimerCallBack() {}
public:
explicit PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
NS_DECL_ISUPPORTS
- NS_IMETHOD Notify(nsITimer* aTimer) final
+ NS_IMETHOD Notify(nsITimer* aTimer) final override
{
if (!mContent->IsInUncomposedDoc())
return NS_OK;
nsIPresShell* ps = mContent->OwnerDoc()->GetShell();
if (ps) {
DocAccessible* doc = ps->GetDocAccessible();
if (doc) {
@@ -432,17 +501,17 @@ public:
// We couldn't get a doc accessible so presumably the document went away.
// In this case don't leak our ref to the content or timer.
sPendingPlugins->RemoveElement(mContent);
sPluginTimers->RemoveElement(aTimer);
return NS_OK;
}
- NS_IMETHOD GetName(nsACString& aName) final
+ NS_IMETHOD GetName(nsACString& aName) final override
{
aName.AssignLiteral("PluginTimerCallBack");
return NS_OK;
}
private:
nsCOMPtr<nsIContent> mContent;
};
@@ -463,19 +532,20 @@ nsAccessibilityService::CreatePluginAcce
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
RefPtr<nsNPAPIPluginInstance> pluginInstance;
if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
pluginInstance) {
#ifdef XP_WIN
if (!sPendingPlugins->Contains(aContent) &&
(Preferences::GetBool("accessibility.delay_plugins") ||
Compatibility::IsJAWS() || Compatibility::IsWE())) {
- nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
RefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent);
- timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"),
+ nsCOMPtr<nsITimer> timer;
+ NS_NewTimerWithCallback(getter_AddRefs(timer),
+ cb, Preferences::GetUint("accessibility.delay_plugin_time"),
nsITimer::TYPE_ONE_SHOT);
sPluginTimers->AppendElement(timer);
sPendingPlugins->AppendElement(aContent);
return nullptr;
}
// We need to remove aContent from the pending plugins here to avoid
// reentrancy. When the timer fires it calls
@@ -658,17 +728,17 @@ nsAccessibilityService::UpdateListBullet
listItem->UpdateBullet(aHasBullet);
}
}
}
void
nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame)
{
- nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
+ nsIPresShell* presShell = aImageFrame->PresShell();
DocAccessible* document = GetDocAccessible(presShell);
if (document) {
Accessible* accessible =
document->GetAccessible(aImageFrame->GetContent());
if (accessible) {
HTMLImageMapAccessible* imageMap = accessible->AsImageMap();
if (imageMap) {
imageMap->UpdateChildAreas();
@@ -1156,19 +1226,30 @@ nsAccessibilityService::CreateAccessible
if (deckFrame && deckFrame->GetSelectedBox() != frame) {
if (aIsSubtreeHidden)
*aIsSubtreeHidden = true;
return nullptr;
}
}
+#ifdef MOZ_XUL
+ // Prefer to use XUL to decide if and what kind of accessible to create.
+ const XULMarkupMapInfo* xulMap =
+ mXULMarkupMaps.Get(content->NodeInfo()->NameAtom());
+ if (xulMap && xulMap->new_func) {
+ newAcc = xulMap->new_func(content, aContext);
+ }
+#endif
+
// XBL bindings may use @role attribute to point the accessible type
// they belong to.
- newAcc = CreateAccessibleByType(content, document);
+ if (!newAcc) {
+ newAcc = CreateAccessibleByType(content, document);
+ }
// Any XUL box can be used as tabpanel, make sure we create a proper
// accessible for it.
if (!newAcc && aContext->IsXULTabpanels() &&
content->GetParent() == aContext->GetContent()) {
LayoutFrameType frameType = frame->Type();
if (frameType == LayoutFrameType::Box ||
frameType == LayoutFrameType::Scroll) {
@@ -1256,30 +1337,32 @@ nsAccessibilityService::Init()
#if defined(XP_WIN)
// This information needs to be initialized before the observer fires.
if (XRE_IsParentProcess()) {
Compatibility::Init();
}
#endif // defined(XP_WIN)
- static const char16_t kInitIndicator[] = { '1', 0 };
- observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator);
-
// Subscribe to EventListenerService.
nsCOMPtr<nsIEventListenerService> eventListenerService =
do_GetService("@mozilla.org/eventlistenerservice;1");
if (!eventListenerService)
return false;
eventListenerService->AddListenerChangeListener(this);
for (uint32_t i = 0; i < ArrayLength(sMarkupMapList); i++)
mMarkupMaps.Put(*sMarkupMapList[i].tag, &sMarkupMapList[i]);
+#ifdef MOZ_XUL
+ for (uint32_t i = 0; i < ArrayLength(sXULMapList); i++)
+ mXULMarkupMaps.Put(*sXULMapList[i].tag, &sXULMapList[i]);
+#endif
+
#ifdef A11Y_LOG
logging::CheckEnv();
#endif
gAccessibilityService = this;
NS_ADDREF(gAccessibilityService); // will release in Shutdown()
if (XRE_IsParentProcess()) {
@@ -1300,56 +1383,53 @@ nsAccessibilityService::Init()
#else
gApplicationAccessible = new ApplicationAccessible();
#endif // defined(XP_WIN)
}
NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
gApplicationAccessible->Init();
-#ifdef MOZ_CRASHREPORTER
CrashReporter::
AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
NS_LITERAL_CSTRING("Active"));
-#endif
#ifdef XP_WIN
sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
#endif
// Now its safe to start platform accessibility.
if (XRE_IsParentProcess())
PlatformInit();
statistics::A11yInitialized();
+ static const char16_t kInitIndicator[] = { '1', 0 };
+ observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator);
+
return true;
}
void
nsAccessibilityService::Shutdown()
{
// Application is going to be closed, shutdown accessibility and mark
// accessibility service as shutdown to prevent calls of its methods.
// Don't null accessibility service static member at this point to be safe
// if someone will try to operate with it.
MOZ_ASSERT(gConsumers, "Accessibility was shutdown already");
-
- gConsumers = 0;
+ UnsetConsumers(eXPCOM | eMainProcess | ePlatformAPI);
// Remove observers.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
-
- static const char16_t kShutdownIndicator[] = { '0', 0 };
- observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
}
// Stop accessible document loader.
DocManager::Shutdown();
SelectionManager::Shutdown();
#ifdef XP_WIN
@@ -1369,16 +1449,21 @@ nsAccessibilityService::Shutdown()
NS_RELEASE(gApplicationAccessible);
gApplicationAccessible = nullptr;
NS_IF_RELEASE(gXPCApplicationAccessible);
gXPCApplicationAccessible = nullptr;
NS_RELEASE(gAccessibilityService);
gAccessibilityService = nullptr;
+
+ if (observerService) {
+ static const char16_t kShutdownIndicator[] = { '0', 0 };
+ observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
+ }
}
already_AddRefed<Accessible>
nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
DocAccessible* aDoc)
{
nsAutoString role;
nsCoreUtils::XBLBindingRole(aContent, role);
@@ -1415,29 +1500,16 @@ nsAccessibilityService::CreateAccessible
accessible = new XULTabpanelsAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:dropmarker")) {
accessible = new XULDropmarkerAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:groupbox")) {
accessible = new XULGroupboxAccessible(aContent, aDoc);
- } else if (role.EqualsLiteral("xul:image")) {
- if (aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
- accessible = new XULToolbarButtonAccessible(aContent, aDoc);
-
- } else {
- // Don't include nameless images in accessible tree.
- if (!aContent->HasAttr(kNameSpaceID_None,
- nsGkAtoms::tooltiptext))
- return nullptr;
-
- accessible = new ImageAccessibleWrap(aContent, aDoc);
- }
-
} else if (role.EqualsLiteral("xul:link")) {
accessible = new XULLinkAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:listbox")) {
accessible = new XULListboxAccessibleWrap(aContent, aDoc);
} else if (role.EqualsLiteral("xul:listcell")) {
// Only create cells if there's more than one per row.
@@ -1480,35 +1552,31 @@ nsAccessibilityService::CreateAccessible
// then strip out redundant accessibles in the AccessibleWrap class for each platform.
nsIContent *parent = aContent->GetParent();
if (parent && parent->IsXULElement(nsGkAtoms::menu))
return nullptr;
#endif
accessible = new XULMenupopupAccessible(aContent, aDoc);
- } else if(role.EqualsLiteral("xul:menuseparator")) {
- accessible = new XULMenuSeparatorAccessible(aContent, aDoc);
-
} else if(role.EqualsLiteral("xul:pane")) {
accessible = new EnumRoleAccessible<roles::PANE>(aContent, aDoc);
} else if (role.EqualsLiteral("xul:panel")) {
- if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::noautofocus,
- nsGkAtoms::_true, eCaseMatters))
+ if (aContent->IsElement() &&
+ aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::noautofocus,
+ nsGkAtoms::_true, eCaseMatters))
accessible = new XULAlertAccessible(aContent, aDoc);
else
accessible = new EnumRoleAccessible<roles::PANE>(aContent, aDoc);
} else if (role.EqualsLiteral("xul:progressmeter")) {
accessible = new XULProgressMeterAccessible(aContent, aDoc);
- } else if (role.EqualsLiteral("xul:statusbar")) {
- accessible = new XULStatusBarAccessible(aContent, aDoc);
-
} else if (role.EqualsLiteral("xul:scale")) {
accessible = new XULSliderAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:radiobutton")) {
accessible = new XULRadioButtonAccessible(aContent, aDoc);
} else if (role.EqualsLiteral("xul:radiogroup")) {
accessible = new XULRadioGroupAccessible(aContent, aDoc);
@@ -1704,25 +1772,32 @@ nsAccessibilityService::MarkupAttributes
for (uint32_t i = 0; i < ArrayLength(markupMap->attrs); i++) {
const MarkupAttrInfo* info = markupMap->attrs + i;
if (!info->name)
break;
if (info->DOMAttrName) {
if (info->DOMAttrValue) {
- if (aContent->AttrValueIs(kNameSpaceID_None, *info->DOMAttrName,
- *info->DOMAttrValue, eCaseMatters)) {
+ if (aContent->IsElement() &&
+ aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ *info->DOMAttrName,
+ *info->DOMAttrValue,
+ eCaseMatters)) {
nsAccUtils::SetAccAttr(aAttributes, *info->name, *info->DOMAttrValue);
}
continue;
}
nsAutoString value;
- aContent->GetAttr(kNameSpaceID_None, *info->DOMAttrName, value);
+
+ if (aContent->IsElement()) {
+ aContent->AsElement()->GetAttr(kNameSpaceID_None, *info->DOMAttrName, value);
+ }
+
if (!value.IsEmpty())
nsAccUtils::SetAccAttr(aAttributes, *info->name, value);
continue;
}
nsAccUtils::SetAccAttr(aAttributes, *info->name, *info->value);
}
@@ -1801,30 +1876,84 @@ nsAccessibilityService::CreateAccessible
// Table or tree table accessible.
RefPtr<Accessible> accessible =
new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame);
return accessible.forget();
}
#endif
+void
+nsAccessibilityService::SetConsumers(uint32_t aConsumers) {
+ if (gConsumers & aConsumers) {
+ return;
+ }
+
+ gConsumers |= aConsumers;
+ NotifyOfConsumersChange();
+}
+
+void
+nsAccessibilityService::UnsetConsumers(uint32_t aConsumers) {
+ if (!(gConsumers & aConsumers)) {
+ return;
+ }
+
+ gConsumers &= ~aConsumers;
+ NotifyOfConsumersChange();
+}
+
+void
+nsAccessibilityService::GetConsumers(nsAString& aString)
+{
+ const char16_t* kJSONFmt =
+ u"{ \"XPCOM\": %s, \"MainProcess\": %s, \"PlatformAPI\": %s }";
+ nsString json;
+ nsTextFormatter::ssprintf(json, kJSONFmt,
+ gConsumers & eXPCOM ? "true" : "false",
+ gConsumers & eMainProcess ? "true" : "false",
+ gConsumers & ePlatformAPI ? "true" : "false");
+ aString.Assign(json);
+}
+
+void
+nsAccessibilityService::NotifyOfConsumersChange()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (!observerService) {
+ return;
+ }
+
+ nsAutoString consumers;
+ GetConsumers(consumers);
+ observerService->NotifyObservers(
+ nullptr, "a11y-consumers-changed", consumers.get());
+}
+
nsAccessibilityService*
GetOrCreateAccService(uint32_t aNewConsumer)
{
+ // Do not initialize accessibility if it is force disabled.
+ if (PlatformDisabledState() == ePlatformIsDisabled) {
+ return nullptr;
+ }
+
if (!nsAccessibilityService::gAccessibilityService) {
RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
if (!service->Init()) {
service->Shutdown();
return nullptr;
}
}
MOZ_ASSERT(nsAccessibilityService::gAccessibilityService,
"Accessible service is not initialized.");
- nsAccessibilityService::gConsumers |= aNewConsumer;
+ nsAccessibilityService::gAccessibilityService->SetConsumers(aNewConsumer);
return nsAccessibilityService::gAccessibilityService;
}
void
MaybeShutdownAccService(uint32_t aFormerConsumer)
{
nsAccessibilityService* accService =
nsAccessibilityService::gAccessibilityService;
@@ -1832,24 +1961,25 @@ MaybeShutdownAccService(uint32_t aFormer
if (!accService || accService->IsShutdown()) {
return;
}
if (nsCoreUtils::AccEventObserversExist() ||
xpcAccessibilityService::IsInUse() ||
accService->HasXPCDocuments()) {
// Still used by XPCOM
- nsAccessibilityService::gConsumers =
- (nsAccessibilityService::gConsumers & ~aFormerConsumer) |
- nsAccessibilityService::eXPCOM;
+ if (aFormerConsumer != nsAccessibilityService::eXPCOM) {
+ // Only unset non-XPCOM consumers.
+ accService->UnsetConsumers(aFormerConsumer);
+ }
return;
}
if (nsAccessibilityService::gConsumers & ~aFormerConsumer) {
- nsAccessibilityService::gConsumers &= ~aFormerConsumer;
+ accService->UnsetConsumers(aFormerConsumer);
} else {
accService->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
}
}
////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////
@@ -1886,23 +2016,45 @@ XPCApplicationAcc()
}
return nsAccessibilityService::gXPCApplicationAccessible;
}
EPlatformDisabledState
PlatformDisabledState()
{
- static int disabledState = 0xff;
-
- if (disabledState == 0xff) {
- disabledState = Preferences::GetInt("accessibility.force_disabled", 0);
- if (disabledState < ePlatformIsForceEnabled)
- disabledState = ePlatformIsForceEnabled;
- else if (disabledState > ePlatformIsDisabled)
- disabledState = ePlatformIsDisabled;
+ static bool platformDisabledStateCached = false;
+ if (platformDisabledStateCached) {
+ return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
}
- return (EPlatformDisabledState)disabledState;
+ platformDisabledStateCached = true;
+ Preferences::RegisterCallback(PrefChanged, PREF_ACCESSIBILITY_FORCE_DISABLED);
+ return ReadPlatformDisabledState();
+}
+
+EPlatformDisabledState
+ReadPlatformDisabledState()
+{
+ sPlatformDisabledState = Preferences::GetInt(PREF_ACCESSIBILITY_FORCE_DISABLED, 0);
+ if (sPlatformDisabledState < ePlatformIsForceEnabled) {
+ sPlatformDisabledState = ePlatformIsForceEnabled;
+ } else if (sPlatformDisabledState > ePlatformIsDisabled){
+ sPlatformDisabledState = ePlatformIsDisabled;
+ }
+
+ return static_cast<EPlatformDisabledState>(sPlatformDisabledState);
+}
+
+void
+PrefChanged(const char* aPref, void* aClosure)
+{
+ if (ReadPlatformDisabledState() == ePlatformIsDisabled) {
+ // Force shut down accessibility.
+ nsAccessibilityService* accService = nsAccessibilityService::gAccessibilityService;
+ if (accService && !accService->IsShutdown()) {
+ accService->Shutdown();
+ }
+ }
}
}
}
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -3,16 +3,17 @@
* License, v. 2.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 __nsAccessibilityService_h__
#define __nsAccessibilityService_h__
#include "mozilla/a11y/DocManager.h"
#include "mozilla/a11y/FocusManager.h"
+#include "mozilla/a11y/Platform.h"
#include "mozilla/a11y/Role.h"
#include "mozilla/a11y/SelectionManager.h"
#include "mozilla/Preferences.h"
#include "nsIObserver.h"
#include "nsIAccessibleEvent.h"
#include "nsIEventListenerService.h"
#include "xpcAccessibilityService.h"
@@ -48,30 +49,47 @@ SelectionManager* SelectionMgr();
* Returns the application accessible.
*/
ApplicationAccessible* ApplicationAcc();
xpcAccessibleApplication* XPCApplicationAcc();
typedef Accessible* (New_Accessible)(nsIContent* aContent, Accessible* aContext);
struct MarkupAttrInfo {
- nsAtom** name;
- nsAtom** value;
+ nsStaticAtom** name;
+ nsStaticAtom** value;
- nsAtom** DOMAttrName;
- nsAtom** DOMAttrValue;
+ nsStaticAtom** DOMAttrName;
+ nsStaticAtom** DOMAttrValue;
};
struct MarkupMapInfo {
- nsAtom** tag;
+ nsStaticAtom** tag;
New_Accessible* new_func;
a11y::role role;
MarkupAttrInfo attrs[4];
};
+#ifdef MOZ_XUL
+struct XULMarkupMapInfo {
+ nsStaticAtom** tag;
+ New_Accessible* new_func;
+};
+#endif
+
+/**
+ * PREF_ACCESSIBILITY_FORCE_DISABLED preference change callback.
+ */
+void PrefChanged(const char* aPref, void* aClosure);
+
+/**
+ * Read and normalize PREF_ACCESSIBILITY_FORCE_DISABLED preference.
+ */
+EPlatformDisabledState ReadPlatformDisabledState();
+
} // namespace a11y
} // namespace mozilla
class nsAccessibilityService final : public mozilla::a11y::DocManager,
public mozilla::a11y::FocusManager,
public mozilla::a11y::SelectionManager,
public nsIListenerChangeListener,
public nsIObserver
@@ -281,16 +299,36 @@ private:
/**
* Create an accessible whose type depends on the given frame.
*/
already_AddRefed<Accessible>
CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent,
Accessible* aContext);
+ /**
+ * Notify observers about change of the accessibility service's consumers.
+ */
+ void NotifyOfConsumersChange();
+
+ /**
+ * Get a JSON string representing the accessibility service consumers.
+ */
+ void GetConsumers(nsAString& aString);
+
+ /**
+ * Set accessibility service consumers.
+ */
+ void SetConsumers(uint32_t aConsumers);
+
+ /**
+ * Unset accessibility service consumers.
+ */
+ void UnsetConsumers(uint32_t aConsumers);
+
#ifdef MOZ_XUL
/**
* Create accessible for XUL tree element.
*/
already_AddRefed<Accessible>
CreateAccessibleForXULTree(nsIContent* aContent, DocAccessible* aDoc);
#endif
@@ -306,20 +344,24 @@ private:
static mozilla::a11y::xpcAccessibleApplication* gXPCApplicationAccessible;
/**
* Contains a set of accessibility service consumers.
*/
static uint32_t gConsumers;
nsDataHashtable<nsPtrHashKey<const nsAtom>, const mozilla::a11y::MarkupMapInfo*> mMarkupMaps;
+#ifdef MOZ_XUL
+ nsDataHashtable<nsPtrHashKey<const nsAtom>, const mozilla::a11y::XULMarkupMapInfo*> mXULMarkupMaps;
+#endif
friend nsAccessibilityService* GetAccService();
friend nsAccessibilityService* GetOrCreateAccService(uint32_t);
friend void MaybeShutdownAccService(uint32_t);
+ friend void mozilla::a11y::PrefChanged(const char*, void*);
friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
friend mozilla::a11y::SelectionManager* mozilla::a11y::SelectionMgr();
friend mozilla::a11y::ApplicationAccessible* mozilla::a11y::ApplicationAcc();
friend mozilla::a11y::xpcAccessibleApplication* mozilla::a11y::XPCApplicationAcc();
friend class xpcAccessibilityService;
};
/**
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -15,18 +15,19 @@ using namespace mozilla::a11y;
/**
* An object that stores a given traversal rule during the pivot movement.
*/
class RuleCache
{
public:
- explicit RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
- mAcceptRoles(nullptr) { }
+ explicit RuleCache(nsIAccessibleTraversalRule* aRule) :
+ mRule(aRule), mAcceptRoles(nullptr),
+ mAcceptRolesLength{0}, mPreFilter{0} { }
~RuleCache () {
if (mAcceptRoles)
free(mAcceptRoles);
}
nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult);
private:
@@ -580,18 +581,17 @@ nsAccessiblePivot::MoveToPoint(nsIAccess
if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE)
match = nullptr;
// Match if no node below this is a match
if ((filtered & nsIAccessibleTraversalRule::FILTER_MATCH) && !match) {
nsIntRect childRect = child->Bounds();
// Double-check child's bounds since the deepest child may have been out
// of bounds. This assures we don't return a false positive.
- if (aX >= childRect.x && aX < childRect.x + childRect.width &&
- aY >= childRect.y && aY < childRect.y + childRect.height)
+ if (childRect.Contains(aX, aY))
match = child;
}
child = child->Parent();
}
if (match || !aIgnoreNoMatch)
*aResult = MovePivotInternal(match, nsIAccessiblePivot::REASON_POINT,
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -179,32 +179,33 @@ nsCoreUtils::DispatchTouchEvent(EventMes
}
uint32_t
nsCoreUtils::GetAccessKeyFor(nsIContent* aContent)
{
// Accesskeys are registered by @accesskey attribute only. At first check
// whether it is presented on the given element to avoid the slow
// EventStateManager::GetRegisteredAccessKey() method.
- if (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::accesskey))
+ if (!aContent->IsElement() ||
+ !aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::accesskey))
return 0;
nsIPresShell* presShell = aContent->OwnerDoc()->GetShell();
if (!presShell)
return 0;
nsPresContext *presContext = presShell->GetPresContext();
if (!presContext)
return 0;
EventStateManager *esm = presContext->EventStateManager();
if (!esm)
return 0;
- return esm->GetRegisteredAccessKey(aContent);
+ return esm->GetRegisteredAccessKey(aContent->AsElement());
}
nsIContent *
nsCoreUtils::GetDOMElementFor(nsIContent *aContent)
{
if (aContent->IsElement())
return aContent;
@@ -220,17 +221,17 @@ nsCoreUtils::GetDOMNodeFromDOMPoint(nsIN
if (aNode && aNode->IsElement()) {
uint32_t childCount = aNode->GetChildCount();
NS_ASSERTION(aOffset <= childCount, "Wrong offset of the DOM point!");
// The offset can be after last child of container node that means DOM point
// is placed immediately after the last child. In this case use the DOM node
// from the given DOM point is used as result node.
if (aOffset != childCount)
- return aNode->GetChildAt(aOffset);
+ return aNode->GetChildAt_Deprecated(aOffset);
}
return aNode;
}
bool
nsCoreUtils::IsAncestorOf(nsINode *aPossibleAncestorNode,
nsINode *aPossibleDescendantNode,
@@ -296,17 +297,17 @@ nsCoreUtils::ScrollFrameToPoint(nsIFrame
{
nsIScrollableFrame* scrollableFrame = do_QueryFrame(aScrollableFrame);
if (!scrollableFrame)
return;
nsPoint point =
ToAppUnits(aPoint, aFrame->PresContext()->AppUnitsPerDevPixel());
nsRect frameRect = aFrame->GetScreenRectInAppUnits();
- nsPoint deltaPoint(point.x - frameRect.x, point.y - frameRect.y);
+ nsPoint deltaPoint = point - frameRect.TopLeft();
nsPoint scrollPoint = scrollableFrame->GetScrollPosition();
scrollPoint -= deltaPoint;
scrollableFrame->ScrollTo(scrollPoint, nsIScrollableFrame::INSTANT);
}
void
@@ -450,24 +451,28 @@ nsCoreUtils::IsErrorPage(nsIDocument *aD
NS_NAMED_LITERAL_CSTRING(certerror, "certerror");
return StringBeginsWith(path, neterror) || StringBeginsWith(path, certerror);
}
bool
nsCoreUtils::GetID(nsIContent *aContent, nsAString& aID)
{
- return aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aID);
+ return aContent->IsElement() &&
+ aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aID);
}
bool
nsCoreUtils::GetUIntAttr(nsIContent *aContent, nsAtom *aAttr, int32_t *aUInt)
{
nsAutoString value;
- aContent->GetAttr(kNameSpaceID_None, aAttr, value);
+ if (!aContent->IsElement()) {
+ return false;
+ }
+ aContent->AsElement()->GetAttr(kNameSpaceID_None, aAttr, value);
if (!value.IsEmpty()) {
nsresult error = NS_OK;
int32_t integer = value.ToInteger(&error);
if (NS_SUCCEEDED(error) && integer > 0) {
*aUInt = integer;
return true;
}
}
@@ -478,17 +483,18 @@ nsCoreUtils::GetUIntAttr(nsIContent *aCo
void
nsCoreUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
nsAString& aLanguage)
{
aLanguage.Truncate();
nsIContent *walkUp = aContent;
while (walkUp && walkUp != aRootContent &&
- !walkUp->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLanguage))
+ (!walkUp->IsElement() ||
+ !walkUp->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, aLanguage)))
walkUp = walkUp->GetParent();
}
already_AddRefed<nsIBoxObject>
nsCoreUtils::GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj)
{
nsCOMPtr<nsIDOMElement> tcElm;
aTreeBoxObj->GetTreeBody(getter_AddRefs(tcElm));
@@ -612,17 +618,17 @@ nsCoreUtils::GetPreviousSensibleColumn(n
return prevColumn.forget();
}
bool
nsCoreUtils::IsColumnHidden(nsITreeColumn *aColumn)
{
nsCOMPtr<nsIDOMElement> element;
aColumn->GetElement(getter_AddRefs(element));
- nsCOMPtr<nsIContent> content = do_QueryInterface(element);
+ nsCOMPtr<Element> content = do_QueryInterface(element);
return content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
nsGkAtoms::_true, eCaseMatters);
}
void
nsCoreUtils::ScrollTo(nsIPresShell* aPresShell, nsIContent* aContent,
uint32_t aScrollType)
{
@@ -672,14 +678,14 @@ nsCoreUtils::DispatchAccEvent(RefPtr<nsI
obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
}
void
nsCoreUtils::XBLBindingRole(const nsIContent* aEl, nsAString& aRole)
{
for (const nsXBLBinding* binding = aEl->GetXBLBinding(); binding;
binding = binding->GetBaseBinding()) {
- nsIContent* bindingElm = binding->PrototypeBinding()->GetBindingElement();
+ Element* bindingElm = binding->PrototypeBinding()->GetBindingElement();
bindingElm->GetAttr(kNameSpaceID_None, nsGkAtoms::role, aRole);
if (!aRole.IsEmpty())
break;
}
}
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -2,16 +2,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsCoreUtils_h_
#define nsCoreUtils_h_
#include "mozilla/EventForwards.h"
+#include "mozilla/dom/Element.h"
#include "nsIAccessibleEvent.h"
#include "nsIContent.h"
#include "nsIDocument.h" // for GetShell()
#include "nsIPresShell.h"
#include "nsPoint.h"
#include "nsTArray.h"
@@ -286,17 +287,18 @@ public:
uint32_t aScrollType);
/**
* Return true if the given node is table header element.
*/
static bool IsHTMLTableHeader(nsIContent *aContent)
{
return aContent->NodeInfo()->Equals(nsGkAtoms::th) ||
- aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::scope);
+ (aContent->IsElement() &&
+ aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope));
}
/**
* Returns true if the given string is empty or contains whitespace symbols
* only. In contrast to nsWhitespaceTokenizer class it takes into account
* non-breaking space (0xa0).
*/
static bool IsWhitespaceString(const nsAString& aString);
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -308,22 +308,22 @@ nsTextEquivUtils::AppendFromDOMNode(nsIC
nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl =
do_QueryInterface(aContent);
if (labeledEl) {
labeledEl->GetLabel(textEquivalent);
} else {
if (aContent->NodeInfo()->Equals(nsGkAtoms::label,
kNameSpaceID_XUL))
- aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
- textEquivalent);
+ aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value,
+ textEquivalent);
if (textEquivalent.IsEmpty())
- aContent->GetAttr(kNameSpaceID_None,
- nsGkAtoms::tooltiptext, textEquivalent);
+ aContent->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::tooltiptext, textEquivalent);
}
AppendString(aString, textEquivalent);
}
return AppendFromDOMChildren(aContent, aString);
}
--- a/accessible/generic/ARIAGridAccessible.cpp
+++ b/accessible/generic/ARIAGridAccessible.cpp
@@ -460,26 +460,28 @@ ARIAGridAccessible::GetCellInRowAt(Acces
nsresult
ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
bool aIsSelected, bool aNotify)
{
if (IsARIARole(nsGkAtoms::table))
return NS_OK;
- nsIContent *content = aAccessible->GetContent();
+ nsIContent* content = aAccessible->GetContent();
NS_ENSURE_STATE(content);
nsresult rv = NS_OK;
- if (aIsSelected)
- rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
- NS_LITERAL_STRING("true"), aNotify);
- else
- rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
- NS_LITERAL_STRING("false"), aNotify);
+ if (content->IsElement()) {
+ if (aIsSelected)
+ rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
+ NS_LITERAL_STRING("true"), aNotify);
+ else
+ rv = content->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
+ NS_LITERAL_STRING("false"), aNotify);
+ }
NS_ENSURE_SUCCESS(rv, rv);
// No "smart" select/unselect for internal call.
if (!aNotify)
return NS_OK;
// If row or cell accessible was selected then we're able to not bother about
@@ -634,21 +636,20 @@ ARIAGridCellAccessible::ApplyARIAState(u
return;
// Check aria-selected="true" on the row.
Accessible* row = Parent();
if (!row || row->Role() != roles::ROW)
return;
nsIContent *rowContent = row->GetContent();
- if (nsAccUtils::HasDefinedARIAToken(rowContent,
- nsGkAtoms::aria_selected) &&
- !rowContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::aria_selected,
- nsGkAtoms::_false, eCaseMatters))
+ if (nsAccUtils::HasDefinedARIAToken(rowContent, nsGkAtoms::aria_selected) &&
+ !rowContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::aria_selected,
+ nsGkAtoms::_false, eCaseMatters))
*aState |= states::SELECTABLE | states::SELECTED;
}
already_AddRefed<nsIPersistentProperties>
ARIAGridCellAccessible::NativeAttributes()
{
nsCOMPtr<nsIPersistentProperties> attributes =
HyperTextAccessibleWrap::NativeAttributes();
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -71,18 +71,18 @@ Accessible::SetRoleMapEntry(const nsRole
}
inline bool
Accessible::IsSearchbox() const
{
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
return (roleMapEntry && roleMapEntry->Is(nsGkAtoms::searchbox)) ||
(mContent->IsHTMLElement(nsGkAtoms::input) &&
- mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::search, eCaseMatters));
+ mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::search, eCaseMatters));
}
inline bool
Accessible::HasGenericType(AccGenericType aType) const
{
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
return (mGenericTypes & aType) ||
(roleMapEntry && roleMapEntry->IsOfType(aType));
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -148,22 +148,22 @@ Accessible::Name(nsString& aName)
}
ENameValueFlag nameFlag = NativeName(aName);
if (!aName.IsEmpty())
return nameFlag;
// In the end get the name from tooltip.
if (mContent->IsHTMLElement()) {
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
aName.CompressWhitespace();
return eNameFromTooltip;
}
} else if (mContent->IsXULElement()) {
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
aName.CompressWhitespace();
return eNameFromTooltip;
}
} else if (mContent->IsSVGElement()) {
// If user agents need to choose among multiple ‘desc’ or ‘title’ elements
// for processing, the user agent shall choose the first one.
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
@@ -197,19 +197,19 @@ Accessible::Description(nsString& aDescr
aDescription);
if (aDescription.IsEmpty()) {
NativeDescription(aDescription);
if (aDescription.IsEmpty()) {
// Keep the Name() method logic.
if (mContent->IsHTMLElement()) {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
} else if (mContent->IsXULElement()) {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
} else if (mContent->IsSVGElement()) {
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (childElm->IsSVGElement(nsGkAtoms::desc)) {
nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
&aDescription);
break;
}
@@ -446,17 +446,17 @@ Accessible::NativeState()
else
state |= states::HORIZONTAL;
}
}
}
// Check if a XUL element has the popup attribute (an attached popup menu).
if (HasOwnContent() && mContent->IsXULElement() &&
- mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
+ mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
state |= states::HASPOPUP;
// Bypass the link states specialization for non links.
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
if (!roleMapEntry || roleMapEntry->roleRule == kUseNativeRole ||
roleMapEntry->role == roles::LINK)
state |= NativeLinkState();
@@ -486,18 +486,19 @@ Accessible::NativeLinkState() const
}
bool
Accessible::NativelyUnavailable() const
{
if (mContent->IsHTMLElement())
return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
- return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
- nsGkAtoms::_true, eCaseMatters);
+ return mContent->IsElement() &&
+ mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
+ nsGkAtoms::_true, eCaseMatters);
}
Accessible*
Accessible::FocusedChild()
{
Accessible* focus = FocusMgr()->FocusedAccessible();
if (focus && (focus == this || focus->Parent() == this))
return focus;
@@ -508,18 +509,17 @@ Accessible::FocusedChild()
Accessible*
Accessible::ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild)
{
// If we can't find the point in a child, we will return the fallback answer:
// we return |this| if the point is within it, otherwise nullptr.
Accessible* fallbackAnswer = nullptr;
nsIntRect rect = Bounds();
- if (aX >= rect.x && aX < rect.x + rect.width &&
- aY >= rect.y && aY < rect.y + rect.height)
+ if (rect.Contains(aX, aY))
fallbackAnswer = this;
if (nsAccUtils::MustPrune(this)) // Do not dig any further
return fallbackAnswer;
// Search an accessible at the given point starting from accessible document
// because containing block (see CSS2) for out of flow element (for example,
// absolutely positioned element) may be different from its DOM parent and
@@ -537,17 +537,17 @@ Accessible::ChildAtPoint(int32_t aX, int
// Check whether the point is at popup content.
nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
NS_ENSURE_TRUE(rootWidget, nullptr);
LayoutDeviceIntRect rootRect = rootWidget->GetScreenBounds();
WidgetMouseEvent dummyEvent(true, eMouseMove, rootWidget,
WidgetMouseEvent::eSynthesized);
- dummyEvent.mRefPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
+ dummyEvent.mRefPoint = LayoutDeviceIntPoint(aX - rootRect.X(), aY - rootRect.Y());
nsIFrame* popupFrame = nsLayoutUtils::
GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
&dummyEvent);
if (popupFrame) {
// If 'this' accessible is not inside the popup then ignore the popup when
// searching an accessible at point.
DocAccessible* popupDoc =
@@ -559,18 +559,18 @@ Accessible::ChildAtPoint(int32_t aX, int
popupChild = popupChild->Parent();
if (popupChild == popupAcc)
startFrame = popupFrame;
}
nsPresContext* presContext = startFrame->PresContext();
nsRect screenRect = startFrame->GetScreenRectInAppUnits();
- nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
- presContext->DevPixelsToAppUnits(aY) - screenRect.y);
+ nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.X(),
+ presContext->DevPixelsToAppUnits(aY) - screenRect.Y());
nsIFrame* foundFrame = nsLayoutUtils::GetFrameForPoint(startFrame, offset);
nsIContent* content = nullptr;
if (!foundFrame || !(content = foundFrame->GetContent()))
return fallbackAnswer;
// Get accessible for the node with the point or the first accessible in
// the DOM parent chain.
@@ -611,18 +611,17 @@ Accessible::ChildAtPoint(int32_t aX, int
// where layout won't walk into things for us, such as image map areas and
// sub documents (XXX: subdocuments should be handled by methods of
// OuterDocAccessibles).
uint32_t childCount = accessible->ChildCount();
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
Accessible* child = accessible->GetChildAt(childIdx);
nsIntRect childRect = child->Bounds();
- if (aX >= childRect.x && aX < childRect.x + childRect.width &&
- aY >= childRect.y && aY < childRect.y + childRect.height &&
+ if (childRect.Contains(aX, aY) &&
(child->State() & states::INVISIBLE) == 0) {
if (aWhichChild == eDeepestChild)
return child->ChildAtPoint(aX, aY, eDeepestChild);
return child;
}
}
@@ -675,46 +674,47 @@ Accessible::Bounds() const
{
nsIFrame* boundingFrame = nullptr;
nsRect unionRectTwips = RelativeBounds(&boundingFrame);
if (!boundingFrame)
return nsIntRect();
nsIntRect screenRect;
nsPresContext* presContext = mDoc->PresContext();
- screenRect.x = presContext->AppUnitsToDevPixels(unionRectTwips.x);
- screenRect.y = presContext->AppUnitsToDevPixels(unionRectTwips.y);
- screenRect.width = presContext->AppUnitsToDevPixels(unionRectTwips.width);
- screenRect.height = presContext->AppUnitsToDevPixels(unionRectTwips.height);
+ screenRect.SetRect(presContext->AppUnitsToDevPixels(unionRectTwips.X()),
+ presContext->AppUnitsToDevPixels(unionRectTwips.Y()),
+ presContext->AppUnitsToDevPixels(unionRectTwips.Width()),
+ presContext->AppUnitsToDevPixels(unionRectTwips.Height()));
// We have the union of the rectangle, now we need to put it in absolute
// screen coords.
nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
ToNearestPixels(presContext->AppUnitsPerDevPixel());
- screenRect.x += orgRectPixels.x;
- screenRect.y += orgRectPixels.y;
+ screenRect.MoveBy(orgRectPixels.X(), orgRectPixels.Y());
return screenRect;
}
void
Accessible::SetSelected(bool aSelect)
{
if (!HasOwnContent())
return;
Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
if (select) {
if (select->State() & states::MULTISELECTABLE) {
- if (ARIARoleMap()) {
+ if (mContent->IsElement() && ARIARoleMap()) {
if (aSelect) {
- mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
- NS_LITERAL_STRING("true"), true);
+ mContent->AsElement()->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_selected,
+ NS_LITERAL_STRING("true"), true);
} else {
- mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true);
+ mContent->AsElement()->UnsetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_selected, true);
}
}
return;
}
if (aSelect)
TakeFocus();
}
@@ -820,19 +820,20 @@ Accessible::XULElmName(DocAccessible* aD
// Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
nsIContent *bindingParent = aElm->GetBindingParent();
nsIContent* parent =
bindingParent? bindingParent->GetParent() : aElm->GetParent();
nsAutoString ancestorTitle;
while (parent) {
if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
- parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
+ parent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
// Before returning this, check if the element itself has a tooltip:
- if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
+ if (aElm->IsElement() &&
+ aElm->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
aName.CompressWhitespace();
return;
}
aName.Assign(ancestorTitle);
aName.CompressWhitespace();
return;
}
@@ -937,17 +938,17 @@ Accessible::Attributes()
// 'xml-roles' attribute for landmark.
nsAtom* landmark = LandmarkRole();
if (landmark) {
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, landmark);
} else {
// 'xml-roles' attribute coming from ARIA.
nsAutoString xmlRoles;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles))
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles))
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles);
}
// Expose object attributes from ARIA attributes.
nsAutoString unused;
aria::AttrIterator attribIter(mContent);
nsAutoString name, value;
while(attribIter.Next(name, value))
@@ -1058,17 +1059,17 @@ Accessible::NativeAttributes()
return attributes.forget();
nsAutoString id;
if (nsCoreUtils::GetID(mContent, id))
attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, unused);
// Expose class because it may have useful microformat information.
nsAutoString _class;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::_class, _class);
// Expose tag.
nsAutoString tagName;
mContent->NodeInfo()->GetName(tagName);
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tag, tagName);
// Expose draggable object attribute.
@@ -1172,19 +1173,20 @@ Accessible::State()
// Apply ARIA states to be sure accessible states will be overridden.
ApplyARIAState(&state);
// If this is an ARIA item of the selectable widget and if it's focused and
// not marked unselected explicitly (i.e. aria-selected="false") then expose
// it as selected to make ARIA widget authors life easier.
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
if (roleMapEntry && !(state & states::SELECTED) &&
- !mContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::aria_selected,
- nsGkAtoms::_false, eCaseMatters)) {
+ (!mContent->IsElement() ||
+ !mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::aria_selected,
+ nsGkAtoms::_false, eCaseMatters))) {
// Special case for tabs: focused tab or focus inside related tab panel
// implies selected state.
if (roleMapEntry->role == roles::PAGETAB) {
if (state & states::FOCUSED) {
state |= states::SELECTED;
} else {
// If focus is in a child of the tab panel surely the tab is selected!
Relation rel = RelationByType(RelationType::LABEL_FOR);
@@ -1332,20 +1334,24 @@ Accessible::Value(nsString& aValue)
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
if (!roleMapEntry)
return;
if (roleMapEntry->valueRule != eNoValue) {
// aria-valuenow is a number, and aria-valuetext is the optional text
// equivalent. For the string value, we will try the optional text
// equivalent first.
- if (!mContent->GetAttr(kNameSpaceID_None,
- nsGkAtoms::aria_valuetext, aValue)) {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
- aValue);
+ if (!mContent->IsElement()) {
+ return;
+ }
+
+ if (!mContent->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_valuetext, aValue)) {
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
+ aValue);
}
return;
}
// Value of textbox is a textified subtree.
if (roleMapEntry->Is(nsGkAtoms::textbox)) {
nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
return;
@@ -1411,18 +1417,22 @@ Accessible::SetCurValue(double aValue)
checkValue = MaxValue();
if (!IsNaN(checkValue) && aValue > checkValue)
return false;
nsAutoString strValue;
strValue.AppendFloat(aValue);
+ if (!mContent->IsElement())
+ return true;
+
return NS_SUCCEEDED(
- mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
+ mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow,
+ strValue, true));
}
role
Accessible::ARIATransformRole(role aRole)
{
// Beginning with ARIA 1.1, user agents are expected to use the native host
// language role of the element when the region role is used without a name.
// https://rawgit.com/w3c/aria/master/core-aam/core-aam.html#role-map-region
@@ -1443,20 +1453,21 @@ Accessible::ARIATransformRole(role aRole
// where the accessible role depends on both the role and ARIA state.
if (aRole == roles::PUSHBUTTON) {
if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
// For simplicity, any existing pressed attribute except "" or "undefined"
// indicates a toggle.
return roles::TOGGLE_BUTTON;
}
- if (mContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::aria_haspopup,
- nsGkAtoms::_true,
- eCaseMatters)) {
+ if (mContent->IsElement() &&
+ mContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::aria_haspopup,
+ nsGkAtoms::_true,
+ eCaseMatters)) {
// For button with aria-haspopup="true".
return roles::BUTTONMENU;
}
} else if (aRole == roles::LISTBOX) {
// A listbox inside of a combobox needs a special role because of ATK
// mapping to menu.
if (mParent && mParent->IsCombobox()) {
@@ -1471,18 +1482,19 @@ Accessible::ARIATransformRole(role aRole
}
} else if (aRole == roles::OPTION) {
if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
return roles::COMBOBOX_OPTION;
} else if (aRole == roles::MENUITEM) {
// Menuitem has a submenu.
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
- nsGkAtoms::_true, eCaseMatters)) {
+ if (mContent->IsElement() &&
+ mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
+ nsGkAtoms::_true, eCaseMatters)) {
return roles::PARENT_MENUITEM;
}
}
return aRole;
}
nsAtom*
@@ -1586,17 +1598,20 @@ Accessible::DoAction(uint8_t aIndex)
return false;
}
nsIContent*
Accessible::GetAtomicRegion() const
{
nsIContent *loopContent = mContent;
nsAutoString atomic;
- while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
+ while (loopContent &&
+ (!loopContent->IsElement() ||
+ !loopContent->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_atomic, atomic)))
loopContent = loopContent->GetParent();
return atomic.EqualsLiteral("true") ? loopContent : nullptr;
}
Relation
Accessible::RelationByType(RelationType aType)
{
@@ -1976,17 +1991,19 @@ Accessible::ARIAName(nsString& aName)
// aria-labelledby now takes precedence over aria-label
nsresult rv = nsTextEquivUtils::
GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
if (NS_SUCCEEDED(rv)) {
aName.CompressWhitespace();
}
if (aName.IsEmpty() &&
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
+ mContent->IsElement() &&
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label,
+ aName)) {
aName.CompressWhitespace();
}
}
// Accessible protected
ENameValueFlag
Accessible::NativeName(nsString& aName)
{
@@ -2533,30 +2550,32 @@ Accessible::IsActiveWidget() const
return false;
}
bool
Accessible::AreItemsOperable() const
{
return HasOwnContent() &&
- mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
+ mContent->IsElement() &&
+ mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
}
Accessible*
Accessible::CurrentItem()
{
// Check for aria-activedescendant, which changes which element has focus.
// For activedescendant, the ARIA spec does not require that the user agent
// checks whether pointed node is actually a DOM descendant of the element
// with the aria-activedescendant attribute.
nsAutoString id;
if (HasOwnContent() &&
- mContent->GetAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant, id)) {
+ mContent->IsElement() &&
+ mContent->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_activedescendant, id)) {
nsIDocument* DOMDoc = mContent->OwnerDoc();
dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
if (activeDescendantElm) {
DocAccessible* document = Document();
if (document)
return document->GetAccessible(activeDescendantElm);
}
}
@@ -2565,30 +2584,33 @@ Accessible::CurrentItem()
void
Accessible::SetCurrentItem(Accessible* aItem)
{
nsAtom* id = aItem->GetContent()->GetID();
if (id) {
nsAutoString idStr;
id->ToString(idStr);
- mContent->SetAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant, idStr, true);
+ mContent->AsElement()->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_activedescendant,
+ idStr,
+ true);
}
}
Accessible*
Accessible::ContainerWidget() const
{
if (HasARIARole() && mContent->HasID()) {
for (Accessible* parent = Parent(); parent; parent = parent->Parent()) {
nsIContent* parentContent = parent->GetContent();
if (parentContent &&
- parentContent->HasAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant)) {
+ parentContent->IsElement() &&
+ parentContent->AsElement()->HasAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_activedescendant)) {
return parent;
}
// Don't cross DOM document boundaries.
if (parent->IsDoc())
break;
}
}
@@ -2651,33 +2673,34 @@ Accessible::GetSiblingAtOffset(int32_t a
double
Accessible::AttrNumericValue(nsAtom* aAttr) const
{
const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
return UnspecifiedNaN<double>();
nsAutoString attrValue;
- if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
+ if (!mContent->IsElement() ||
+ !mContent->AsElement()->GetAttr(kNameSpaceID_None, aAttr, attrValue))
return UnspecifiedNaN<double>();
nsresult error = NS_OK;
double value = attrValue.ToDouble(&error);
return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
}
uint32_t
Accessible::GetActionRule() const
{
if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
return eNoAction;
// Return "click" action on elements that have an attached popup menu.
if (mContent->IsXULElement())
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
return eClickAction;
// Has registered 'click' event handler.
bool isOnclick = nsCoreUtils::HasClickListener(mContent);
if (isOnclick)
return eClickAction;
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -174,17 +174,17 @@ public:
nsIContent* GetContent() const { return mContent; }
mozilla::dom::Element* Elm() const
{ return mContent && mContent->IsElement() ? mContent->AsElement() : nullptr; }
/**
* Return node type information of DOM node associated with the accessible.
*/
bool IsContent() const
- { return GetNode() && GetNode()->IsNodeOfType(nsINode::eCONTENT); }
+ { return GetNode() && GetNode()->IsContent(); }
/**
* Return the unique identifier of the accessible.
*/
void* UniqueID() { return static_cast<void*>(this); }
/**
* Return language associated with the accessible.
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -13,16 +13,17 @@
#include "Role.h"
#include "States.h"
#include "nsIComponentManager.h"
#include "nsIDOMDocument.h"
#include "nsIWindowMediator.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Services.h"
+#include "nsGlobalWindow.h"
#include "nsIStringBundle.h"
using namespace mozilla::a11y;
ApplicationAccessible::ApplicationAccessible() :
AccessibleWrap(nullptr, nullptr)
{
mType = eApplicationType;
@@ -159,38 +160,32 @@ void
ApplicationAccessible::Init()
{
// Basically children are kept updated by Append/RemoveChild method calls.
// However if there are open windows before accessibility was started
// then we need to make sure root accessibles for open windows are created so
// that all root accessibles are stored in application accessible children
// array.
- nsCOMPtr<nsIWindowMediator> windowMediator =
- do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
+ nsGlobalWindowOuter::OuterWindowByIdTable* windowsById =
+ nsGlobalWindowOuter::GetWindowsTable();
- nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
- nsresult rv = windowMediator->GetEnumerator(nullptr,
- getter_AddRefs(windowEnumerator));
- if (NS_FAILED(rv))
+ if (!windowsById) {
return;
+ }
- bool hasMore = false;
- windowEnumerator->HasMoreElements(&hasMore);
- while (hasMore) {
- nsCOMPtr<nsISupports> window;
- windowEnumerator->GetNext(getter_AddRefs(window));
- nsCOMPtr<nsPIDOMWindowOuter> DOMWindow = do_QueryInterface(window);
- if (DOMWindow) {
- nsCOMPtr<nsIDocument> docNode = DOMWindow->GetDoc();
+ for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
+ nsGlobalWindowOuter* window = iter.Data();
+ if (window->GetDocShell() && window->IsRootOuterWindow()) {
+ nsCOMPtr<nsIDocument> docNode = window->GetExtantDoc();
+
if (docNode) {
- GetAccService()->GetDocAccessible(docNode); // ensure creation
+ GetAccService()->GetDocAccessible(docNode); // ensure creation
}
}
- windowEnumerator->HasMoreElements(&hasMore);
}
}
Accessible*
ApplicationAccessible::GetSiblingAtOffset(int32_t aOffset,
nsresult* aError) const
{
if (aError)
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -18,23 +18,21 @@
#include "RootAccessible.h"
#include "TreeWalker.h"
#include "xpcAccessibleDocument.h"
#include "nsIMutableArray.h"
#include "nsICommandManager.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
-#include "nsIDOMAttr.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMDocument.h"
#include "nsIDOMXULDocument.h"
#include "nsIDOMMutationEvent.h"
#include "nsPIDOMWindow.h"
-#include "nsIDOMXULPopupElement.h"
#include "nsIEditingSession.h"
#include "nsIFrame.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsImageFrame.h"
#include "nsIPersistentProperties2.h"
#include "nsIPresShell.h"
#include "nsIServiceManager.h"
#include "nsViewManager.h"
@@ -57,17 +55,17 @@
#endif
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// Static member initialization
-static nsAtom** kRelationAttrs[] =
+static nsStaticAtom** kRelationAttrs[] =
{
&nsGkAtoms::aria_labelledby,
&nsGkAtoms::aria_describedby,
&nsGkAtoms::aria_details,
&nsGkAtoms::aria_owns,
&nsGkAtoms::aria_controls,
&nsGkAtoms::aria_flowto,
&nsGkAtoms::aria_errormessage,
@@ -89,17 +87,17 @@ DocAccessible::
// confusing leak checking machinary.
HyperTextAccessibleWrap(nullptr, nullptr),
// XXX aaronl should we use an algorithm for the initial cache size?
mAccessibleCache(kDefaultCacheLength),
mNodeToAccessibleMap(kDefaultCacheLength),
mDocumentNode(aDocument),
mScrollPositionChangedTicks(0),
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
- mVirtualCursor(nullptr),
+ mARIAAttrOldValue{nullptr}, mVirtualCursor(nullptr),
mPresShell(aPresShell), mIPCDoc(nullptr)
{
mGenericTypes |= eDocument;
mStateFlags |= eNotNodeMapEntry;
mDoc = this;
MOZ_ASSERT(mPresShell, "should have been given a pres shell");
mPresShell->SetDocAccessible(this);
@@ -645,25 +643,24 @@ DocAccessible::ScrollPositionDidChange(n
{
// Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
// then the ::Notify() method will fire the accessibility event for scroll position changes
const uint32_t kScrollPosCheckWait = 50;
if (mScrollWatchTimer) {
mScrollWatchTimer->SetDelay(kScrollPosCheckWait); // Create new timer, to avoid leaks
}
else {
- mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
+ NS_NewTimerWithFuncCallback(getter_AddRefs(mScrollWatchTimer),
+ ScrollTimerCallback,
+ this,
+ kScrollPosCheckWait,
+ nsITimer::TYPE_REPEATING_SLACK,
+ "a11y::DocAccessible::ScrollPositionDidChange");
if (mScrollWatchTimer) {
NS_ADDREF_THIS(); // Kung fu death grip
- mScrollWatchTimer->InitWithNamedFuncCallback(
- ScrollTimerCallback,
- this,
- kScrollPosCheckWait,
- nsITimer::TYPE_REPEATING_SLACK,
- "a11y::DocAccessible::ScrollPositionDidChange");
}
}
mScrollPositionChangedTicks = 1;
}
////////////////////////////////////////////////////////////////////////////////
// nsIObserver
@@ -872,17 +869,17 @@ DocAccessible::AttributeChangedImpl(Acce
return;
}
if (aAttribute == nsGkAtoms::aria_describedby) {
FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
return;
}
- nsIContent* elm = aAccessible->GetContent();
+ dom::Element* elm = aAccessible->GetContent()->AsElement();
if (aAttribute == nsGkAtoms::aria_labelledby &&
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
return;
}
if (aAttribute == nsGkAtoms::alt &&
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
@@ -993,17 +990,17 @@ DocAccessible::ARIAAttributeChanged(Acce
// change event; at least until native API comes up with a more meaningful event.
uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
if (!(attrFlags & ATTR_BYPASSOBJ)) {
RefPtr<AccEvent> event =
new AccObjectAttrChangedEvent(aAccessible, aAttribute);
FireDelayedEvent(event);
}
- nsIContent* elm = aAccessible->GetContent();
+ dom::Element* elm = aAccessible->GetContent()->AsElement();
// Update aria-hidden flag for the whole subtree iff aria-hidden is changed
// on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
// of top aria-hidden.
if (aAttribute == nsGkAtoms::aria_hidden) {
bool isDefined = aria::HasDefinedARIAHidden(elm);
if (isDefined != aAccessible->IsARIAHidden() &&
(!aAccessible->Parent() || !aAccessible->Parent()->IsARIAHidden())) {
@@ -1069,19 +1066,21 @@ DocAccessible::ARIAAttributeChanged(Acce
mNotificationController->ScheduleRelocation(aAccessible);
}
}
void
DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
{
nsIContent* elm = aAccessible->GetContent();
- if (elm && aAccessible->IsActiveWidget()) {
+ if (elm && elm->IsElement() && aAccessible->IsActiveWidget()) {
nsAutoString id;
- if (elm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
+ if (elm->AsElement()->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::aria_activedescendant,
+ id)) {
dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
if (activeDescendantElm) {
Accessible* activeDescendant = GetAccessible(activeDescendantElm);
if (activeDescendant) {
FocusMgr()->ActiveItemChanged(activeDescendant, false);
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))
logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
@@ -1246,36 +1245,24 @@ DocAccessible::GetAccessibleByUniqueIDIn
}
Accessible*
DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const
{
if (!aNode || !aNode->GetComposedDoc())
return nullptr;
- nsINode* currNode = aNode;
- Accessible* accessible = nullptr;
- while (!(accessible = GetAccessible(currNode))) {
- nsINode* parent = nullptr;
-
- // If this is a content node, try to get a flattened parent content node.
- // This will smartly skip from the shadow root to the host element,
- // over parentless document fragment
- if (currNode->IsContent())
- parent = currNode->AsContent()->GetFlattenedTreeParent();
-
- // Fallback to just get parent node, in case there is no parent content
- // node. Or current node is not a content node.
- if (!parent)
- parent = currNode->GetParentNode();
-
- if (!(currNode = parent)) break;
+ for (nsINode* currNode = aNode; currNode;
+ currNode = currNode->GetFlattenedTreeParentNode()) {
+ if (Accessible* accessible = GetAccessible(currNode)) {
+ return accessible;
+ }
}
- return accessible;
+ return nullptr;
}
Accessible*
DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const
{
Accessible* acc = GetAccessible(aNode);
if (acc)
return acc;
@@ -1308,18 +1295,19 @@ DocAccessible::BindToDocument(Accessible
// Put into unique ID cache.
mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
aAccessible->SetRoleMapEntry(aRoleMapEntry);
if (aAccessible->HasOwnContent()) {
AddDependentIDsFor(aAccessible);
- nsIContent* el = aAccessible->GetContent();
- if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
+ nsIContent* content = aAccessible->GetContent();
+ if (content->IsElement() &&
+ content->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
mNotificationController->ScheduleRelocation(aAccessible);
}
}
}
void
DocAccessible::UnbindFromDocument(Accessible* aAccessible)
{
@@ -1850,16 +1838,17 @@ InsertIterator::Next()
return true;
}
}
else {
TreeWalker finder(container);
if (finder.Seek(node)) {
mChild = mWalker.Scope(node);
if (mChild) {
+ MOZ_ASSERT(!mChild->IsRelocated(), "child cannot be aria owned");
mChildBefore = finder.Prev();
return true;
}
}
}
}
return false;
@@ -2209,17 +2198,18 @@ DocAccessible::PutChildrenBack(nsTArray<
"old parent", owner, "child", child, nullptr);
#endif
// Unset relocated flag to find an insertion point for the child.
child->SetRelocated(false);
nsIContent* content = child->GetContent();
int32_t idxInParent = -1;
- Accessible* origContainer = AccessibleOrTrueContainer(content->GetParentNode());
+ Accessible* origContainer =
+ AccessibleOrTrueContainer(content->GetFlattenedTreeParentNode());
if (origContainer) {
TreeWalker walker(origContainer);
if (walker.Seek(content)) {
Accessible* prevChild = walker.Prev();
if (prevChild) {
idxInParent = prevChild->IndexInParent() + 1;
MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
origContainer = prevChild->Parent();
--- a/accessible/generic/FormControlAccessible.cpp
+++ b/accessible/generic/FormControlAccessible.cpp
@@ -35,17 +35,17 @@ ProgressMeterAccessible<Max>::NativeRole
template<int Max>
uint64_t
ProgressMeterAccessible<Max>::NativeState()
{
uint64_t state = LeafAccessible::NativeState();
// An undetermined progressbar (i.e. without a value) has a mixed state.
nsAutoString attrValue;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue);
if (attrValue.IsEmpty())
state |= states::MIXED;
return state;
}
////////////////////////////////////////////////////////////////////////////////
@@ -89,17 +89,17 @@ template<int Max>
double
ProgressMeterAccessible<Max>::MaxValue() const
{
double value = LeafAccessible::MaxValue();
if (!IsNaN(value))
return value;
nsAutoString strValue;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::max, strValue)) {
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::max, strValue)) {
nsresult result = NS_OK;
value = strValue.ToDouble(&result);
if (NS_SUCCEEDED(result))
return value;
}
return Max;
}
@@ -124,17 +124,17 @@ template<int Max>
double
ProgressMeterAccessible<Max>::CurValue() const
{
double value = LeafAccessible::CurValue();
if (!IsNaN(value))
return value;
nsAutoString attrValue;
- if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue))
+ if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue))
return UnspecifiedNaN<double>();
nsresult error = NS_OK;
value = attrValue.ToDouble(&error);
return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
}
template<int Max>
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -136,18 +136,18 @@ HyperTextAccessible::GetBoundsInFrame(ns
rv = frame->GetPointFromOffset(startContentOffset, &frameTextStartPoint);
NS_ENSURE_SUCCESS(rv, nsIntRect());
// Use the point for the end offset to calculate the width
nsPoint frameTextEndPoint;
rv = frame->GetPointFromOffset(startContentOffset + frameSubStringLength, &frameTextEndPoint);
NS_ENSURE_SUCCESS(rv, nsIntRect());
- frameScreenRect.x += std::min(frameTextStartPoint.x, frameTextEndPoint.x);
- frameScreenRect.width = mozilla::Abs(frameTextStartPoint.x - frameTextEndPoint.x);
+ frameScreenRect.SetRectX(frameScreenRect.X() + std::min(frameTextStartPoint.x, frameTextEndPoint.x),
+ mozilla::Abs(frameTextStartPoint.x - frameTextEndPoint.x));
screenRect.UnionRect(frameScreenRect, screenRect);
// Get ready to loop back for next frame continuation
startContentOffset += frameSubStringLength;
startContentOffsetInFrame = 0;
frame = frame->GetNextContinuation();
}
@@ -237,17 +237,17 @@ HyperTextAccessible::DOMPointToOffset(ns
// findNode could be null if aNodeOffset == # of child nodes, which means
// one of two things:
// 1) there are no children, and the passed-in node is not mContent -- use
// parentContent for the node to find
// 2) there are no children and the passed-in node is mContent, which means
// we're an empty nsIAccessibleText
// 3) there are children and we're at the end of the children
- findNode = aNode->GetChildAt(aNodeOffset);
+ findNode = aNode->GetChildAt_Deprecated(aNodeOffset);
if (!findNode) {
if (aNodeOffset == 0) {
if (aNode == GetNode()) {
// Case #1: this accessible has no children and thus has empty text,
// we can only be at hypertext offset 0.
return 0;
}
@@ -264,22 +264,21 @@ HyperTextAccessible::DOMPointToOffset(ns
}
}
// Get accessible for this findNode, or if that node isn't accessible, use the
// accessible for the next DOM node which has one (based on forward depth first search)
Accessible* descendant = nullptr;
if (findNode) {
nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
- if (findContent && findContent->IsHTMLElement() &&
- findContent->NodeInfo()->Equals(nsGkAtoms::br) &&
- findContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::mozeditorbogusnode,
- nsGkAtoms::_true,
- eIgnoreCase)) {
+ if (findContent && findContent->IsHTMLElement(nsGkAtoms::br) &&
+ findContent->AsElement()->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::mozeditorbogusnode,
+ nsGkAtoms::_true,
+ eIgnoreCase)) {
// This <br> is the hacky "bogus node" used when there is no text in a control
return 0;
}
descendant = mDoc->GetAccessible(findNode);
if (!descendant && findNode->IsContent()) {
Accessible* container = mDoc->GetContainerAccessible(findNode);
if (container) {
@@ -424,17 +423,17 @@ HyperTextAccessible::OffsetToDOMPoint(in
innerOffset = 1;
}
// Case of embedded object. The point is either before or after the element.
NS_ASSERTION(innerOffset == 0 || innerOffset == 1, "A wrong inner offset!");
nsINode* node = child->GetNode();
nsINode* parentNode = node->GetParentNode();
return parentNode ?
- DOMPoint(parentNode, parentNode->IndexOf(node) + innerOffset) :
+ DOMPoint(parentNode, parentNode->ComputeIndexOf(node) + innerOffset) :
DOMPoint();
}
DOMPoint
HyperTextAccessible::ClosestNotGeneratedDOMPoint(const DOMPoint& aDOMPoint,
nsIContent* aElementContent)
{
MOZ_ASSERT(aDOMPoint.node, "The node must not be null");
@@ -1173,18 +1172,18 @@ HyperTextAccessible::OffsetAtPoint(int32
nsPresContext* presContext = mDoc->PresContext();
nsPoint coordsInAppUnits =
ToAppUnits(coords, presContext->AppUnitsPerDevPixel());
nsRect frameScreenRect = hyperFrame->GetScreenRectInAppUnits();
if (!frameScreenRect.Contains(coordsInAppUnits.x, coordsInAppUnits.y))
return -1; // Not found
- nsPoint pointInHyperText(coordsInAppUnits.x - frameScreenRect.x,
- coordsInAppUnits.y - frameScreenRect.y);
+ nsPoint pointInHyperText(coordsInAppUnits.x - frameScreenRect.X(),
+ coordsInAppUnits.y - frameScreenRect.Y());
// Go through the frames to check if each one has the point.
// When one does, add up the character offsets until we have a match
// We have an point in an accessible child of this, now we need to add up the
// offsets before it to what we already have
int32_t offset = 0;
uint32_t childCount = ChildCount();
@@ -1263,17 +1262,20 @@ HyperTextAccessible::TextBounds(int32_t
bounds.UnionRect(bounds, GetBoundsInFrame(frame, offset1,
nextOffset - prevOffset));
prevOffset = nextOffset;
offset1 = 0;
}
- nsAccUtils::ConvertScreenCoordsTo(&bounds.x, &bounds.y, aCoordType, this);
+ auto boundsX = bounds.X();
+ auto boundsY = bounds.Y();
+ nsAccUtils::ConvertScreenCoordsTo(&boundsX, &boundsY, aCoordType, this);
+ bounds.MoveTo(boundsX, boundsY);
return bounds;
}
already_AddRefed<TextEditor>
HyperTextAccessible::GetEditor() const
{
if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
// If we're inside an editable container, then return that container's editor
@@ -1518,18 +1520,17 @@ HyperTextAccessible::GetCaretRect(nsIWid
// Correct for character size, so that caret always matches the size of
// the character. This is important for font size transitions, and is
// necessary because the Gecko caret uses the previous character's size as
// the user moves forward in the text by character.
nsIntRect charRect = CharBounds(CaretOffset(),
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE);
if (!charRect.IsEmpty()) {
- caretRect.height -= charRect.y - caretRect.y;
- caretRect.y = charRect.y;
+ caretRect.SetTopEdge(charRect.Y());
}
return caretRect;
}
void
HyperTextAccessible::GetSelectionDOMRanges(SelectionType aSelectionType,
nsTArray<nsRange*>* aRanges)
{
@@ -1710,18 +1711,18 @@ HyperTextAccessible::ScrollSubstringToPo
nsIFrame *parentFrame = frame;
while ((parentFrame = parentFrame->GetParent())) {
nsIScrollableFrame *scrollableFrame = do_QueryFrame(parentFrame);
if (scrollableFrame) {
if (!initialScrolled) {
// Scroll substring to the given point. Turn the point into percents
// relative scrollable area to use nsCoreUtils::ScrollSubstringTo.
nsRect frameRect = parentFrame->GetScreenRectInAppUnits();
- nscoord offsetPointX = coordsInAppUnits.x - frameRect.x;
- nscoord offsetPointY = coordsInAppUnits.y - frameRect.y;
+ nscoord offsetPointX = coordsInAppUnits.x - frameRect.X();
+ nscoord offsetPointY = coordsInAppUnits.y - frameRect.Y();
nsSize size(parentFrame->GetSize());
// avoid divide by zero
size.width = size.width ? size.width : 1;
size.height = size.height ? size.height : 1;
int16_t hPercent = offsetPointX * 100 / size.width;
@@ -1847,30 +1848,31 @@ HyperTextAccessible::RangeAtPoint(int32_
// Accessible protected
ENameValueFlag
HyperTextAccessible::NativeName(nsString& aName)
{
// Check @alt attribute for invalid img elements.
bool hasImgAlt = false;
if (mContent->IsHTMLElement(nsGkAtoms::img)) {
- hasImgAlt = mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
+ hasImgAlt =
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
if (!aName.IsEmpty())
return eNameOK;
}
ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
if (!aName.IsEmpty())
return nameFlag;
// Get name from title attribute for HTML abbr and acronym elements making it
// a valid name from markup. Otherwise their name isn't picked up by recursive
// name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
if (IsAbbreviation() &&
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName))
aName.CompressWhitespace();
return hasImgAlt ? eNoNameOnPurpose : eNameOK;
}
void
HyperTextAccessible::Shutdown()
{
@@ -2074,17 +2076,17 @@ HyperTextAccessible::GetDOMPointByFrameO
NS_ASSERTION(!aAccessible->IsDoc(),
"Shouldn't be called on document accessible!");
nsIContent* content = aAccessible->GetContent();
NS_ASSERTION(content, "Shouldn't operate on defunct accessible!");
nsIContent* parent = content->GetParent();
- aPoint->idx = parent->IndexOf(content) + 1;
+ aPoint->idx = parent->ComputeIndexOf(content) + 1;
aPoint->node = parent;
} else if (aFrame->IsTextFrame()) {
nsIContent* content = aFrame->GetContent();
NS_ENSURE_STATE(content);
nsIFrame *primaryFrame = content->GetPrimaryFrame();
nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, &(aPoint->idx));
@@ -2094,17 +2096,17 @@ HyperTextAccessible::GetDOMPointByFrameO
} else {
nsIContent* content = aFrame->GetContent();
NS_ENSURE_STATE(content);
nsIContent* parent = content->GetParent();
NS_ENSURE_STATE(parent);
- aPoint->idx = parent->IndexOf(content);
+ aPoint->idx = parent->ComputeIndexOf(content);
aPoint->node = parent;
}
return NS_OK;
}
// HyperTextAccessible
void
--- a/accessible/generic/ImageAccessible.cpp
+++ b/accessible/generic/ImageAccessible.cpp
@@ -12,17 +12,16 @@
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "nsGenericHTMLElement.h"
#include "nsIDocument.h"
#include "nsIImageLoadingContent.h"
#include "nsIPresShell.h"
#include "nsIServiceManager.h"
-#include "nsIDOMHTMLImageElement.h"
#include "nsIPersistentProperties2.h"
#include "nsPIDOMWindow.h"
#include "nsIURI.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// ImageAccessible
@@ -70,17 +69,17 @@ ImageAccessible::NativeState()
return state;
}
ENameValueFlag
ImageAccessible::NativeName(nsString& aName)
{
bool hasAltAttrib =
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName);
if (!aName.IsEmpty())
return eNameOK;
ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty())
return nameFlag;
// No accessible name but empty 'alt' attribute is present. If further name
@@ -144,52 +143,52 @@ ImageAccessible::DoAction(uint8_t aIndex
}
////////////////////////////////////////////////////////////////////////////////
// ImageAccessible
nsIntPoint
ImageAccessible::Position(uint32_t aCoordType)
{
- nsIntRect rect = Bounds();
- nsAccUtils::ConvertScreenCoordsTo(&rect.x, &rect.y, aCoordType, this);
- return rect.TopLeft();
+ nsIntPoint point = Bounds().TopLeft();
+ nsAccUtils::ConvertScreenCoordsTo(&point.x, &point.y, aCoordType, this);
+ return point;
}
nsIntSize
ImageAccessible::Size()
{
return Bounds().Size();
}
// Accessible
already_AddRefed<nsIPersistentProperties>
ImageAccessible::NativeAttributes()
{
nsCOMPtr<nsIPersistentProperties> attributes =
LinkableAccessible::NativeAttributes();
nsAutoString src;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
if (!src.IsEmpty())
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::src, src);
return attributes.forget();
}
////////////////////////////////////////////////////////////////////////////////
// Private methods
already_AddRefed<nsIURI>
ImageAccessible::GetLongDescURI() const
{
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::longdesc)) {
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::longdesc)) {
// To check if longdesc contains an invalid url.
nsAutoString longdesc;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::longdesc, longdesc);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::longdesc, longdesc);
if (longdesc.FindChar(' ') != -1 || longdesc.FindChar('\t') != -1 ||
longdesc.FindChar('\r') != -1 || longdesc.FindChar('\n') != -1) {
return nullptr;
}
nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
nsCOMPtr<nsIURI> uri;
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), longdesc,
mContent->OwnerDoc(), baseURI);
@@ -197,17 +196,17 @@ ImageAccessible::GetLongDescURI() const
}
DocAccessible* document = Document();
if (document) {
IDRefsIterator iter(document, mContent, nsGkAtoms::aria_describedby);
while (nsIContent* target = iter.NextElem()) {
if ((target->IsHTMLElement(nsGkAtoms::a) ||
target->IsHTMLElement(nsGkAtoms::area)) &&
- target->HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
+ target->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
nsGenericHTMLElement* element =
nsGenericHTMLElement::FromContent(target);
nsCOMPtr<nsIURI> uri;
element->GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
return uri.forget();
}
}
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -65,18 +65,17 @@ OuterDocAccessible::NativeRole()
return roles::INTERNAL_FRAME;
}
Accessible*
OuterDocAccessible::ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild)
{
nsIntRect docRect = Bounds();
- if (aX < docRect.x || aX >= docRect.x + docRect.width ||
- aY < docRect.y || aY >= docRect.y + docRect.height)
+ if (!docRect.Contains(aX, aY))
return nullptr;
// Always return the inner doc as direct child accessible unless bounds
// outside of it.
Accessible* child = GetChildAt(0);
NS_ENSURE_TRUE(child, nullptr);
if (aWhichChild == eDeepestChild)
--- a/accessible/generic/RootAccessible.cpp
+++ b/accessible/generic/RootAccessible.cpp
@@ -480,17 +480,18 @@ RootAccessible::Shutdown()
Relation
RootAccessible::RelationByType(RelationType aType)
{
if (!mDocumentNode || aType != RelationType::EMBEDS)
return DocAccessibleWrap::RelationByType(aType);
if (nsPIDOMWindowOuter* rootWindow = mDocumentNode->GetWindow()) {
- nsCOMPtr<nsPIDOMWindowOuter> contentWindow = nsGlobalWindow::Cast(rootWindow)->GetContent();
+ nsCOMPtr<nsPIDOMWindowOuter> contentWindow =
+ nsGlobalWindowOuter::Cast(rootWindow)->GetContent();
if (contentWindow) {
nsCOMPtr<nsIDocument> contentDocumentNode = contentWindow->GetDoc();
if (contentDocumentNode) {
DocAccessible* contentDocument =
GetAccService()->GetDocAccessible(contentDocumentNode);
if (contentDocument)
return Relation(contentDocument);
}
--- a/accessible/generic/moz.build
+++ b/accessible/generic/moz.build
@@ -61,10 +61,10 @@ else:
LOCAL_INCLUDES += [
'/accessible/other',
]
FINAL_LIBRARY = 'xul'
include('/ipc/chromium/chromium-config.mozbuild')
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -10,17 +10,17 @@
#include "nsEventShell.h"
#include "nsTextEquivUtils.h"
#include "Relation.h"
#include "Role.h"
#include "States.h"
#include "nsContentList.h"
#include "mozilla/dom/HTMLInputElement.h"
-#include "nsIDOMHTMLTextAreaElement.h"
+#include "mozilla/dom/HTMLTextAreaElement.h"
#include "nsIEditor.h"
#include "nsIFormControl.h"
#include "nsIPersistentProperties2.h"
#include "nsISelectionController.h"
#include "nsIServiceManager.h"
#include "nsITextControlElement.h"
#include "nsITextControlFrame.h"
#include "nsNameSpaceManager.h"
@@ -126,19 +126,19 @@ void
HTMLRadioButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
int32_t* aSetSize)
{
int32_t namespaceId = mContent->NodeInfo()->NamespaceID();
nsAutoString tagName;
mContent->NodeInfo()->GetName(tagName);
nsAutoString type;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
nsAutoString name;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
RefPtr<nsContentList> inputElms;
nsCOMPtr<nsIFormControl> formControlNode(do_QueryInterface(mContent));
dom::Element* formElm = formControlNode->GetFormElement();
if (formElm)
inputElms = NS_GetContentList(formElm, namespaceId, tagName);
else
@@ -148,20 +148,21 @@ HTMLRadioButtonAccessible::GetPositionAn
uint32_t inputCount = inputElms->Length(false);
// Compute posinset and setsize.
int32_t indexOf = 0;
int32_t count = 0;
for (uint32_t index = 0; index < inputCount; index++) {
nsIContent* inputElm = inputElms->Item(index, false);
- if (inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- type, eCaseMatters) &&
- inputElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
- name, eCaseMatters) && mDoc->HasAccessible(inputElm)) {
+ if (inputElm->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ type, eCaseMatters) &&
+ inputElm->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
+ name, eCaseMatters) &&
+ mDoc->HasAccessible(inputElm)) {
count++;
if (inputElm == mContent)
indexOf = count;
}
}
*aPosInSet = indexOf;
*aSetSize = count;
@@ -247,22 +248,22 @@ HTMLButtonAccessible::NativeName(nsStrin
// element that has no valid @src (note if input@type="image" has an image
// then neither @alt nor @value attributes are used to generate a visual label
// and thus we need to obtain the accessible name directly from attribute
// value). Also the same algorithm works in case of default labels for
// type="submit"/"reset"/"image" elements.
ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty() || !mContent->IsHTMLElement(nsGkAtoms::input) ||
- !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::image, eCaseMatters))
+ !mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::image, eCaseMatters))
return nameFlag;
- if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
+ if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
aName.CompressWhitespace();
return eNameOK;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLButtonAccessible: Widgets
@@ -285,34 +286,34 @@ HTMLTextFieldAccessible::
}
NS_IMPL_ISUPPORTS_INHERITED0(HTMLTextFieldAccessible,
HyperTextAccessible)
role
HTMLTextFieldAccessible::NativeRole()
{
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::password, eIgnoreCase)) {
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::password, eIgnoreCase)) {
return roles::PASSWORD_TEXT;
}
return roles::ENTRY;
}
already_AddRefed<nsIPersistentProperties>
HTMLTextFieldAccessible::NativeAttributes()
{
nsCOMPtr<nsIPersistentProperties> attributes =
HyperTextAccessibleWrap::NativeAttributes();
// Expose type for text input elements as it gives some useful context,
// especially for mobile.
nsAutoString type;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType, type);
if (!ARIARoleMap() && type.EqualsLiteral("search")) {
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles,
NS_LITERAL_STRING("searchbox"));
}
}
return attributes.forget();
@@ -329,28 +330,28 @@ HTMLTextFieldAccessible::NativeName(nsSt
nsIContent* widgetElm = XULWidgetElm();
if (widgetElm)
XULElmName(mDoc, widgetElm, aName);
if (!aName.IsEmpty())
return eNameOK;
// text inputs and textareas might have useful placeholder text
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName);
return eNameOK;
}
void
HTMLTextFieldAccessible::Value(nsString& aValue)
{
aValue.Truncate();
if (NativeState() & states::PROTECTED) // Don't return password text!
return;
- nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent));
+ HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromContent(mContent);
if (textArea) {
textArea->GetValue(aValue);
return;
}
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
if (input) {
// Pass NonSystem as the caller type, to be safe. We don't expect to have a
@@ -377,22 +378,22 @@ HTMLTextFieldAccessible::NativeState()
{
uint64_t state = HyperTextAccessibleWrap::NativeState();
// Text fields are always editable, even if they are also read only or
// disabled.
state |= states::EDITABLE;
// can be focusable, focused, protected. readonly, unavailable, selected
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
- nsGkAtoms::password, eIgnoreCase)) {
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+ nsGkAtoms::password, eIgnoreCase)) {
state |= states::PROTECTED;
}
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
state |= states::READONLY;
}
// Is it an <input> or a <textarea> ?
HTMLInputElement* input = HTMLInputElement::FromContent(mContent);
state |= input && input->IsSingleLineTextControl() ?
states::SINGLE_LINE : states::MULTI_LINE;
@@ -403,38 +404,38 @@ HTMLTextFieldAccessible::NativeState()
// Expose autocomplete states if this input is part of autocomplete widget.
Accessible* widget = ContainerWidget();
if (widget && widget-IsAutoComplete()) {
state |= states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION;
return state;
}
// Expose autocomplete state if it has associated autocomplete list.
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::list))
return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP;
// Ordinal XUL textboxes don't support autocomplete.
if (!XULWidgetElm() && Preferences::GetBool("browser.formfill.enable")) {
// Check to see if autocompletion is allowed on this input. We don't expose
// it for password fields even though the entire password can be remembered
// for a page if the user asks it to be. However, the kind of autocomplete
// we're talking here is based on what the user types, where a popup of
// possible choices comes up.
nsAutoString autocomplete;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
- autocomplete);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete,
+ autocomplete);
if (!autocomplete.LowerCaseEqualsLiteral("off")) {
- nsIContent* formContent = input->GetFormElement();
- if (formContent) {
- formContent->GetAttr(kNameSpaceID_None,
+ Element* formElement = input->GetFormElement();
+ if (formElement) {
+ formElement->GetAttr(kNameSpaceID_None,
nsGkAtoms::autocomplete, autocomplete);
}
- if (!formContent || !autocomplete.LowerCaseEqualsLiteral("off"))
+ if (!formElement || !autocomplete.LowerCaseEqualsLiteral("off"))
state |= states::SUPPORTS_AUTOCOMPLETION;
}
}
return state;
}
uint8_t
--- a/accessible/html/HTMLImageMapAccessible.cpp
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -5,17 +5,16 @@
#include "HTMLImageMapAccessible.h"
#include "ARIAMap.h"
#include "nsAccUtils.h"
#include "DocAccessible-inl.h"
#include "Role.h"
-#include "nsIDOMHTMLCollection.h"
#include "nsIServiceManager.h"
#include "nsIDOMElement.h"
#include "nsIFrame.h"
#include "nsImageFrame.h"
#include "nsImageMap.h"
#include "nsIURI.h"
#include "mozilla/dom/HTMLAreaElement.h"
@@ -151,29 +150,30 @@ HTMLAreaAccessible::
ENameValueFlag
HTMLAreaAccessible::NativeName(nsString& aName)
{
ENameValueFlag nameFlag = Accessible::NativeName(aName);
if (!aName.IsEmpty())
return nameFlag;
- if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
+ if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt, aName))
Value(aName);
return eNameOK;
}
void
HTMLAreaAccessible::Description(nsString& aDescription)
{
aDescription.Truncate();
// Still to do - follow IE's standard here
- RefPtr<HTMLAreaElement> area = HTMLAreaElement::FromContentOrNull(mContent);
+ RefPtr<dom::HTMLAreaElement> area =
+ dom::HTMLAreaElement::FromContentOrNull(mContent);
if (area)
area->GetShape(aDescription);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLAreaAccessible: Accessible public
Accessible*
@@ -217,12 +217,11 @@ HTMLAreaAccessible::RelativeBounds(nsIFr
nsRect bounds;
nsresult rv = map->GetBoundsForAreaContent(mContent, bounds);
if (NS_FAILED(rv))
return nsRect();
// XXX Areas are screwy; they return their rects as a pair of points, one pair
// stored into the width and height.
*aBoundingFrame = frame;
- bounds.width -= bounds.x;
- bounds.height -= bounds.y;
+ bounds.SizeTo(bounds.Width() - bounds.X(), bounds.Height() - bounds.Y());
return bounds;
}
--- a/accessible/html/HTMLLinkAccessible.cpp
+++ b/accessible/html/HTMLLinkAccessible.cpp
@@ -63,17 +63,17 @@ HTMLLinkAccessible::NativeLinkState() co
uint64_t
HTMLLinkAccessible::NativeInteractiveState() const
{
uint64_t state = HyperTextAccessibleWrap::NativeInteractiveState();
// This is how we indicate it is a named anchor. In other words, this anchor
// can be selected as a location :) There is no other better state to use to
// indicate this.
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::name))
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::name))
state |= states::SELECTABLE;
return state;
}
void
HTMLLinkAccessible::Value(nsString& aValue)
{
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -82,18 +82,18 @@ nsIntRect
HTMLLIAccessible::Bounds() const
{
nsIntRect rect = AccessibleWrap::Bounds();
if (rect.IsEmpty() || !mBullet || mBullet->IsInside())
return rect;
nsIntRect bulletRect = mBullet->Bounds();
- rect.width += rect.x - bulletRect.x;
- rect.x = bulletRect.x; // Move x coordinate of list item over to cover bullet as well
+ // Move x coordinate of list item over to cover bullet as well
+ rect.SetLeftEdge(bulletRect.X());
return rect;
}
bool
HTMLLIAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
{
// Adjust index if there's a bullet.
if (mBullet && aIndex == 0 && aChild != mBullet) {
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -37,17 +37,17 @@ HTMLSelectListAccessible::
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectListAccessible: Accessible public
uint64_t
HTMLSelectListAccessible::NativeState()
{
uint64_t state = AccessibleWrap::NativeState();
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple))
state |= states::MULTISELECTABLE | states::EXTSELECTABLE;
return state;
}
role
HTMLSelectListAccessible::NativeRole()
{
@@ -55,24 +55,24 @@ HTMLSelectListAccessible::NativeRole()
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectListAccessible: SelectAccessible
bool
HTMLSelectListAccessible::SelectAll()
{
- return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
+ return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
AccessibleWrap::SelectAll() : false;
}
bool
HTMLSelectListAccessible::UnselectAll()
{
- return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
+ return mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
AccessibleWrap::UnselectAll() : false;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectListAccessible: Widgets
bool
HTMLSelectListAccessible::IsWidget() const
@@ -105,19 +105,23 @@ HTMLSelectListAccessible::CurrentItem()
}
}
return nullptr;
}
void
HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem)
{
- aItem->GetContent()->SetAttr(kNameSpaceID_None,
- nsGkAtoms::selected, NS_LITERAL_STRING("true"),
- true);
+ if (!aItem->GetContent()->IsElement())
+ return;
+
+ aItem->GetContent()->AsElement()->SetAttr(kNameSpaceID_None,
+ nsGkAtoms::selected,
+ NS_LITERAL_STRING("true"),
+ true);
}
bool
HTMLSelectListAccessible::IsAcceptableChild(nsIContent* aEl) const
{
return aEl->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup);
}
@@ -143,17 +147,17 @@ HTMLSelectOptionAccessible::NativeRole()
return roles::OPTION;
}
ENameValueFlag
HTMLSelectOptionAccessible::NativeName(nsString& aName)
{
// CASE #1 -- great majority of the cases
// find the label attribute - this is what the W3C says we should use
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
if (!aName.IsEmpty())
return eNameOK;
// CASE #2 -- no label parameter, get the first child,
// use it if it is a text node
nsIContent* text = mContent->GetFirstChild();
if (text && text->IsNodeOfType(nsINode::eTEXT)) {
nsTextEquivUtils::AppendTextEquivFromTextContent(text, &aName);
@@ -204,18 +208,18 @@ HTMLSelectOptionAccessible::NativeState(
// XXX list frames are weird, don't rely on Accessible's general
// visibility implementation unless they get reimplemented in layout
state &= ~states::OFFSCREEN;
// <select> is not collapsed: compare bounds to calculate OFFSCREEN
Accessible* listAcc = Parent();
if (listAcc) {
nsIntRect optionRect = Bounds();
nsIntRect listRect = listAcc->Bounds();
- if (optionRect.y < listRect.y ||
- optionRect.y + optionRect.height > listRect.y + listRect.height) {
+ if (optionRect.Y() < listRect.Y() ||
+ optionRect.YMost() > listRect.YMost()) {
state |= states::OFFSCREEN;
}
}
}
return state;
}
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -17,17 +17,17 @@
#include "States.h"
#include "TreeWalker.h"
#include "mozilla/dom/HTMLTableElement.h"
#include "nsIDOMElement.h"
#include "nsIDOMRange.h"
#include "nsISelectionPrivate.h"
#include "nsIDOMNodeList.h"
-#include "nsIDOMHTMLCollection.h"
+#include "nsIHTMLCollection.h"
#include "nsIDocument.h"
#include "nsIMutableArray.h"
#include "nsIPersistentProperties2.h"
#include "nsIPresShell.h"
#include "nsITableCellLayout.h"
#include "nsFrameSelection.h"
#include "nsError.h"
#include "nsArrayUtils.h"
@@ -117,24 +117,24 @@ HTMLTableCellAccessible::NativeAttribute
nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild();
if (firstChildNode) {
nsTextEquivUtils::
AppendTextEquivFromTextContent(firstChildNode, &abbrText);
}
}
}
if (abbrText.IsEmpty())
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText);
if (!abbrText.IsEmpty())
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText);
// axis attribute
nsAutoString axisText;
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
if (!axisText.IsEmpty())
nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText);
#ifdef DEBUG
nsAutoString unused;
attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"),
NS_LITERAL_STRING("HTMLTableCellAccessible"),
unused);
@@ -170,33 +170,27 @@ HTMLTableCellAccessible::Table() const
}
return nullptr;
}
uint32_t
HTMLTableCellAccessible::ColIdx() const
{
- nsITableCellLayout* cellLayout = GetCellLayout();
- NS_ENSURE_TRUE(cellLayout, 0);
-
- int32_t colIdx = 0;
- cellLayout->GetColIndex(colIdx);
- return colIdx > 0 ? static_cast<uint32_t>(colIdx) : 0;
+ nsTableCellFrame* cellFrame = GetCellFrame();
+ NS_ENSURE_TRUE(cellFrame, 0);
+ return cellFrame->ColIndex();
}
uint32_t
HTMLTableCellAccessible::RowIdx() const
{
- nsITableCellLayout* cellLayout = GetCellLayout();
- NS_ENSURE_TRUE(cellLayout, 0);
-
- int32_t rowIdx = 0;
- cellLayout->GetRowIndex(rowIdx);
- return rowIdx > 0 ? static_cast<uint32_t>(rowIdx) : 0;
+ nsTableCellFrame* cellFrame = GetCellFrame();
+ NS_ENSURE_TRUE(cellFrame, 0);
+ return cellFrame->RowIndex();
}
uint32_t
HTMLTableCellAccessible::ColExtent() const
{
int32_t rowIdx = -1, colIdx = -1;
GetCellIndexes(rowIdx, colIdx);
@@ -280,16 +274,22 @@ HTMLTableCellAccessible::Selected()
// HTMLTableCellAccessible: protected implementation
nsITableCellLayout*
HTMLTableCellAccessible::GetCellLayout() const
{
return do_QueryFrame(mContent->GetPrimaryFrame());
}
+nsTableCellFrame*
+HTMLTableCellAccessible::GetCellFrame() const
+{
+ return do_QueryFrame(mContent->GetPrimaryFrame());
+}
+
nsresult
HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const
{
nsITableCellLayout *cellLayout = GetCellLayout();
NS_ENSURE_STATE(cellLayout);
return cellLayout->GetCellIndexes(aRowIdx, aColIdx);
}
@@ -307,22 +307,22 @@ HTMLTableHeaderCellAccessible::
////////////////////////////////////////////////////////////////////////////////
// HTMLTableHeaderCellAccessible: Accessible implementation
role
HTMLTableHeaderCellAccessible::NativeRole()
{
// Check value of @scope attribute.
- static nsIContent::AttrValuesArray scopeValues[] =
+ static Element::AttrValuesArray scopeValues[] =
{ &nsGkAtoms::col, &nsGkAtoms::colgroup,
&nsGkAtoms::row, &nsGkAtoms::rowgroup, nullptr };
int32_t valueIdx =
- mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope,
- scopeValues, eCaseMatters);
+ mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope,
+ scopeValues, eCaseMatters);
switch (valueIdx) {
case 0:
case 1:
return roles::COLUMNHEADER;
case 2:
case 3:
return roles::ROWHEADER;
@@ -432,17 +432,17 @@ HTMLTableAccessible::NativeName(nsString
if (captionContent) {
nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
if (!aName.IsEmpty())
return eNameOK;
}
}
// If no caption then use summary as a name.
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
return eNameOK;
}
already_AddRefed<nsIPersistentProperties>
HTMLTableAccessible::NativeAttributes()
{
nsCOMPtr<nsIPersistentProperties> attributes =
AccessibleWrap::NativeAttributes();
@@ -515,21 +515,19 @@ HTMLTableAccessible::SelectedCellCount()
uint32_t count = 0, rowCount = RowCount(), colCount = ColCount();
for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
if (!cellFrame || !cellFrame->IsSelected())
continue;
- int32_t startRow = -1, startCol = -1;
- cellFrame->GetRowIndex(startRow);
- cellFrame->GetColIndex(startCol);
- if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
- startCol >= 0 && (uint32_t)startCol == colIdx)
+ uint32_t startRow = cellFrame->RowIndex();
+ uint32_t startCol = cellFrame->ColIndex();
+ if (startRow == rowIdx && startCol == colIdx)
count++;
}
}
return count;
}
uint32_t
@@ -565,21 +563,19 @@ HTMLTableAccessible::SelectedCells(nsTAr
uint32_t rowCount = RowCount(), colCount = ColCount();
for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
if (!cellFrame || !cellFrame->IsSelected())
continue;
- int32_t startCol = -1, startRow = -1;
- cellFrame->GetRowIndex(startRow);
- cellFrame->GetColIndex(startCol);
- if ((startRow >= 0 && (uint32_t)startRow != rowIdx) ||
- (startCol >= 0 && (uint32_t)startCol != colIdx))
+ uint32_t startRow = cellFrame->RowIndex();
+ uint32_t startCol = cellFrame->ColIndex();
+ if (startRow != rowIdx || startCol != colIdx)
continue;
Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
aCells->AppendElement(cell);
}
}
}
@@ -592,21 +588,19 @@ HTMLTableAccessible::SelectedCellIndices
uint32_t rowCount = RowCount(), colCount = ColCount();
for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx);
if (!cellFrame || !cellFrame->IsSelected())
continue;
- int32_t startRow = -1, startCol = -1;
- cellFrame->GetColIndex(startCol);
- cellFrame->GetRowIndex(startRow);
- if (startRow >= 0 && (uint32_t)startRow == rowIdx &&
- startCol >= 0 && (uint32_t)startCol == colIdx)
+ uint32_t startCol = cellFrame->ColIndex();
+ uint32_t startRow = cellFrame->RowIndex();
+ if (startRow == rowIdx && startCol == colIdx)
aCells->AppendElement(CellIndexAt(rowIdx, colIdx));
}
}
}
void
HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
{
@@ -864,18 +858,18 @@ HTMLTableAccessible::Description(nsStrin
if (caption) {
nsIContent* captionContent = caption->GetContent();
if (captionContent) {
nsAutoString captionText;
nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
&captionText);
if (!captionText.IsEmpty()) { // summary isn't used as a name.
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
- aDescription);
+ mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
+ aDescription);
}
}
}
#ifdef SHOW_LAYOUT_HEURISTIC
if (aDescription.IsEmpty()) {
bool isProbablyForLayout = IsProbablyLayoutTable();
aDescription = mLayoutHeuristic;
@@ -947,35 +941,35 @@ HTMLTableAccessible::IsProbablyLayoutTab
}
}
// Check to see if an ARIA role overrides the role from native markup,
// but for which we still expose table semantics (treegrid, for example).
if (Role() != roles::TABLE)
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
+ if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
// Role attribute is present, but overridden roles have already been dealt with.
// Only landmarks and other roles that don't override the role from native
// markup are left to deal with here.
RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
}
NS_ASSERTION(mContent->IsHTMLElement(nsGkAtoms::table),
"table should not be built by CSS display:table style");
// Check if datatable attribute has "0" value.
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
+ if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable,
NS_LITERAL_STRING("0"), eCaseMatters)) {
RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
}
// Check for legitimate data table attributes.
nsAutoString summary;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
+ if (mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
!summary.IsEmpty())
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
// Check for legitimate data table elements.
Accessible* caption = FirstChild();
if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
@@ -1000,19 +994,19 @@ HTMLTableAccessible::IsProbablyLayoutTab
cellElm = cellElm->GetNextSibling()) {
if (cellElm->IsHTMLElement()) {
if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
RETURN_LAYOUT_ANSWER(false,
"Has th -- legitimate table structures");
}
- if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
- cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
- cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
+ if (cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) ||
+ cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) ||
+ cellElm->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) {
RETURN_LAYOUT_ANSWER(false,
"Has headers, scope, or abbr attribute -- legitimate table structures");
}
Accessible* cell = mDoc->GetAccessible(cellElm);
if (cell && cell->ChildCount() == 1 &&
cell->FirstChild()->IsAbbreviation()) {
RETURN_LAYOUT_ANSWER(false,
--- a/accessible/html/HTMLTableAccessible.h
+++ b/accessible/html/HTMLTableAccessible.h
@@ -6,16 +6,17 @@
#ifndef mozilla_a11y_HTMLTableAccessible_h__
#define mozilla_a11y_HTMLTableAccessible_h__
#include "HyperTextAccessibleWrap.h"
#include "TableAccessible.h"
#include "TableCellAccessible.h"
class nsITableCellLayout;
+class nsTableCellFrame;
namespace mozilla {
namespace a11y {
/**
* HTML table cell accessible (html:td).
*/
class HTMLTableCellAccessible : public HyperTextAccessibleWrap,
@@ -49,16 +50,21 @@ protected:
virtual ~HTMLTableCellAccessible() {}
/**
* Return nsITableCellLayout of the table cell frame.
*/
nsITableCellLayout* GetCellLayout() const;
/**
+ * Return the table cell frame.
+ */
+ nsTableCellFrame* GetCellFrame() const;
+
+ /**
* Return row and column indices of the cell.
*/
nsresult GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const;
};
/**
* HTML table row/column header accessible (html:th or html:td@scope).
--- a/accessible/html/moz.build
+++ b/accessible/html/moz.build
@@ -41,10 +41,10 @@ else:
LOCAL_INCLUDES += [
'/accessible/other',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/interfaces/gecko/moz.build
+++ b/accessible/interfaces/gecko/moz.build
@@ -20,10 +20,10 @@ GENERATED_FILES += [
FINAL_LIBRARY = 'xul'
# The Windows MIDL code generator creates things like:
#
# #endif !_MIDL_USE_GUIDDEF_
#
# which clang-cl complains about. MSVC doesn't, so turn this warning off.
-if CONFIG['CLANG_CL']:
+if CONFIG['CC_TYPE'] == 'clang-cl':
CFLAGS += ['-Wno-extra-tokens']
--- a/accessible/interfaces/ia2/moz.build
+++ b/accessible/interfaces/ia2/moz.build
@@ -86,10 +86,10 @@ GENERATED_FILES += [
RCINCLUDE = 'IA2Marshal.rc'
# The Windows MIDL code generator creates things like:
#
# #endif !_MIDL_USE_GUIDDEF_
#
# which clang-cl complains about. MSVC doesn't, so turn this warning off.
-if CONFIG['CLANG_CL']:
+if CONFIG['CC_TYPE'] == 'clang-cl':
CXXFLAGS += ['-Wno-extra-tokens']
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -36,10 +36,10 @@ GENERATED_FILES += [
RCINCLUDE = 'AccessibleMarshal.rc'
# The Windows MIDL code generator creates things like:
#
# #endif !_MIDL_USE_GUIDDEF_
#
# which clang-cl complains about. MSVC doesn't, so turn this warning off.
-if CONFIG['CLANG_CL']:
+if CONFIG['CC_TYPE'] == 'clang-cl':
CFLAGS += ['-Wno-extra-tokens']
--- a/accessible/interfaces/nsIAccessibilityService.idl
+++ b/accessible/interfaces/nsIAccessibilityService.idl
@@ -11,17 +11,17 @@ interface nsIWeakReference;
interface nsIPresShell;
interface nsIAccessiblePivot;
/**
* An interface for in-process accessibility clients wishing to get an
* nsIAccessible for a given DOM node. More documentation at:
* http://www.mozilla.org/projects/ui/accessibility
*/
-[scriptable, builtinclass, uuid(9a6f80fe-25cc-405c-9f8f-25869bc9f94e)]
+[scriptable, builtinclass, uuid(2188e3a0-c88e-11e7-8f1a-0800200c9a66)]
interface nsIAccessibilityService : nsISupports
{
/**
* Return application accessible.
*/
nsIAccessible getApplicationAccessible();
/**
@@ -92,9 +92,15 @@ interface nsIAccessibilityService : nsIS
* @see Logging.cpp for list of possible values.
*/
void setLogging(in ACString aModules);
/**
* Return true if the given module is logged.
*/
boolean isLogged(in AString aModule);
+
+ /**
+ * Get the current accessibility service consumers.
+ * @returns a JSON string representing the accessibility service consumers.
+ */
+ AString getConsumers();
};
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -2,17 +2,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIArray.idl"
interface nsIPersistentProperties;
-interface nsIDOMCSSPrimitiveValue;
interface nsIDOMNode;
interface nsIAccessibleDocument;
interface nsIAccessibleRelation;
%{C++
namespace mozilla {
namespace a11y {
class Accessible;
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -618,18 +618,18 @@ DocAccessibleParent::MaybeInitWindowEmul
RootAccessible* rootDocument = outerDoc->RootAccessible();
MOZ_ASSERT(rootDocument);
bool isActive = true;
nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
if (Compatibility::IsDolphin()) {
rect = Bounds();
nsIntRect rootRect = rootDocument->Bounds();
- rect.x = rootRect.x - rect.x;
- rect.y -= rootRect.y;
+ rect.MoveToX(rootRect.X() - rect.X());
+ rect.MoveToY(rect.Y() - rootRect.Y());
auto tab = static_cast<dom::TabParent*>(Manager());
tab->GetDocShellIsActive(&isActive);
}
nsWinUtils::NativeWindowCreateProc onCreate([this](HWND aHwnd) -> void {
IDispatchHolder hWndAccHolder;
@@ -647,18 +647,18 @@ DocAccessibleParent::MaybeInitWindowEmul
Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
hWndAccHolder);
});
HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
DebugOnly<HWND> hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
parentWnd,
- rect.x, rect.y,
- rect.width, rect.height,
+ rect.X(), rect.Y(),
+ rect.Width(), rect.Height(),
isActive, &onCreate);
MOZ_ASSERT(hWnd);
}
/**
* @param aCOMProxy COM Proxy to the document in the content process.
*/
void
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -24,27 +24,28 @@ class xpcAccessibleGeneric;
* an accessible document in a content process.
*/
class DocAccessibleParent : public ProxyAccessible,
public PDocAccessibleParent
{
public:
DocAccessibleParent() :
ProxyAccessible(this), mParentDoc(kNoParentDoc),
+#if defined(XP_WIN)
+ mEmulatedWindowHandle(nullptr),
+#endif // defined(XP_WIN)
mTopLevel(false), mShutdown(false)
-#if defined(XP_WIN)
- , mEmulatedWindowHandle(nullptr)
-#endif // defined(XP_WIN)
{
MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
sMaxDocID++;
mActorID = sMaxDocID;
MOZ_ASSERT(!LiveDocs().Get(mActorID));
LiveDocs().Put(mActorID, this);
}
+
~DocAccessibleParent()
{
LiveDocs().Remove(mActorID);
MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
MOZ_ASSERT(mChildDocs.Length() == 0);
MOZ_ASSERT(!ParentDoc());
}
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -28,17 +28,17 @@ else:
LOCAL_INCLUDES += [
'/accessible/other',
]
EXPORTS.mozilla.a11y += [
'IPCTypes.h',
]
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.a11y += [
'DocAccessibleChildBase.h',
'DocAccessibleParent.h',
'ProxyAccessibleBase.h',
'ProxyAccessibleShared.h',
--- a/accessible/ipc/other/moz.build
+++ b/accessible/ipc/other/moz.build
@@ -35,13 +35,13 @@ if CONFIG['ACCESSIBILITY']:
]
else:
LOCAL_INCLUDES += [
'/accessible/other',
]
include('/ipc/chromium/chromium-config.mozbuild')
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
FINAL_LIBRARY = 'xul'
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -14,18 +14,18 @@
namespace mozilla {
namespace a11y {
static StaticAutoPtr<PlatformChild> sPlatformChild;
DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
: DocAccessibleChildBase(aDoc)
+ , mIsRemoteConstructed(false)
, mEmulatedWindowHandle(nullptr)
- , mIsRemoteConstructed(false)
{
MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
if (!sPlatformChild) {
sPlatformChild = new PlatformChild();
ClearOnShutdown(&sPlatformChild, ShutdownPhase::Shutdown);
}
SetManager(aManager);
@@ -313,9 +313,8 @@ ipc::IPCResult
DocAccessibleChild::RecvRestoreFocus()
{
FocusMgr()->ForceFocusEvent();
return IPC_OK();
}
} // namespace a11y
} // namespace mozilla
-
--- a/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -4,29 +4,37 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define INITGUID
#include "mozilla/a11y/HandlerProvider.h"
#include "Accessible2_3.h"
+#include "AccessibleDocument.h"
+#include "AccessibleTable.h"
+#include "AccessibleTable2.h"
+#include "AccessibleTableCell.h"
#include "HandlerData.h"
#include "HandlerData_i.c"
#include "mozilla/Assertions.h"
#include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/a11y/HandlerDataCleanup.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Move.h"
#include "mozilla/mscom/AgileReference.h"
#include "mozilla/mscom/FastMarshaler.h"
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/MainThreadHandoff.h"
#include "mozilla/mscom/MainThreadInvoker.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/StructStream.h"
#include "mozilla/mscom/Utils.h"
#include "nsThreadUtils.h"
+#include "nsTArray.h"
#include <memory.h>
namespace mozilla {
namespace a11y {
HandlerProvider::HandlerProvider(REFIID aIid,
mscom::InterceptorTargetPtr<IUnknown> aTarget)
@@ -88,106 +96,346 @@ HandlerProvider::GetHandler(NotNull<CLSI
return E_NOINTERFACE;
}
*aHandlerClsid = CLSID_AccessibleHandler;
return S_OK;
}
void
-HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
+HandlerProvider::GetAndSerializePayload(const MutexAutoLock&,
+ NotNull<mscom::IInterceptor*> aInterceptor)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
if (mSerializer) {
return;
}
IA2Payload payload{};
- if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
- this, &HandlerProvider::BuildIA2Data,
- &payload.mData) ||
- !payload.mData.mUniqueId) {
+ if (!mscom::InvokeOnMainThread("HandlerProvider::BuildInitialIA2Data",
+ this, &HandlerProvider::BuildInitialIA2Data,
+ aInterceptor,
+ &payload.mStaticData, &payload.mDynamicData) ||
+ !payload.mDynamicData.mUniqueId) {
return;
}
// But we set mGeckoBackChannel on the current thread which resides in the
// MTA. This is important to ensure that COM always invokes
// IGeckoBackChannel methods in an MTA background thread.
RefPtr<IGeckoBackChannel> payloadRef(this);
// AddRef/Release pair for this reference is handled by payloadRef
payload.mGeckoBackChannel = this;
mSerializer = MakeUnique<mscom::StructToStream>(payload, &IA2Payload_Encode);
- // Now that we have serialized payload, we should free any BSTRs that were
- // allocated in BuildIA2Data.
- ClearIA2Data(payload.mData);
+ // Now that we have serialized payload, we should clean up any
+ // BSTRs, interfaces, etc. fetched in BuildInitialIA2Data.
+ CleanupStaticIA2Data(payload.mStaticData);
+ // No need to zero memory, since payload is going out of scope.
+ CleanupDynamicIA2Data(payload.mDynamicData, false);
}
HRESULT
-HandlerProvider::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
+HandlerProvider::GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<DWORD*> aOutPayloadSize)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
if (!IsTargetInterfaceCacheable()) {
*aOutPayloadSize = mscom::StructToStream::GetEmptySize();
return S_OK;
}
MutexAutoLock lock(mMutex);
- GetAndSerializePayload(lock);
+ GetAndSerializePayload(lock, aInterceptor);
if (!mSerializer || !(*mSerializer)) {
// Failed payload serialization is non-fatal
*aOutPayloadSize = mscom::StructToStream::GetEmptySize();
return S_OK;
}
*aOutPayloadSize = mSerializer->GetSize();
return S_OK;
}
+template <typename CondFnT, typename ExeFnT>
+class MOZ_RAII ExecuteWhen final
+{
+public:
+ ExecuteWhen(CondFnT& aCondFn, ExeFnT& aExeFn)
+ : mCondFn(aCondFn)
+ , mExeFn(aExeFn)
+ {
+ }
+
+ ~ExecuteWhen()
+ {
+ if (mCondFn()) {
+ mExeFn();
+ }
+ }
+
+ ExecuteWhen(const ExecuteWhen&) = delete;
+ ExecuteWhen(ExecuteWhen&&) = delete;
+ ExecuteWhen& operator=(const ExecuteWhen&) = delete;
+ ExecuteWhen& operator=(ExecuteWhen&&) = delete;
+
+private:
+ CondFnT& mCondFn;
+ ExeFnT& mExeFn;
+};
+
void
-HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
+HandlerProvider::BuildStaticIA2Data(
+ NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutData)
+{
+ MOZ_ASSERT(aOutData);
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTargetUnk);
+ MOZ_ASSERT(IsTargetInterfaceCacheable());
+
+ // Include interfaces the client is likely to request.
+ // This is cheap here and saves multiple cross-process calls later.
+ // These interfaces must be released in CleanupStaticIA2Data!
+
+ // If the target is already an IAccessible2, this pointer is redundant.
+ // However, the target might be an IAccessibleHyperlink, etc., in which
+ // case the client will almost certainly QI for IAccessible2.
+ HRESULT hr = aInterceptor->GetInterceptorForIID(NEWEST_IA2_IID,
+ (void**)&aOutData->mIA2);
+ if (FAILED(hr)) {
+ // IA2 should always be present, so something has
+ // gone very wrong if this fails.
+ aOutData->mIA2 = nullptr;
+ return;
+ }
+
+ // Some of these interfaces aren't present on all accessibles,
+ // so it's not a failure if these interfaces can't be fetched.
+ hr = aInterceptor->GetInterceptorForIID(IID_IEnumVARIANT,
+ (void**)&aOutData->mIEnumVARIANT);
+ if (FAILED(hr)) {
+ aOutData->mIEnumVARIANT = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHypertext2,
+ (void**)&aOutData->mIAHypertext);
+ if (FAILED(hr)) {
+ aOutData->mIAHypertext = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleHyperlink,
+ (void**)&aOutData->mIAHyperlink);
+ if (FAILED(hr)) {
+ aOutData->mIAHyperlink = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable,
+ (void**)&aOutData->mIATable);
+ if (FAILED(hr)) {
+ aOutData->mIATable = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTable2,
+ (void**)&aOutData->mIATable2);
+ if (FAILED(hr)) {
+ aOutData->mIATable2 = nullptr;
+ }
+
+ hr = aInterceptor->GetInterceptorForIID(IID_IAccessibleTableCell,
+ (void**)&aOutData->mIATableCell);
+ if (FAILED(hr)) {
+ aOutData->mIATableCell = nullptr;
+ }
+}
+
+void
+HandlerProvider::BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data)
{
MOZ_ASSERT(aOutIA2Data);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTargetUnk);
MOZ_ASSERT(IsTargetInterfaceCacheable());
- RefPtr<NEWEST_IA2_INTERFACE>
- target(static_cast<NEWEST_IA2_INTERFACE*>(mTargetUnk.get()));
+ RefPtr<NEWEST_IA2_INTERFACE> target;
+ HRESULT hr = mTargetUnk.get()->QueryInterface(NEWEST_IA2_IID,
+ getter_AddRefs(target));
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = E_UNEXPECTED;
+
+ auto hasFailed = [&hr]() -> bool {
+ return FAILED(hr);
+ };
+
+ auto cleanup = [this, aOutIA2Data]() -> void {
+ CleanupDynamicIA2Data(*aOutIA2Data);
+ };
+
+ ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed, cleanup);
+
+ const VARIANT kChildIdSelf = {VT_I4};
+ VARIANT varVal;
+
+ hr = target->accLocation(&aOutIA2Data->mLeft, &aOutIA2Data->mTop,
+ &aOutIA2Data->mWidth, &aOutIA2Data->mHeight,
+ kChildIdSelf);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accRole(kChildIdSelf, &aOutIA2Data->mRole);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accState(kChildIdSelf, &varVal);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ aOutIA2Data->mState = varVal.lVal;
+
+ hr = target->get_accKeyboardShortcut(kChildIdSelf,
+ &aOutIA2Data->mKeyboardShortcut);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accName(kChildIdSelf, &aOutIA2Data->mName);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accDescription(kChildIdSelf, &aOutIA2Data->mDescription);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accDefaultAction(kChildIdSelf, &aOutIA2Data->mDefaultAction);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accChildCount(&aOutIA2Data->mChildCount);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_accValue(kChildIdSelf, &aOutIA2Data->mValue);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_states(&aOutIA2Data->mIA2States);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->get_attributes(&aOutIA2Data->mAttributes);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ HWND hwnd;
+ hr = target->get_windowHandle(&hwnd);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ aOutIA2Data->mHwnd = PtrToLong(hwnd);
+
+ hr = target->get_locale(&aOutIA2Data->mIA2Locale);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = target->role(&aOutIA2Data->mIA2Role);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ RefPtr<IAccessibleAction> action;
+ // It is not an error if this fails.
+ hr = mTargetUnk.get()->QueryInterface(IID_IAccessibleAction,
+ getter_AddRefs(action));
+ if (SUCCEEDED(hr)) {
+ hr = action->nActions(&aOutIA2Data->mNActions);
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+
+ RefPtr<IAccessibleTableCell> cell;
+ // It is not an error if this fails.
+ hr = mTargetUnk.get()->QueryInterface(IID_IAccessibleTableCell,
+ getter_AddRefs(cell));
+ if (SUCCEEDED(hr)) {
+ hr = cell->get_rowColumnExtents(&aOutIA2Data->mRowIndex,
+ &aOutIA2Data->mColumnIndex,
+ &aOutIA2Data->mRowExtent,
+ &aOutIA2Data->mColumnExtent,
+ &aOutIA2Data->mCellIsSelected);
+ if (FAILED(hr)) {
+ return;
+ }
+ }
// NB: get_uniqueID should be the final property retrieved in this method,
// as its presence is used to determine whether the rest of this data
// retrieval was successful.
- HRESULT hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
- if (FAILED(hr)) {
- ClearIA2Data(*aOutIA2Data);
- }
+ hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
+}
+
+void
+HandlerProvider::CleanupStaticIA2Data(StaticIA2Data& aData)
+{
+ // When CoMarshalInterface writes interfaces out to a stream, it AddRefs.
+ // Therefore, we must release our references after this.
+ ReleaseStaticIA2DataInterfaces(aData);
+ ZeroMemory(&aData, sizeof(StaticIA2Data));
}
void
-HandlerProvider::ClearIA2Data(IA2Data& aData)
+HandlerProvider::BuildInitialIA2Data(
+ NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutStaticData,
+ DynamicIA2Data* aOutDynamicData)
{
- ZeroMemory(&aData, sizeof(IA2Data));
+ BuildStaticIA2Data(aInterceptor, aOutStaticData);
+ if (!aOutStaticData->mIA2) {
+ return;
+ }
+ BuildDynamicIA2Data(aOutDynamicData);
+ if (!aOutDynamicData->mUniqueId) {
+ // Building dynamic data failed, which means building the payload failed.
+ // However, we've already built static data, so we must clean this up.
+ CleanupStaticIA2Data(*aOutStaticData);
+ }
}
bool
HandlerProvider::IsTargetInterfaceCacheable()
{
- return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID;
+ return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID ||
+ mTargetUnkIid == IID_IAccessibleHyperlink;
}
HRESULT
-HandlerProvider::WriteHandlerPayload(NotNull<IStream*> aStream)
+HandlerProvider::WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<IStream*> aStream)
{
MutexAutoLock lock(mMutex);
if (!mSerializer || !(*mSerializer)) {
// Failed payload serialization is non-fatal
mscom::StructToStream emptyStruct;
return emptyStruct.Write(aStream);
}
@@ -209,16 +457,40 @@ HandlerProvider::MarshalAs(REFIID aIid)
aIid == IID_IAccessible2_3) {
// This should always be the newest IA2 interface ID
return NEWEST_IA2_IID;
}
// Otherwise we juse return the identity.
return aIid;
}
+REFIID
+HandlerProvider::GetEffectiveOutParamIid(REFIID aCallIid,
+ ULONG aCallMethod)
+{
+ if (aCallIid == IID_IAccessibleTable ||
+ aCallIid == IID_IAccessibleTable2 ||
+ aCallIid == IID_IAccessibleDocument ||
+ aCallIid == IID_IAccessibleTableCell ||
+ aCallIid == IID_IAccessibleRelation) {
+ return NEWEST_IA2_IID;
+ }
+
+ // IAccessible2_2::accessibleWithCaret
+ static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+ "You have modified NEWEST_IA2_IID. This code needs updating.");
+ if ((aCallIid == IID_IAccessible2_2 || aCallIid == IID_IAccessible2_3) &&
+ aCallMethod == 47) {
+ return NEWEST_IA2_IID;
+ }
+
+ MOZ_ASSERT(false);
+ return IID_IUnknown;
+}
+
HRESULT
HandlerProvider::NewInstance(REFIID aIid,
mscom::InterceptorTargetPtr<IUnknown> aTarget,
NotNull<mscom::IHandlerProvider**> aOutNewPayload)
{
RefPtr<IHandlerProvider> newPayload(new HandlerProvider(aIid, Move(aTarget)));
newPayload.forget(aOutNewPayload.get());
return S_OK;
@@ -254,24 +526,140 @@ HandlerProvider::put_HandlerControl(long
static_cast<DWORD>(aPid), Move(ptrProxy))) {
return E_FAIL;
}
return S_OK;
}
HRESULT
-HandlerProvider::Refresh(IA2Data* aOutData)
+HandlerProvider::Refresh(DynamicIA2Data* aOutData)
{
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
- if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
- this, &HandlerProvider::BuildIA2Data,
+ if (!mscom::InvokeOnMainThread("HandlerProvider::BuildDynamicIA2Data",
+ this, &HandlerProvider::BuildDynamicIA2Data,
aOutData)) {
return E_FAIL;
}
return S_OK;
}
+template<typename Interface>
+HRESULT
+HandlerProvider::ToWrappedObject(Interface** aObj)
+{
+ mscom::STAUniquePtr<Interface> inObj(*aObj);
+ RefPtr<HandlerProvider> hprov = new HandlerProvider(__uuidof(Interface),
+ mscom::ToInterceptorTargetPtr(inObj));
+ HRESULT hr = mscom::MainThreadHandoff::WrapInterface(Move(inObj), hprov,
+ aObj);
+ if (FAILED(hr)) {
+ *aObj = nullptr;
+ }
+ return hr;
+}
+
+void
+HandlerProvider::GetAllTextInfoMainThread(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns, HRESULT* result)
+{
+ MOZ_ASSERT(aText);
+ MOZ_ASSERT(aHyperlinks);
+ MOZ_ASSERT(aNHyperlinks);
+ MOZ_ASSERT(aAttribRuns);
+ MOZ_ASSERT(aNAttribRuns);
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTargetUnk);
+
+ RefPtr<IAccessibleHypertext2> ht;
+ HRESULT hr = mTargetUnk->QueryInterface(IID_IAccessibleHypertext2,
+ getter_AddRefs(ht));
+ if (FAILED(hr)) {
+ *result = hr;
+ return;
+ }
+
+ hr = ht->get_text(0, IA2_TEXT_OFFSET_LENGTH, aText);
+ if (FAILED(hr)) {
+ *result = hr;
+ return;
+ }
+
+ if (hr == S_FALSE) {
+ // No text.
+ *aHyperlinks = nullptr;
+ *aNHyperlinks = 0;
+ *aAttribRuns = nullptr;
+ *aNAttribRuns = 0;
+ *result = S_FALSE;
+ return;
+ }
+
+ hr = ht->get_hyperlinks(aHyperlinks, aNHyperlinks);
+ if (FAILED(hr)) {
+ *aHyperlinks = nullptr;
+ // -1 signals to the handler that it should call hyperlinks itself.
+ *aNHyperlinks = -1;
+ }
+ // We must wrap these hyperlinks in an interceptor.
+ for (long index = 0; index < *aNHyperlinks; ++index) {
+ ToWrappedObject(&(*aHyperlinks)[index]);
+ }
+
+ // Fetch all attribute runs.
+ nsTArray<IA2TextSegment> attribRuns;
+ long end = 0;
+ long length = ::SysStringLen(*aText);
+ while (end < length) {
+ long offset = end;
+ long start;
+ BSTR attribs;
+ // The (exclusive) end of the last run is the start of the next run.
+ hr = ht->get_attributes(offset, &start, &end, &attribs);
+ // Bug 1421873: Gecko can return end <= offset in some rare cases, which
+ // isn't valid. This is perhaps because the text mutated during the loop
+ // for some reason, making this offset invalid.
+ if (FAILED(hr) || end <= offset) {
+ break;
+ }
+ attribRuns.AppendElement(IA2TextSegment({attribs, start, end}));
+ }
+
+ // Put the attribute runs in a COM array.
+ *aNAttribRuns = attribRuns.Length();
+ *aAttribRuns = static_cast<IA2TextSegment*>(::CoTaskMemAlloc(
+ sizeof(IA2TextSegment) * *aNAttribRuns));
+ for (long index = 0; index < *aNAttribRuns; ++index) {
+ (*aAttribRuns)[index] = attribRuns[index];
+ }
+
+ *result = S_OK;
+}
+
+HRESULT
+HandlerProvider::get_AllTextInfo(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns)
+{
+ MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+ HRESULT hr;
+ if (!mscom::InvokeOnMainThread("HandlerProvider::GetAllTextInfoMainThread",
+ this,
+ &HandlerProvider::GetAllTextInfoMainThread,
+ aText, aHyperlinks, aNHyperlinks,
+ aAttribRuns, aNAttribRuns, &hr)) {
+ return E_FAIL;
+ }
+
+ return hr;
+}
+
} // namespace a11y
} // namespace mozilla
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -2,17 +2,17 @@
/* 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/. */
#ifndef mozilla_a11y_HandlerProvider_h
#define mozilla_a11y_HandlerProvider_h
-#include "handler/AccessibleHandler.h"
+#include "mozilla/a11y/AccessibleHandler.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Atomics.h"
#include "mozilla/mscom/IHandlerProvider.h"
#include "mozilla/mscom/Ptr.h"
#include "mozilla/mscom/StructStream.h"
#include "mozilla/Mutex.h"
#include "mozilla/UniquePtr.h"
@@ -36,36 +36,63 @@ public:
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IHandlerProvider
STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
- STDMETHODIMP GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize) override;
- STDMETHODIMP WriteHandlerPayload(NotNull<IStream*> aStream) override;
+ STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<DWORD*> aOutPayloadSize) override;
+ STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
+ NotNull<IStream*> aStream) override;
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
+ STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
+ ULONG aCallMethod) override;
STDMETHODIMP NewInstance(REFIID aIid,
mscom::InterceptorTargetPtr<IUnknown> aTarget,
NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
// IGeckoBackChannel
STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
- STDMETHODIMP Refresh(IA2Data* aOutData) override;
+ STDMETHODIMP Refresh(DynamicIA2Data* aOutData) override;
+ STDMETHODIMP get_AllTextInfo(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns) override;
private:
~HandlerProvider() = default;
void SetHandlerControlOnMainThread(DWORD aPid,
mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
- void GetAndSerializePayload(const MutexAutoLock&);
- void BuildIA2Data(IA2Data* aOutIA2Data);
- static void ClearIA2Data(IA2Data& aData);
+ void GetAndSerializePayload(const MutexAutoLock&,
+ NotNull<mscom::IInterceptor*> aInterceptor);
+ void BuildStaticIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutData);
+ void BuildDynamicIA2Data(DynamicIA2Data* aOutIA2Data);
+ void BuildInitialIA2Data(NotNull<mscom::IInterceptor*> aInterceptor,
+ StaticIA2Data* aOutStaticData,
+ DynamicIA2Data* aOutDynamicData);
+ static void CleanupStaticIA2Data(StaticIA2Data& aData);
bool IsTargetInterfaceCacheable();
+ // Replace a raw object from the main thread with a wrapped, intercepted
+ // object suitable for calling from the MTA.
+ // The reference to the original object is adopted; i.e. you should not
+ // separately release it.
+ // This is intended for objects returned from method calls on the main thread.
+ template<typename Interface> HRESULT ToWrappedObject(Interface** aObj);
+ void GetAllTextInfoMainThread(BSTR* aText,
+ IAccessibleHyperlink*** aHyperlinks,
+ long* aNHyperlinks,
+ IA2TextSegment** aAttribRuns,
+ long* aNAttribRuns,
+ HRESULT* result);
Atomic<uint32_t> mRefCnt;
Mutex mMutex; // Protects mSerializer
const IID mTargetUnkIid;
mscom::InterceptorTargetPtr<IUnknown> mTargetUnk; // Constant, main thread only
UniquePtr<mscom::StructToStream> mSerializer;
RefPtr<IUnknown> mFastMarshalUnk;
};
--- a/accessible/ipc/win/PlatformChild.cpp
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -1,55 +1,62 @@
/* -*- 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 "mozilla/a11y/AccessibleHandler.h"
#include "mozilla/a11y/Compatibility.h"
#include "mozilla/a11y/PlatformChild.h"
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/mscom/InterceptorLog.h"
#include "Accessible2.h"
#include "Accessible2_2.h"
#include "AccessibleHypertext2.h"
+#include "AccessibleTable2.h"
#include "AccessibleTableCell.h"
+#include "AccessibleDocument_i.c"
#include "AccessibleHypertext2_i.c"
namespace mozilla {
namespace a11y {
/**
* Unfortunately the COM interceptor does not intrinsically handle array
* outparams. Instead we manually define the relevant metadata here, and
* register it in a call to mozilla::mscom::RegisterArrayData.
* @see mozilla::mscom::ArrayData
*/
static const mozilla::mscom::ArrayData sPlatformChildArrayData[] = {
{IID_IEnumVARIANT, 3, 1, VT_DISPATCH, IID_IDispatch, 2},
{IID_IAccessible2, 30, 1, VT_UNKNOWN | VT_BYREF, IID_IAccessibleRelation, 2},
- {IID_IAccessibleRelation, 7, 1, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 2},
- {IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 3,
+ {IID_IAccessibleRelation, 7, 1, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 2},
+ {IID_IAccessible2_2, 48, 2, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 3,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
- {IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
+ {IID_IAccessibleTableCell, 4, 0, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 1,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
- {IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
+ {IID_IAccessibleTableCell, 7, 0, VT_UNKNOWN | VT_BYREF, NEWEST_IA2_IID, 1,
mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
- {IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF, IID_IUnknown, 1,
- mozilla::mscom::ArrayData::Flag::eAllocatedByServer}
+ {IID_IAccessibleHypertext2, 25, 0, VT_UNKNOWN | VT_BYREF,
+ IID_IAccessibleHyperlink, 1,
+ mozilla::mscom::ArrayData::Flag::eAllocatedByServer},
+ {IID_IAccessibleTable2, 12, 0, VT_UNKNOWN | VT_BYREF,
+ NEWEST_IA2_IID, 1, mozilla::mscom::ArrayData::Flag::eAllocatedByServer}
};
// Type libraries are thread-neutral, so we can register those from any
// apartment. OTOH, proxies must be registered from within the apartment where
// we intend to instantiate them. Therefore RegisterProxy() must be called
// via EnsureMTA.
PlatformChild::PlatformChild()
- : mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
+ : mIA2Proxy(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"))
+ , mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
, mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
, mSdnTypelib(mozilla::mscom::RegisterTypelib(L"AccessibleMarshal.dll"))
{
WORD actCtxResourceId = Compatibility::GetActCtxResourceId();
mozilla::mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> tmpActCtxMTA;
mozilla::mscom::EnsureMTA([actCtxResourceId, &tmpActCtxMTA]() -> void {
@@ -62,18 +69,19 @@ PlatformChild::PlatformChild()
UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
mozilla::mscom::EnsureMTA([&customProxy]() -> void {
customProxy = Move(mozilla::mscom::RegisterProxy());
});
mCustomProxy = Move(customProxy);
- UniquePtr<mozilla::mscom::RegisteredProxy> ia2Proxy;
- mozilla::mscom::EnsureMTA([&ia2Proxy]() -> void {
- ia2Proxy = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
+ // IA2 needs to be registered in both the main thread's STA as well as the MTA
+ UniquePtr<mozilla::mscom::RegisteredProxy> ia2ProxyMTA;
+ mozilla::mscom::EnsureMTA([&ia2ProxyMTA]() -> void {
+ ia2ProxyMTA = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
});
- mIA2Proxy = Move(ia2Proxy);
+ mIA2ProxyMTA = Move(ia2ProxyMTA);
}
} // namespace a11y
} // namespace mozilla
--- a/accessible/ipc/win/PlatformChild.h
+++ b/accessible/ipc/win/PlatformChild.h
@@ -23,16 +23,17 @@ public:
PlatformChild(PlatformChild&&) = delete;
PlatformChild& operator=(PlatformChild&) = delete;
PlatformChild& operator=(PlatformChild&&) = delete;
private:
mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> mActCtxMTA;
UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
+ UniquePtr<mozilla::mscom::RegisteredProxy> mIA2ProxyMTA;
UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
};
} // namespace mozilla
} // namespace a11y
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -226,20 +226,17 @@ ProxyAccessible::Bounds()
long left;
long top;
long width;
long height;
HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
if (FAILED(hr)) {
return rect;
}
- rect.x = left;
- rect.y = top;
- rect.width = width;
- rect.height = height;
+ rect.SetRect(left, top, width, height);
return rect;
}
void
ProxyAccessible::Language(nsString& aLocale)
{
aLocale.Truncate();
--- a/accessible/ipc/win/handler/AccessibleHandler.cpp
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -7,32 +7,41 @@
#if defined(MOZILLA_INTERNAL_API)
#error This code is NOT for internal Gecko use!
#endif // defined(MOZILLA_INTERNAL_API)
#define INITGUID
#include "AccessibleHandler.h"
#include "AccessibleHandlerControl.h"
-#include "AccessibleTextTearoff.h"
#include "Factory.h"
#include "HandlerData.h"
#include "mozilla/ArrayUtils.h"
+#include "mozilla/a11y/HandlerDataCleanup.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/UniquePtr.h"
#include <objbase.h>
#include <uiautomation.h>
#include <winreg.h>
#include "AccessibleHypertext.h"
+#include "AccessibleHypertext2.h"
#include "Accessible2_i.c"
#include "Accessible2_2_i.c"
#include "Accessible2_3_i.c"
+#include "AccessibleAction_i.c"
+#include "AccessibleHyperlink_i.c"
+#include "AccessibleHypertext_i.c"
+#include "AccessibleHypertext2_i.c"
+#include "AccessibleTable_i.c"
+#include "AccessibleTable2_i.c"
+#include "AccessibleTableCell_i.c"
+#include "AccessibleText_i.c"
namespace mozilla {
namespace a11y {
static mscom::Factory<AccessibleHandler> sHandlerFactory;
HRESULT
AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface)
@@ -55,36 +64,46 @@ AccessibleHandler::Create(IUnknown* aOut
return handler->InternalQueryInterface(aIid, aOutInterface);
}
AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
: mscom::Handler(aOuter, aResult)
, mDispatch(nullptr)
, mIA2PassThru(nullptr)
, mServProvPassThru(nullptr)
+ , mIAHyperlinkPassThru(nullptr)
+ , mIATableCellPassThru(nullptr)
+ , mIAHypertextPassThru(nullptr)
, mCachedData()
, mCacheGen(0)
+ , mCachedHyperlinks(nullptr)
+ , mCachedNHyperlinks(-1)
+ , mCachedTextAttribRuns(nullptr)
+ , mCachedNTextAttribRuns(-1)
{
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
MOZ_ASSERT(ctl);
if (!ctl) {
if (aResult) {
*aResult = E_UNEXPECTED;
}
return;
}
mCacheGen = ctl->GetCacheGen();
}
AccessibleHandler::~AccessibleHandler()
{
+ // No need to zero memory, since we're being destroyed anyway.
+ CleanupDynamicIA2Data(mCachedData.mDynamicData, false);
if (mCachedData.mGeckoBackChannel) {
mCachedData.mGeckoBackChannel->Release();
}
+ ClearTextCache();
}
HRESULT
AccessibleHandler::ResolveIA2()
{
if (mIA2PassThru) {
return S_OK;
}
@@ -100,33 +119,144 @@ AccessibleHandler::ResolveIA2()
// mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
mIA2PassThru->Release();
}
return hr;
}
HRESULT
+AccessibleHandler::ResolveIAHyperlink()
+{
+ if (mIAHyperlinkPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = proxy->QueryInterface(IID_IAccessibleHyperlink,
+ reinterpret_cast<void**>(&mIAHyperlinkPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIAHyperlinkPassThru is a weak reference
+ // (see comments in AccesssibleHandler.h)
+ mIAHyperlinkPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::ResolveIATableCell()
+{
+ if (mIATableCellPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = proxy->QueryInterface(IID_IAccessibleTableCell,
+ reinterpret_cast<void**>(&mIATableCellPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIATableCellPassThru is a weak reference
+ // (see comments in AccesssibleHandler.h)
+ mIATableCellPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
+AccessibleHandler::ResolveIAHypertext()
+{
+ if (mIAHypertextPassThru) {
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> proxy(GetProxy());
+ if (!proxy) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr = proxy->QueryInterface(IID_IAccessibleHypertext2,
+ reinterpret_cast<void**>(&mIAHypertextPassThru));
+ if (SUCCEEDED(hr)) {
+ // mIAHypertextPassThru is a weak reference
+ // (see comments in AccessibleHandler.h)
+ mIAHypertextPassThru->Release();
+ }
+
+ return hr;
+}
+
+HRESULT
AccessibleHandler::MaybeUpdateCachedData()
{
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
if (!ctl) {
return E_OUTOFMEMORY;
}
uint32_t gen = ctl->GetCacheGen();
if (gen == mCacheGen) {
return S_OK;
}
if (!mCachedData.mGeckoBackChannel) {
return E_POINTER;
}
- return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mData);
+ return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mDynamicData);
+}
+
+HRESULT
+AccessibleHandler::GetAllTextInfo(BSTR* aText)
+{
+ MOZ_ASSERT(mCachedData.mGeckoBackChannel);
+
+ ClearTextCache();
+
+ return mCachedData.mGeckoBackChannel->get_AllTextInfo(aText,
+ &mCachedHyperlinks, &mCachedNHyperlinks,
+ &mCachedTextAttribRuns, &mCachedNTextAttribRuns);
+}
+
+void
+AccessibleHandler::ClearTextCache()
+{
+ if (mCachedNHyperlinks >= 0) {
+ // We cached hyperlinks, but the caller never retrieved them.
+ for (long index = 0; index < mCachedNHyperlinks; ++index) {
+ mCachedHyperlinks[index]->Release();
+ }
+ // mCachedHyperlinks might already be null if there are no hyperlinks.
+ if (mCachedHyperlinks) {
+ ::CoTaskMemFree(mCachedHyperlinks);
+ mCachedHyperlinks = nullptr;
+ }
+ mCachedNHyperlinks = -1;
+ }
+
+ if (mCachedTextAttribRuns) {
+ for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
+ if (mCachedTextAttribRuns[index].text) {
+ // The caller never requested this attribute run.
+ ::SysFreeString(mCachedTextAttribRuns[index].text);
+ }
+ }
+ // This array is internal to us, so we must always free it.
+ ::CoTaskMemFree(mCachedTextAttribRuns);
+ mCachedTextAttribRuns = nullptr;
+ mCachedNTextAttribRuns = -1;
+ }
}
HRESULT
AccessibleHandler::ResolveIDispatch()
{
if (mDispatch) {
return S_OK;
}
@@ -179,19 +309,61 @@ AccessibleHandler::QueryHandlerInterface
}
if (aIid == IID_IServiceProvider) {
RefPtr<IServiceProvider> svcProv(static_cast<IServiceProvider*>(this));
svcProv.forget(aOutInterface);
return S_OK;
}
- if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext) {
- RefPtr<IAccessibleHypertext> textTearoff(new AccessibleTextTearoff(this));
- textTearoff.forget(aOutInterface);
+ if (HasPayload()) {
+ // The proxy manager caches interfaces marshaled in the payload
+ // and returns them on QI without a cross-process call.
+ // However, it doesn't know about interfaces which don't exist.
+ // We can determine this from the payload.
+ if ((aIid == IID_IEnumVARIANT &&
+ !mCachedData.mStaticData.mIEnumVARIANT) ||
+ ((aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
+ aIid == IID_IAccessibleHypertext2) &&
+ !mCachedData.mStaticData.mIAHypertext) ||
+ ((aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) &&
+ !mCachedData.mStaticData.mIAHyperlink) ||
+ (aIid == IID_IAccessibleTable &&
+ !mCachedData.mStaticData.mIATable) ||
+ (aIid == IID_IAccessibleTable2 &&
+ !mCachedData.mStaticData.mIATable2) ||
+ (aIid == IID_IAccessibleTableCell &&
+ !mCachedData.mStaticData.mIATableCell)) {
+ // We already know this interface is not available, so don't query
+ // the proxy, thus avoiding a pointless cross-process call.
+ // If we return E_NOINTERFACE here, mscom::Handler will try the COM
+ // proxy. S_FALSE signals that the proxy should not be tried.
+ return S_FALSE;
+ }
+ }
+
+ if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) {
+ RefPtr<IAccessibleHyperlink> iaLink(
+ static_cast<IAccessibleHyperlink*>(this));
+ iaLink.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IAccessibleTableCell) {
+ RefPtr<IAccessibleTableCell> iaCell(
+ static_cast<IAccessibleTableCell*>(this));
+ iaCell.forget(aOutInterface);
+ return S_OK;
+ }
+
+ if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
+ aIid == IID_IAccessibleHypertext2) {
+ RefPtr<IAccessibleHypertext2> iaHt(
+ static_cast<IAccessibleHypertext2*>(this));
+ iaHt.forget(aOutInterface);
return S_OK;
}
if (aIid == IID_IProvideClassInfo) {
RefPtr<IProvideClassInfo> clsInfo(this);
clsInfo.forget(aOutInterface);
return S_OK;
}
@@ -209,19 +381,41 @@ AccessibleHandler::ReadHandlerPayload(IS
mscom::StructFromStream deserializer(aStream);
if (!deserializer) {
return E_FAIL;
}
if (deserializer.IsEmpty()) {
return S_FALSE;
}
- if (!deserializer.Read(&mCachedData, &IA2Payload_Decode)) {
+ // QueryHandlerInterface might get called while we deserialize the payload,
+ // but that checks the interface pointers in the payload to determine what
+ // interfaces are available. Therefore, deserialize into a temporary struct
+ // and update mCachedData only after deserialization completes.
+ // The decoding functions can misbehave if their target memory is not zeroed
+ // beforehand, so ensure we do that.
+ IA2Payload newData{};
+ if (!deserializer.Read(&newData, &IA2Payload_Decode)) {
return E_FAIL;
}
+ // Clean up the old data.
+ // No need to zero memory, since we're about to completely replace this.
+ CleanupDynamicIA2Data(mCachedData.mDynamicData, false);
+ mCachedData = newData;
+
+ // These interfaces have been aggregated into the proxy manager.
+ // The proxy manager will resolve these interfaces now on QI,
+ // so we can release these pointers.
+ // However, we don't null them out because we use their presence
+ // to determine whether the interface is available
+ // so as to avoid pointless cross-proc QI calls returning E_NOINTERFACE.
+ // Note that if pointers to other objects (in contrast to
+ // interfaces of *this* object) are added in future, we should not release
+ // those pointers.
+ ReleaseStaticIA2DataInterfaces(mCachedData.mStaticData);
if (!mCachedData.mGeckoBackChannel) {
return S_OK;
}
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
if (!ctl) {
return E_OUTOFMEMORY;
@@ -363,29 +557,41 @@ AccessibleHandler::Invoke(DISPID dispIdM
if (FAILED(hr)) {
return hr;
}
return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
pVarResult, pExcepInfo, puArgErr);
}
+inline static BSTR
+CopyBSTR(BSTR aSrc)
+{
+ return ::SysAllocStringLen(aSrc, ::SysStringLen(aSrc));
+}
+
#define BEGIN_CACHE_ACCESS \
{ \
HRESULT hr; \
if (FAILED(hr = MaybeUpdateCachedData())) { \
return hr; \
} \
}
-static BSTR
-CopyBSTR(BSTR aSrc)
-{
- return SysAllocStringLen(aSrc, SysStringLen(aSrc));
-}
+#define GET_FIELD(member, assignTo) \
+ { \
+ assignTo = mCachedData.mDynamicData.member; \
+ }
+
+#define GET_BSTR(member, assignTo) \
+ { \
+ assignTo = CopyBSTR(mCachedData.mDynamicData.member); \
+ }
+
+/*** IAccessible ***/
HRESULT
AccessibleHandler::get_accParent(IDispatch **ppdispParent)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
@@ -393,21 +599,28 @@ AccessibleHandler::get_accParent(IDispat
}
HRESULT
AccessibleHandler::get_accChildCount(long *pcountChildren)
{
if (!pcountChildren) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accChildCount(pcountChildren);
}
- return mIA2PassThru->get_accChildCount(pcountChildren);
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mChildCount, *pcountChildren);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
{
if (!ppdispChild) {
return E_INVALIDARG;
}
@@ -425,75 +638,110 @@ AccessibleHandler::get_accChild(VARIANT
}
HRESULT
AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName)
{
if (!pszName) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accName(varChild, pszName);
}
- return mIA2PassThru->get_accName(varChild, pszName);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mName, *pszName);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue)
{
if (!pszValue) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accValue(varChild, pszValue);
}
- return mIA2PassThru->get_accValue(varChild, pszValue);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mValue, *pszValue);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription)
{
if (!pszDescription) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accDescription(varChild, pszDescription);
}
- return mIA2PassThru->get_accDescription(varChild, pszDescription);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mDescription, *pszDescription);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
{
if (!pvarRole) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accRole(varChild, pvarRole);
}
- return mIA2PassThru->get_accRole(varChild, pvarRole);
+
+ BEGIN_CACHE_ACCESS;
+ return ::VariantCopy(pvarRole, &mCachedData.mDynamicData.mRole);
}
HRESULT
AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState)
{
if (!pvarState) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accState(varChild, pvarState);
}
- return mIA2PassThru->get_accState(varChild, pvarState);
+
+ pvarState->vt = VT_I4;
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mState, pvarState->lVal);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accHelp(VARIANT varChild, BSTR *pszHelp)
{
// This matches what AccessibleWrap does
if (!pszHelp) {
return E_INVALIDARG;
@@ -517,21 +765,28 @@ AccessibleHandler::get_accHelpTopic(BSTR
HRESULT
AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
BSTR *pszKeyboardShortcut)
{
if (!pszKeyboardShortcut) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
}
- return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_accFocus(VARIANT *pvarChild)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
@@ -551,43 +806,63 @@ AccessibleHandler::get_accSelection(VARI
HRESULT
AccessibleHandler::get_accDefaultAction(VARIANT varChild,
BSTR *pszDefaultAction)
{
if (!pszDefaultAction) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
}
- return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mDefaultAction, *pszDefaultAction);
+ return S_OK;
}
HRESULT
AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
return mIA2PassThru->accSelect(flagsSelect, varChild);
}
HRESULT
AccessibleHandler::accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
long *pcyHeight, VARIANT varChild)
{
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+ if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
+ varChild);
}
- return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
- varChild);
+
+ if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
+ return E_INVALIDARG;
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mLeft, *pxLeft);
+ GET_FIELD(mTop, *pyTop);
+ GET_FIELD(mWidth, *pcxWidth);
+ GET_FIELD(mHeight, *pcyHeight);
+ return S_OK;
}
HRESULT
AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
VARIANT *pvarEndUpAt)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
@@ -625,16 +900,18 @@ AccessibleHandler::put_accName(VARIANT v
HRESULT
AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue)
{
// This matches AccessibleWrap
return E_NOTIMPL;
}
+/*** IAccessible2 ***/
+
HRESULT
AccessibleHandler::get_nRelations(long* nRelations)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
return mIA2PassThru->get_nRelations(nRelations);
@@ -664,21 +941,28 @@ AccessibleHandler::get_relations(long ma
}
HRESULT
AccessibleHandler::role(long* role)
{
if (!role) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->role(role);
}
- return mIA2PassThru->role(role);
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mIA2Role, *role);
+ return S_OK;
}
HRESULT
AccessibleHandler::scrollTo(IA2ScrollType scrollType)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
@@ -710,21 +994,28 @@ AccessibleHandler::get_groupPosition(lon
}
HRESULT
AccessibleHandler::get_states(AccessibleStates* states)
{
if (!states) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_states(states);
}
- return mIA2PassThru->get_states(states);
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mIA2States, *states);
+ return S_OK;
}
HRESULT
AccessibleHandler::get_extendedRole(BSTR* extendedRole)
{
// This matches ia2Accessible
if (!extendedRole) {
return E_INVALIDARG;
@@ -790,31 +1081,40 @@ AccessibleHandler::get_uniqueID(long* un
}
if (!HasPayload()) {
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
return mIA2PassThru->get_uniqueID(uniqueID);
}
- *uniqueID = mCachedData.mData.mUniqueId;
+ *uniqueID = mCachedData.mDynamicData.mUniqueId;
return S_OK;
}
HRESULT
AccessibleHandler::get_windowHandle(HWND* windowHandle)
{
if (!windowHandle) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_windowHandle(windowHandle);
}
- return mIA2PassThru->get_windowHandle(windowHandle);
+
+ BEGIN_CACHE_ACCESS;
+ long hwnd = 0;
+ GET_FIELD(mHwnd, hwnd);
+ *windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
+ return S_OK;
}
HRESULT
AccessibleHandler::get_indexInParent(long* indexInParent)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
@@ -823,47 +1123,62 @@ AccessibleHandler::get_indexInParent(lon
}
HRESULT
AccessibleHandler::get_locale(IA2Locale* locale)
{
if (!locale) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_locale(locale);
}
- return mIA2PassThru->get_locale(locale);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mIA2Locale.language, locale->language);
+ GET_BSTR(mIA2Locale.country, locale->country);
+ GET_BSTR(mIA2Locale.variant, locale->variant);
return S_OK;
}
HRESULT
AccessibleHandler::get_attributes(BSTR* attributes)
{
if (!attributes) {
return E_INVALIDARG;
}
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIA2();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIA2PassThru->get_attributes(attributes);
}
- return mIA2PassThru->get_attributes(attributes);
+
+ BEGIN_CACHE_ACCESS;
+ GET_BSTR(mAttributes, *attributes);
+ return S_OK;
}
+/*** IAccessible2_2 ***/
+
HRESULT
AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute)
{
- // We could extract these individually from cached mAttributes.
- // Consider it if traffic warrants it
- HRESULT hr = ResolveIA2();
- if (FAILED(hr)) {
- return hr;
- }
- return mIA2PassThru->get_attribute(name, attribute);
+ // Not yet implemented by ia2Accessible.
+ // Once ia2Accessible implements this, we could either pass it through
+ // or we could extract these individually from cached mAttributes.
+ // The latter should be considered if traffic warrants it.
+ return E_NOTIMPL;
}
HRESULT
AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible,
long* caretOffset)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
@@ -880,16 +1195,18 @@ AccessibleHandler::get_relationTargetsOf
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
nTargets);
}
+/*** IAccessible2_3 ***/
+
HRESULT
AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges)
{
HRESULT hr = ResolveIA2();
if (FAILED(hr)) {
return hr;
}
return mIA2PassThru->get_selectionRanges(ranges, nRanges);
@@ -899,35 +1216,48 @@ static const GUID kUnsupportedServices[]
// Unknown, queried by Windows
{0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}},
// Unknown, queried by Windows
{0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }},
// Unknown, queried by Windows
{0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }},
// Unknown, queried by Windows
{0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }},
- // Unknown, queried by Windows
- {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }}
+ // SID_IsUIAutomationObject (undocumented), queried by Windows
+ {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }},
+ // IIS_IsOleaccProxy (undocumented), queried by Windows
+ {0x902697FA, 0x80E4, 0x4560, {0x80, 0x2A, 0xA1, 0x3F, 0x22, 0xA6, 0x47, 0x09}},
+ // IID_IHTMLElement, queried by JAWS
+ {0x3050F1FF, 0x98B5, 0x11CF, {0xBB, 0x82, 0x00, 0xAA, 0x00, 0xBD, 0xCE, 0x0B}}
};
+/*** IServiceProvider ***/
+
HRESULT
AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid,
void** aOutInterface)
{
static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
"You have modified NEWEST_IA2_IID. This code needs updating.");
/* We're taking advantage of the fact that we are implementing IA2 as part
of our own object to implement this just like a QI. */
if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
aIid == IID_IAccessible2) {
RefPtr<NEWEST_IA2_INTERFACE> ia2(this);
ia2.forget(aOutInterface);
return S_OK;
}
+ // JAWS uses QueryService for these, but QI will work just fine and we can
+ // thus avoid a cross-process call. More importantly, if QS is used, the
+ // handler won't get used for that object, so our caching won't be used.
+ if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleText) {
+ return InternalQueryInterface(aIid, aOutInterface);
+ }
+
for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) {
if (aServiceId == kUnsupportedServices[i]) {
return E_NOINTERFACE;
}
}
if (!mServProvPassThru) {
RefPtr<IUnknown> proxy(GetProxy());
@@ -944,27 +1274,707 @@ AccessibleHandler::QueryService(REFGUID
// mServProvPassThru is a weak reference (see comments in
// AccessibleHandler.h)
mServProvPassThru->Release();
}
return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
}
+/*** IProvideClassInfo ***/
+
HRESULT
AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo)
{
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
if (!ctl) {
return E_OUTOFMEMORY;
}
return ctl->GetHandlerTypeInfo(aOutTypeInfo);
}
+/*** IAccessibleAction ***/
+
+HRESULT
+AccessibleHandler::nActions(long* nActions)
+{
+ if (!nActions) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->nActions(nActions);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mNActions, *nActions);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::doAction(long actionIndex)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->doAction(actionIndex);
+}
+
+HRESULT
+AccessibleHandler::get_description(long actionIndex, BSTR* description)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_description(actionIndex, description);
+}
+
+HRESULT
+AccessibleHandler::get_keyBinding(long actionIndex,
+ long nMaxBindings,
+ BSTR** keyBindings,
+ long* nBindings)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_keyBinding(
+ actionIndex, nMaxBindings, keyBindings, nBindings);
+}
+
+HRESULT
+AccessibleHandler::get_name(long actionIndex, BSTR* name)
+{
+ if (!name) {
+ return E_INVALIDARG;
+ }
+
+ if (HasPayload()) {
+ if (actionIndex >= mCachedData.mDynamicData.mNActions) {
+ // Action does not exist.
+ return E_INVALIDARG;
+ }
+
+ if (actionIndex == 0) {
+ // same as accDefaultAction.
+ GET_BSTR(mDefaultAction, *name);
+ return S_OK;
+ }
+ }
+
+ // At this point, there's either no payload or actionIndex is > 0.
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_name(actionIndex, name);
+}
+
+HRESULT
+AccessibleHandler::get_localizedName(long actionIndex, BSTR* localizedName)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_localizedName(actionIndex, localizedName);
+}
+
+/*** IAccessibleHyperlink ***/
+
+HRESULT
+AccessibleHandler::get_anchor(long index, VARIANT* anchor)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_anchor(index, anchor);
+}
+
+HRESULT
+AccessibleHandler::get_anchorTarget(long index, VARIANT* anchorTarget)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_anchorTarget(index, anchorTarget);
+}
+
+HRESULT
+AccessibleHandler::get_startIndex(long* index)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_startIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_endIndex(long* index)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_endIndex(index);
+}
+
+HRESULT
+AccessibleHandler::get_valid(boolean* valid)
+{
+ HRESULT hr = ResolveIAHyperlink();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIAHyperlinkPassThru->get_valid(valid);
+}
+
+/*** IAccessibleTableCell ***/
+
+HRESULT
+AccessibleHandler::get_columnExtent(long* nColumnsSpanned)
+{
+ if (!nColumnsSpanned) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_columnExtent(nColumnsSpanned);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mColumnExtent, *nColumnsSpanned);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_columnHeaderCells(IUnknown*** cellAccessibles,
+ long* nColumnHeaderCells)
+{
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_columnHeaderCells(cellAccessibles,
+ nColumnHeaderCells);
+}
+
+HRESULT
+AccessibleHandler::get_columnIndex(long* columnIndex)
+{
+ if (!columnIndex) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_columnIndex(columnIndex);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mColumnIndex, *columnIndex);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowExtent(long* nRowsSpanned)
+{
+ if (!nRowsSpanned) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowExtent(nRowsSpanned);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowExtent, *nRowsSpanned);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowHeaderCells(IUnknown*** cellAccessibles,
+ long* nRowHeaderCells)
+{
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_rowHeaderCells(cellAccessibles,
+ nRowHeaderCells);
+}
+
+HRESULT
+AccessibleHandler::get_rowIndex(long* rowIndex)
+{
+ if (!rowIndex) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowIndex(rowIndex);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowIndex, *rowIndex);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_isSelected(boolean* isSelected)
+{
+ if (!isSelected) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_isSelected(isSelected);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mCellIsSelected, *isSelected);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_rowColumnExtents(long* row, long* column,
+ long* rowExtents, long* columnExtents,
+ boolean* isSelected)
+{
+ if (!row || !column || !rowExtents || !columnExtents || !isSelected) {
+ return E_INVALIDARG;
+ }
+
+ if (!HasPayload()) {
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return mIATableCellPassThru->get_rowColumnExtents(row, column, rowExtents,
+ columnExtents, isSelected);
+ }
+
+ BEGIN_CACHE_ACCESS;
+ GET_FIELD(mRowIndex, *row);
+ GET_FIELD(mColumnIndex, *column);
+ GET_FIELD(mRowExtent, *rowExtents);
+ GET_FIELD(mColumnExtent, *columnExtents);
+ GET_FIELD(mCellIsSelected, *isSelected);
+ return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_table(IUnknown** table)
+{
+ HRESULT hr = ResolveIATableCell();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIATableCellPassThru->get_table(table);
+}
+
+/*** IAccessibleText ***/
+
+HRESULT
+AccessibleHandler::addSelection(long startOffset, long endOffset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->addSelection(startOffset, endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_attributes(long offset, long *startOffset,
+ long *endOffset, BSTR *textAttributes)
+{
+ if (!startOffset || !endOffset || !textAttributes) {
+ return E_INVALIDARG;
+ }
+
+ if (mCachedNTextAttribRuns >= 0) {
+ // We have cached attributes.
+ for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
+ auto& attribRun = mCachedTextAttribRuns[index];
+ if (attribRun.start <= offset && offset < attribRun.end) {
+ *startOffset = attribRun.start;
+ *endOffset = attribRun.end;
+ *textAttributes = attribRun.text;
+ // The caller will clean this up.
+ // (We only keep each cached attribute run for one call.)
+ attribRun.text = nullptr;
+ // The cache for this run is now invalid, so don't visit it again.
+ attribRun.end = 0;
+ return S_OK;
+ }
+ }
+ }
+
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_attributes(offset, startOffset, endOffset,
+ textAttributes);
+}
+
+HRESULT
+AccessibleHandler::get_caretOffset(long *offset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_caretOffset(offset);
+}
+
+HRESULT
+AccessibleHandler::get_characterExtents(long offset,
+ enum IA2CoordinateType coordType,
+ long *x, long *y, long *width,
+ long *height)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_characterExtents(offset, coordType, x, y,
+ width, height);
+}
+
+HRESULT
+AccessibleHandler::get_nSelections(long *nSelections)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_nSelections(nSelections);
+}
+
+HRESULT
+AccessibleHandler::get_offsetAtPoint(long x, long y,
+ enum IA2CoordinateType coordType,
+ long *offset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_offsetAtPoint(x, y, coordType, offset);
+}
+
+HRESULT
+AccessibleHandler::get_selection(long selectionIndex, long *startOffset,
+ long *endOffset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_selection(selectionIndex, startOffset,
+ endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_text(long startOffset, long endOffset, BSTR *text)
+{
+ if (!text) {
+ return E_INVALIDARG;
+ }
+
+ HRESULT hr;
+ if (mCachedData.mGeckoBackChannel &&
+ startOffset == 0 && endOffset == IA2_TEXT_OFFSET_LENGTH) {
+ // If the caller is retrieving all text, they will probably want all
+ // hyperlinks and attributes as well.
+ hr = GetAllTextInfo(text);
+ if (SUCCEEDED(hr)) {
+ return hr;
+ }
+ // We fall back to a normal call if this fails.
+ }
+
+ hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_text(startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::get_textBeforeOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textBeforeOffset(offset, boundaryType,
+ startOffset, endOffset,
+ text);
+}
+
+HRESULT
+AccessibleHandler::get_textAfterOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textAfterOffset(offset, boundaryType,
+ startOffset, endOffset,
+ text);
+}
+
+HRESULT
+AccessibleHandler::get_textAtOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_textAtOffset(offset, boundaryType,
+ startOffset, endOffset, text);
+}
+
+HRESULT
+AccessibleHandler::removeSelection(long selectionIndex)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->removeSelection(selectionIndex);
+}
+
+HRESULT
+AccessibleHandler::setCaretOffset(long offset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->setCaretOffset(offset);
+}
+
+HRESULT
+AccessibleHandler::setSelection(long selectionIndex, long startOffset,
+ long endOffset)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->setSelection(selectionIndex, startOffset,
+ endOffset);
+}
+
+HRESULT
+AccessibleHandler::get_nCharacters(long *nCharacters)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_nCharacters(nCharacters);
+}
+
+HRESULT
+AccessibleHandler::scrollSubstringTo(long startIndex, long endIndex,
+ enum IA2ScrollType scrollType)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->scrollSubstringTo(startIndex, endIndex,
+ scrollType);
+}
+
+HRESULT
+AccessibleHandler::scrollSubstringToPoint(long startIndex, long endIndex,
+ enum IA2CoordinateType coordinateType,
+ long x, long y)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->scrollSubstringToPoint(startIndex, endIndex,
+ coordinateType, x, y);
+}
+
+HRESULT
+AccessibleHandler::get_newText(IA2TextSegment *newText)
+{
+ if (!newText) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
+ MOZ_ASSERT(ctl);
+ if (!ctl) {
+ return S_OK;
+ }
+
+ long id;
+ HRESULT hr = this->get_uniqueID(&id);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ctl->GetNewText(id, WrapNotNull(newText));
+}
+
+HRESULT
+AccessibleHandler::get_oldText(IA2TextSegment *oldText)
+{
+ if (!oldText) {
+ return E_INVALIDARG;
+ }
+
+ RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
+ MOZ_ASSERT(ctl);
+ if (!ctl) {
+ return S_OK;
+ }
+
+ long id;
+ HRESULT hr = this->get_uniqueID(&id);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return ctl->GetOldText(id, WrapNotNull(oldText));
+}
+
+/*** IAccessibleHypertext ***/
+
+HRESULT
+AccessibleHandler::get_nHyperlinks(long *hyperlinkCount)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_nHyperlinks(hyperlinkCount);
+}
+
+HRESULT
+AccessibleHandler::get_hyperlink(long index,
+ IAccessibleHyperlink **hyperlink)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlink(index, hyperlink);
+}
+
+HRESULT
+AccessibleHandler::get_hyperlinkIndex(long charIndex, long *hyperlinkIndex)
+{
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlinkIndex(charIndex, hyperlinkIndex);
+}
+
+/*** IAccessibleHypertext2 ***/
+
+HRESULT
+AccessibleHandler::get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+ long* nHyperlinks)
+{
+ if (!hyperlinks || !nHyperlinks) {
+ return E_INVALIDARG;
+ }
+
+ if (mCachedNHyperlinks >= 0) {
+ // We have cached hyperlinks.
+ *hyperlinks = mCachedHyperlinks;
+ *nHyperlinks = mCachedNHyperlinks;
+ // The client will clean these up. (We only keep the cache for one call.)
+ mCachedHyperlinks = nullptr;
+ mCachedNHyperlinks = -1;
+ return mCachedNHyperlinks == 0 ? S_FALSE : S_OK;
+ }
+
+ HRESULT hr = ResolveIAHypertext();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ return mIAHypertextPassThru->get_hyperlinks(hyperlinks, nHyperlinks);
+}
+
} // namespace a11y
} // namespace mozilla
extern "C" HRESULT __stdcall
ProxyDllCanUnloadNow();
extern "C" HRESULT __stdcall
DllCanUnloadNow()
--- a/accessible/ipc/win/handler/AccessibleHandler.h
+++ b/accessible/ipc/win/handler/AccessibleHandler.h
@@ -30,30 +30,36 @@ import NEWEST_IA2_IDL;
#include "HandlerData.h"
#include <windows.h>
#if !defined(MOZILLA_INTERNAL_API)
#include "Accessible2_3.h"
+#include "AccessibleHyperlink.h"
+#include "AccessibleHypertext2.h"
+#include "AccessibleTableCell.h"
#include "Handler.h"
#include "mozilla/mscom/StructStream.h"
#include "mozilla/UniquePtr.h"
#include <ocidl.h>
#include <servprov.h>
namespace mozilla {
namespace a11y {
class AccessibleHandler final : public mscom::Handler
, public NEWEST_IA2_INTERFACE
, public IServiceProvider
, public IProvideClassInfo
+ , public IAccessibleHyperlink
+ , public IAccessibleTableCell
+ , public IAccessibleHypertext2
{
public:
static HRESULT Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface);
// mscom::Handler
HRESULT QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
void** aOutInterface) override;
HRESULT ReadHandlerPayload(IStream* aStream, REFIID aIid) override;
@@ -147,23 +153,112 @@ public:
// IServiceProvider
STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid,
void** aOutInterface) override;
// IProvideClassInfo
STDMETHODIMP GetClassInfo(ITypeInfo** aOutTypeInfo) override;
+ // IAccessibleAction
+ STDMETHODIMP nActions(long* nActions) override;
+ STDMETHODIMP doAction(long actionIndex) override;
+ STDMETHODIMP get_description(long actionIndex, BSTR* description) override;
+ STDMETHODIMP get_keyBinding(long actionIndex,
+ long nMaxBindings,
+ BSTR** keyBindings,
+ long* nBindings) override;
+ STDMETHODIMP get_name(long actionIndex, BSTR* name) override;
+ STDMETHODIMP get_localizedName(long actionIndex,
+ BSTR* localizedName) override;
+
+ // IAccessibleHyperlink
+ STDMETHODIMP get_anchor(long index, VARIANT* anchor) override;
+ STDMETHODIMP get_anchorTarget(long index, VARIANT* anchorTarget) override;
+ STDMETHODIMP get_startIndex(long* index) override;
+ STDMETHODIMP get_endIndex(long* index) override;
+ STDMETHODIMP get_valid(boolean* valid) override;
+
+ // IAccessibleTableCell
+ STDMETHODIMP get_columnExtent(long* nColumnsSpanned) override;
+ STDMETHODIMP get_columnHeaderCells(IUnknown*** cellAccessibles,
+ long* nColumnHeaderCells) override;
+ STDMETHODIMP get_columnIndex(long* columnIndex) override;
+ STDMETHODIMP get_rowExtent(long* nRowsSpanned) override;
+ STDMETHODIMP get_rowHeaderCells(IUnknown*** cellAccessibles,
+ long* nRowHeaderCells) override;
+ STDMETHODIMP get_rowIndex(long* rowIndex) override;
+ STDMETHODIMP get_isSelected(boolean* isSelected) override;
+ STDMETHODIMP get_rowColumnExtents(long* row, long* column,
+ long* rowExtents, long* columnExtents,
+ boolean* isSelected) override;
+ STDMETHODIMP get_table(IUnknown** table) override;
+
+ // IAccessibleText
+ STDMETHODIMP addSelection(long startOffset, long endOffset) override;
+ STDMETHODIMP get_attributes(long offset, long *startOffset, long *endOffset,
+ BSTR *textAttributes) override;
+ STDMETHODIMP get_caretOffset(long *offset) override;
+ STDMETHODIMP get_characterExtents(long offset,
+ enum IA2CoordinateType coordType, long *x,
+ long *y, long *width, long *height) override;
+ STDMETHODIMP get_nSelections(long *nSelections) override;
+ STDMETHODIMP get_offsetAtPoint(long x, long y,
+ enum IA2CoordinateType coordType,
+ long *offset) override;
+ STDMETHODIMP get_selection(long selectionIndex, long *startOffset,
+ long *endOffset) override;
+ STDMETHODIMP get_text(long startOffset, long endOffset, BSTR *text) override;
+ STDMETHODIMP get_textBeforeOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text) override;
+ STDMETHODIMP get_textAfterOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text) override;
+ STDMETHODIMP get_textAtOffset(long offset,
+ enum IA2TextBoundaryType boundaryType,
+ long *startOffset, long *endOffset,
+ BSTR *text) override;
+ STDMETHODIMP removeSelection(long selectionIndex) override;
+ STDMETHODIMP setCaretOffset(long offset) override;
+ STDMETHODIMP setSelection(long selectionIndex, long startOffset,
+ long endOffset) override;
+ STDMETHODIMP get_nCharacters(long *nCharacters) override;
+ STDMETHODIMP scrollSubstringTo(long startIndex, long endIndex,
+ enum IA2ScrollType scrollType) override;
+ STDMETHODIMP scrollSubstringToPoint(long startIndex, long endIndex,
+ enum IA2CoordinateType coordinateType,
+ long x, long y) override;
+ STDMETHODIMP get_newText(IA2TextSegment *newText) override;
+ STDMETHODIMP get_oldText(IA2TextSegment *oldText) override;
+
+ // IAccessibleHypertext
+ STDMETHODIMP get_nHyperlinks(long *hyperlinkCount) override;
+ STDMETHODIMP get_hyperlink(long index,
+ IAccessibleHyperlink **hyperlink) override;
+ STDMETHODIMP get_hyperlinkIndex(long charIndex, long *hyperlinkIndex) override;
+
+ // IAccessibleHypertext2
+ STDMETHODIMP get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
+ long* nHyperlinks) override;
+
private:
AccessibleHandler(IUnknown* aOuter, HRESULT* aResult);
virtual ~AccessibleHandler();
HRESULT ResolveIA2();
HRESULT ResolveIDispatch();
+ HRESULT ResolveIAHyperlink();
+ HRESULT ResolveIAHypertext();
+ HRESULT ResolveIATableCell();
HRESULT MaybeUpdateCachedData();
+ HRESULT GetAllTextInfo(BSTR* aText);
+ void ClearTextCache();
RefPtr<IUnknown> mDispatchUnk;
/**
* Handlers aggregate their proxies. This means that their proxies delegate
* their IUnknown implementation to us.
*
* mDispatchUnk and the result of Handler::GetProxy() are both strong
* references to the aggregated objects. OTOH, any interfaces that are QI'd
@@ -178,19 +273,26 @@ private:
* must immediately Release() them to prevent these cycles.
*
* It is safe for us to use these raw pointers because the aggregated
* objects's lifetimes are proper subsets of our own lifetime.
*/
IDispatch* mDispatch; // weak
NEWEST_IA2_INTERFACE* mIA2PassThru; // weak
IServiceProvider* mServProvPassThru; // weak
+ IAccessibleHyperlink* mIAHyperlinkPassThru; // weak
+ IAccessibleTableCell* mIATableCellPassThru; // weak
+ IAccessibleHypertext2* mIAHypertextPassThru; // weak
IA2Payload mCachedData;
UniquePtr<mscom::StructToStream> mSerializer;
uint32_t mCacheGen;
+ IAccessibleHyperlink** mCachedHyperlinks;
+ long mCachedNHyperlinks;
+ IA2TextSegment* mCachedTextAttribRuns;
+ long mCachedNTextAttribRuns;
};
} // namespace a11y
} // namespace mozilla
#endif // !defined(MOZILLA_INTERNAL_API)
#endif // defined(__midl)
deleted file mode 100644
--- a/accessible/ipc/win/handler/AccessibleTextTearoff.cpp
+++ /dev/null
@@ -1,359 +0,0 @@
-/* -*- 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/. */
-
-#if defined(MOZILLA_INTERNAL_API)
-#error This code is NOT for internal Gecko use!
-#endif // defined(MOZILLA_INTERNAL_API)
-
-#include "AccessibleTextTearoff.h"
-
-#include "AccessibleHandlerControl.h"
-#include "AccessibleText_i.c"
-#include "AccessibleHypertext_i.c"
-#include "Factory.h"
-
-#include "mozilla/Assertions.h"
-
-namespace mozilla {
-namespace a11y {
-
-AccessibleTextTearoff::AccessibleTextTearoff(AccessibleHandler* aHandler)
- : mHandler(aHandler)
-{
- MOZ_ASSERT(aHandler);
-}
-
-HRESULT
-AccessibleTextTearoff::ResolveAccText()
-{
- if (mAccTextProxy) {
- return S_OK;
- }
-
- RefPtr<IUnknown> proxy(mHandler->GetProxy());
- if (!proxy) {
- return E_UNEXPECTED;
- }
-
- return proxy->QueryInterface(IID_IAccessibleText,
- getter_AddRefs(mAccTextProxy));
-}
-
-HRESULT
-AccessibleTextTearoff::ResolveAccHypertext()
-{
- if (mAccHypertextProxy) {
- return S_OK;
- }
-
- RefPtr<IUnknown> proxy(mHandler->GetProxy());
- if (!proxy) {
- return E_UNEXPECTED;
- }
-
- return proxy->QueryInterface(IID_IAccessibleHypertext,
- getter_AddRefs(mAccHypertextProxy));
-}
-
-IMPL_IUNKNOWN_QUERY_HEAD(AccessibleTextTearoff)
-IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleText)
-IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleHypertext)
-IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mHandler)
-
-HRESULT
-AccessibleTextTearoff::addSelection(long startOffset, long endOffset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->addSelection(startOffset, endOffset);
-}
-
-HRESULT
-AccessibleTextTearoff::get_attributes(long offset, long *startOffset,
- long *endOffset, BSTR *textAttributes)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_attributes(offset, startOffset, endOffset,
- textAttributes);
-}
-
-HRESULT
-AccessibleTextTearoff::get_caretOffset(long *offset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_caretOffset(offset);
-}
-
-HRESULT
-AccessibleTextTearoff::get_characterExtents(long offset,
- enum IA2CoordinateType coordType,
- long *x, long *y, long *width,
- long *height)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_characterExtents(offset, coordType, x, y, width,
- height);
-}
-
-HRESULT
-AccessibleTextTearoff::get_nSelections(long *nSelections)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_nSelections(nSelections);
-}
-
-HRESULT
-AccessibleTextTearoff::get_offsetAtPoint(long x, long y,
- enum IA2CoordinateType coordType,
- long *offset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_offsetAtPoint(x, y, coordType, offset);
-}
-
-HRESULT
-AccessibleTextTearoff::get_selection(long selectionIndex, long *startOffset,
- long *endOffset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_selection(selectionIndex, startOffset, endOffset);
-}
-
-HRESULT
-AccessibleTextTearoff::get_text(long startOffset, long endOffset, BSTR *text)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_text(startOffset, endOffset, text);
-}
-
-HRESULT
-AccessibleTextTearoff::get_textBeforeOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_textBeforeOffset(offset, boundaryType, startOffset,
- endOffset, text);
-}
-
-HRESULT
-AccessibleTextTearoff::get_textAfterOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_textAfterOffset(offset, boundaryType,
- startOffset, endOffset, text);
-}
-
-HRESULT
-AccessibleTextTearoff::get_textAtOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_textAtOffset(offset, boundaryType, startOffset,
- endOffset, text);
-}
-
-HRESULT
-AccessibleTextTearoff::removeSelection(long selectionIndex)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->removeSelection(selectionIndex);
-}
-
-HRESULT
-AccessibleTextTearoff::setCaretOffset(long offset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->setCaretOffset(offset);
-}
-
-HRESULT
-AccessibleTextTearoff::setSelection(long selectionIndex, long startOffset,
- long endOffset)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->setSelection(selectionIndex, startOffset, endOffset);
-}
-
-HRESULT
-AccessibleTextTearoff::get_nCharacters(long *nCharacters)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->get_nCharacters(nCharacters);
-}
-
-HRESULT
-AccessibleTextTearoff::scrollSubstringTo(long startIndex, long endIndex,
- enum IA2ScrollType scrollType)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->scrollSubstringTo(startIndex, endIndex, scrollType);
-}
-
-HRESULT
-AccessibleTextTearoff::scrollSubstringToPoint(long startIndex, long endIndex,
- enum IA2CoordinateType coordinateType,
- long x, long y)
-{
- HRESULT hr = ResolveAccText();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccTextProxy->scrollSubstringToPoint(startIndex, endIndex,
- coordinateType, x, y);
-}
-
-HRESULT
-AccessibleTextTearoff::get_newText(IA2TextSegment *newText)
-{
- if (!newText) {
- return E_INVALIDARG;
- }
-
- RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
- MOZ_ASSERT(ctl);
- if (!ctl) {
- return S_OK;
- }
-
- long id;
- HRESULT hr = mHandler->get_uniqueID(&id);
- if (FAILED(hr)) {
- return hr;
- }
-
- return ctl->GetNewText(id, WrapNotNull(newText));
-}
-
-HRESULT
-AccessibleTextTearoff::get_oldText(IA2TextSegment *oldText)
-{
- if (!oldText) {
- return E_INVALIDARG;
- }
-
- RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
- MOZ_ASSERT(ctl);
- if (!ctl) {
- return S_OK;
- }
-
- long id;
- HRESULT hr = mHandler->get_uniqueID(&id);
- if (FAILED(hr)) {
- return hr;
- }
-
- return ctl->GetOldText(id, WrapNotNull(oldText));
-}
-
-HRESULT
-AccessibleTextTearoff::get_nHyperlinks(long *hyperlinkCount)
-{
- HRESULT hr = ResolveAccHypertext();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccHypertextProxy->get_nHyperlinks(hyperlinkCount);
-}
-
-HRESULT
-AccessibleTextTearoff::get_hyperlink(long index,
- IAccessibleHyperlink **hyperlink)
-{
- HRESULT hr = ResolveAccHypertext();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccHypertextProxy->get_hyperlink(index, hyperlink);
-}
-
-HRESULT
-AccessibleTextTearoff::get_hyperlinkIndex(long charIndex, long *hyperlinkIndex)
-{
- HRESULT hr = ResolveAccHypertext();
- if (FAILED(hr)) {
- return hr;
- }
-
- return mAccHypertextProxy->get_hyperlinkIndex(charIndex, hyperlinkIndex);
-}
-
-
-} // namespace a11y
-} // namespace mozilla
deleted file mode 100644
--- a/accessible/ipc/win/handler/AccessibleTextTearoff.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- 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/. */
-
-#if defined(MOZILLA_INTERNAL_API)
-#error This code is NOT for internal Gecko use!
-#endif // defined(MOZILLA_INTERNAL_API)
-
-#ifndef mozilla_a11y_AccessibleTextTearoff_h
-#define mozilla_a11y_AccessibleTextTearoff_h
-
-#include "AccessibleHandler.h"
-#include "AccessibleHypertext.h"
-#include "IUnknownImpl.h"
-#include "mozilla/RefPtr.h"
-
-namespace mozilla {
-namespace a11y {
-
-class AccessibleTextTearoff final : public IAccessibleHypertext
-{
-public:
- explicit AccessibleTextTearoff(AccessibleHandler* aHandler);
-
- DECL_IUNKNOWN
-
- // IAccessibleText
- STDMETHODIMP addSelection(long startOffset, long endOffset) override;
- STDMETHODIMP get_attributes(long offset, long *startOffset, long *endOffset,
- BSTR *textAttributes) override;
- STDMETHODIMP get_caretOffset(long *offset) override;
- STDMETHODIMP get_characterExtents(long offset,
- enum IA2CoordinateType coordType, long *x,
- long *y, long *width, long *height) override;
- STDMETHODIMP get_nSelections(long *nSelections) override;
- STDMETHODIMP get_offsetAtPoint(long x, long y,
- enum IA2CoordinateType coordType,
- long *offset) override;
- STDMETHODIMP get_selection(long selectionIndex, long *startOffset,
- long *endOffset) override;
- STDMETHODIMP get_text(long startOffset, long endOffset, BSTR *text) override;
- STDMETHODIMP get_textBeforeOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text) override;
- STDMETHODIMP get_textAfterOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text) override;
- STDMETHODIMP get_textAtOffset(long offset,
- enum IA2TextBoundaryType boundaryType,
- long *startOffset, long *endOffset,
- BSTR *text) override;
- STDMETHODIMP removeSelection(long selectionIndex) override;
- STDMETHODIMP setCaretOffset(long offset) override;
- STDMETHODIMP setSelection(long selectionIndex, long startOffset,
- long endOffset) override;
- STDMETHODIMP get_nCharacters(long *nCharacters) override;
- STDMETHODIMP scrollSubstringTo(long startIndex, long endIndex,
- enum IA2ScrollType scrollType) override;
- STDMETHODIMP scrollSubstringToPoint(long startIndex, long endIndex,
- enum IA2CoordinateType coordinateType,
- long x, long y) override;
- STDMETHODIMP get_newText(IA2TextSegment *newText) override;
- STDMETHODIMP get_oldText(IA2TextSegment *oldText) override;
-
- // IAccessibleHypertext
- STDMETHODIMP get_nHyperlinks(long *hyperlinkCount) override;
- STDMETHODIMP get_hyperlink(long index,
- IAccessibleHyperlink **hyperlink) override;
- STDMETHODIMP get_hyperlinkIndex(long charIndex, long *hyperlinkIndex) override;
-
-private:
- ~AccessibleTextTearoff() = default;
- HRESULT ResolveAccText();
- HRESULT ResolveAccHypertext();
-
- RefPtr<AccessibleHandler> mHandler;
- RefPtr<IAccessibleText> mAccTextProxy;
- RefPtr<IAccessibleHypertext> mAccHypertextProxy;
-};
-
-} // namespace a11y
-} // namespace mozilla
-
-#endif // mozilla_a11y_AccessibleTextTearoff_h
--- a/accessible/ipc/win/handler/HandlerData.idl
+++ b/accessible/ipc/win/handler/HandlerData.idl
@@ -2,25 +2,69 @@
/* 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 "mozilla-config.h"
#include "AccessibleHandler.h"
+import "oaidl.idl";
import "ocidl.idl";
import "ServProv.idl";
-import "AccessibleText.idl";
+import "Accessible2_3.idl";
+import "AccessibleHypertext2.idl";
+import "AccessibleHyperlink.idl";
+import "AccessibleTable.idl";
+import "AccessibleTable2.idl";
+import "AccessibleTableCell.idl";
+
+typedef struct _StaticIA2Data
+{
+ NEWEST_IA2_INTERFACE* mIA2;
+ IEnumVARIANT* mIEnumVARIANT;
+ IAccessibleHypertext2* mIAHypertext;
+ IAccessibleHyperlink* mIAHyperlink;
+ IAccessibleTable* mIATable;
+ IAccessibleTable2* mIATable2;
+ IAccessibleTableCell* mIATableCell;
+} StaticIA2Data;
-typedef struct _IA2Data
+typedef struct _DynamicIA2Data
{
- long mUniqueId;
-} IA2Data;
+ // From IAccessible/IAccessible2
+ VARIANT mRole;
+ long mState;
+ long mChildCount;
+ long mIA2Role;
+ AccessibleStates mIA2States;
+ long mLeft;
+ long mTop;
+ long mWidth;
+ long mHeight;
+ long mHwnd;
+ BSTR mKeyboardShortcut;
+ BSTR mName;
+ BSTR mDescription;
+ BSTR mDefaultAction;
+ BSTR mValue;
+ BSTR mAttributes;
+ IA2Locale mIA2Locale;
+ // From IAccessibleAction
+ long mNActions;
+ // From IAccessibleTableCell
+ long mRowIndex;
+ long mColumnIndex;
+ long mRowExtent;
+ long mColumnExtent;
+ boolean mCellIsSelected;
+ // From IAccessible2
+ long mUniqueId;
+} DynamicIA2Data;
interface IGeckoBackChannel;
// We define different CLSIDs and IIDs depending on channel and officiality.
// This prevents handlers from installing overtop one another when multiple
// channels are present. Note that we do not do this for all UUIDs in this IDL,
// just the ones that are written to the registry (coclass and interfaces that
// have the [object] annotation)
@@ -78,17 +122,18 @@ interface IGeckoBackChannel;
#endif
[uuid(2b0e83b3-fd1a-443f-9ed6-c00d39055b58)]
interface HandlerData
{
typedef struct _IA2Payload
{
- IA2Data mData;
+ StaticIA2Data mStaticData;
+ DynamicIA2Data mDynamicData;
IGeckoBackChannel* mGeckoBackChannel;
} IA2Payload;
}
[object,
uuid(IHANDLERCONTROL_IID),
async_uuid(ASYNCIHANDLERCONTROL_IID),
pointer_default(unique)]
@@ -101,17 +146,22 @@ interface IHandlerControl : IUnknown
}
[object,
uuid(IGECKOBACKCHANNEL_IID),
pointer_default(unique)]
interface IGeckoBackChannel : IUnknown
{
[propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl);
- HRESULT Refresh([out] IA2Data* aOutData);
+ HRESULT Refresh([out] DynamicIA2Data* aOutData);
+ [propget] HRESULT AllTextInfo([out] BSTR* aText,
+ [out, size_is(,*aNHyperlinks)] IAccessibleHyperlink*** aHyperlinks,
+ [out] long* aNHyperlinks,
+ [out, size_is(,*aNAttribRuns)] IA2TextSegment** aAttribRuns,
+ [out] long* aNAttribRuns);
}
[uuid(1e545f07-f108-4912-9471-546827a80983)]
library AccessibleHandlerTypeLib
{
/**
* This definition is required in order for the handler implementation to
* support IDispatch (aka Automation). This is used by interpreted language
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerDataCleanup.h
@@ -0,0 +1,85 @@
+/* -*- 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/. */
+
+#ifndef mozilla_a11y_HandlerDataCleanup_h
+#define mozilla_a11y_HandlerDataCleanup_h
+
+#include <OleAuto.h>
+#include "HandlerData.h"
+
+namespace mozilla {
+namespace a11y {
+
+inline void
+ReleaseStaticIA2DataInterfaces(StaticIA2Data& aData)
+{
+ // Only interfaces of the proxied object wrapped by this handler should be
+ // released here, never other objects!
+ // For example, if StaticIA2Data were to include accParent in future,
+ // that must not be released here.
+ if (aData.mIA2) {
+ aData.mIA2->Release();
+ }
+ if (aData.mIEnumVARIANT) {
+ aData.mIEnumVARIANT->Release();
+ }
+ if (aData.mIAHypertext) {
+ aData.mIAHypertext->Release();
+ }
+ if (aData.mIAHyperlink) {
+ aData.mIAHyperlink->Release();
+ }
+ if (aData.mIATable) {
+ aData.mIATable->Release();
+ }
+ if (aData.mIATable2) {
+ aData.mIATable2->Release();
+ }
+ if (aData.mIATableCell) {
+ aData.mIATableCell->Release();
+ }
+}
+
+inline void
+CleanupDynamicIA2Data(DynamicIA2Data& aData, bool aZero=true)
+{
+ ::VariantClear(&aData.mRole);
+ if (aData.mKeyboardShortcut) {
+ ::SysFreeString(aData.mKeyboardShortcut);
+ }
+ if (aData.mName) {
+ ::SysFreeString(aData.mName);
+ }
+ if (aData.mDescription) {
+ ::SysFreeString(aData.mDescription);
+ }
+ if (aData.mDefaultAction) {
+ ::SysFreeString(aData.mDefaultAction);
+ }
+ if (aData.mValue) {
+ ::SysFreeString(aData.mValue);
+ }
+ if (aData.mAttributes) {
+ ::SysFreeString(aData.mAttributes);
+ }
+ if (aData.mIA2Locale.language) {
+ ::SysFreeString(aData.mIA2Locale.language);
+ }
+ if (aData.mIA2Locale.country) {
+ ::SysFreeString(aData.mIA2Locale.country);
+ }
+ if (aData.mIA2Locale.variant) {
+ ::SysFreeString(aData.mIA2Locale.variant);
+ }
+ if (aZero) {
+ ZeroMemory(&aData, sizeof(DynamicIA2Data));
+ }
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerDataCleanup_h
--- a/accessible/ipc/win/handler/moz.build
+++ b/accessible/ipc/win/handler/moz.build
@@ -1,31 +1,33 @@
# -*- 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/.
SharedLibrary('AccessibleHandler')
-EXPORTS.mozilla.a11y += ['AccessibleHandler.h']
+EXPORTS.mozilla.a11y += [
+ 'AccessibleHandler.h',
+ 'HandlerDataCleanup.h',
+]
LOCAL_INCLUDES += [
'/accessible/interfaces/ia2',
'/ipc/mscom/oop',
]
SOURCES += [
'!dlldata.c',
'!HandlerData_c.c',
'!HandlerData_i.c',
'!HandlerData_p.c',
'AccessibleHandler.cpp',
'AccessibleHandlerControl.cpp',
- 'AccessibleTextTearoff.cpp',
]
GENERATED_FILES += [
'dlldata.c',
'HandlerData.h',
'HandlerData.tlb',
'HandlerData_c.c',
'HandlerData_i.c',
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -293,17 +293,17 @@ this.AccessFu = { // jshint ignore:line
_handleMessageManager: function _handleMessageManager(aMessageManager) {
if (this._enabled) {
this._addMessageListeners(aMessageManager);
}
this._loadFrameScript(aMessageManager);
},
- onEvent: function(event, data, callback) {
+ onEvent(event, data, callback) {
switch (event) {
case "Accessibility:Settings":
this._systemPref = data.enabled;
this._enableOrDisable();
break;
case "Accessibility:NextObject":
case "Accessibility:PreviousObject": {
let rule = "Simple";
@@ -420,18 +420,17 @@ this.AccessFu = { // jshint ignore:line
/**
* Adjusts the given bounds relative to the given browser.
* @param {Rect} aJsonBounds the bounds to adjust
* @param {browser} aBrowser the browser we want the bounds relative to
* @param {bool} aToCSSPixels whether to convert to CSS pixels (as opposed to
* device pixels)
*/
- adjustContentBounds:
- function(aJsonBounds, aBrowser, aToCSSPixels) {
+ adjustContentBounds(aJsonBounds, aBrowser, aToCSSPixels) {
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top);
let win = Utils.win;
let dpr = win.devicePixelRatio;
let offset = { left: -win.mozInnerScreenX, top: -win.mozInnerScreenY };
// Add the offset; the offset is in CSS pixels, so multiply the
@@ -874,26 +873,26 @@ var Input = {
},
activateCurrent: function activateCurrent(aData, aActivateIfKey = false) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
let offset = aData && typeof aData.keyIndex === "number" ?
aData.keyIndex - Output.brailleState.startOffset : -1;
mm.sendAsyncMessage("AccessFu:Activate",
- {offset: offset, activateIfKey: aActivateIfKey});
+ {offset, activateIfKey: aActivateIfKey});
},
sendContextMenuMessage: function sendContextMenuMessage() {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage("AccessFu:ContextMenu", {});
},
setEditState: function setEditState(aEditState) {
- Logger.debug(() => { return ["setEditState", JSON.stringify(aEditState)] });
+ Logger.debug(() => { return ["setEditState", JSON.stringify(aEditState)]; });
this.editState = aEditState;
},
// XXX: This is here for backwards compatability with screen reader simulator
// it should be removed when the extension is updated on amo.
scroll: function scroll(aPage, aHorizontal) {
this.sendScrollMessage(aPage, aHorizontal);
},
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -234,17 +234,17 @@ this.EventManager.prototype = {
Ci.nsIAccessibleObjectAttributeChangedEvent);
if (evt.changedAttribute !== "aria-hidden") {
// Only handle aria-hidden attribute change.
break;
}
let hidden = Utils.isHidden(aEvent.accessible);
this[hidden ? "_handleHide" : "_handleShow"](evt);
if (this.inTest) {
- this.sendMsgFunc("AccessFu:AriaHidden", { hidden: hidden });
+ this.sendMsgFunc("AccessFu:AriaHidden", { hidden });
}
break;
}
case Events.SHOW:
{
this._handleShow(aEvent);
break;
}
@@ -475,17 +475,17 @@ this.EventManager.prototype = {
return liveAttrs;
}
let parent = aEvent.targetParent;
while (parent) {
liveAttrs = parseLiveAttrs(parent);
if (liveAttrs) {
return liveAttrs;
}
- parent = parent.parent
+ parent = parent.parent;
}
return {};
};
let {live, relevant, /* busy, atomic, memberOf */ } = getLiveAttributes(aEvent);
// If container-live is not present or is set to |off| ignore the event.
if (!live || live === "off") {
return {};
}
@@ -507,17 +507,17 @@ this.EventManager.prototype = {
let domNode = aLiveRegion.DOMNode;
if (this._liveEventQueue && this._liveEventQueue.has(domNode)) {
let queue = this._liveEventQueue.get(domNode);
let nextEvent = queue[0];
if (nextEvent.eventType === aEventType) {
Utils.win.clearTimeout(nextEvent.timeoutID);
queue.shift();
if (queue.length === 0) {
- this._liveEventQueue.delete(domNode)
+ this._liveEventQueue.delete(domNode);
}
}
}
},
_queueLiveEvent: function _queueLiveEvent(aEventType, aLiveRegion, aIsPolite, aModifiedText) {
if (!this._liveEventQueue) {
this._liveEventQueue = new WeakMap();
--- a/accessible/jsat/Gestures.jsm
+++ b/accessible/jsat/Gestures.jsm
@@ -300,17 +300,17 @@ function compileDetail(aType, aPoints, k
maxDeltaY = deltaY;
}
// Since the gesture is resolving, reset the points' distance information
// since they are passed to the next potential gesture.
point.reset();
}
return {
type: aType,
- touches: touches,
+ touches,
deltaX: maxDeltaX,
deltaY: maxDeltaY
};
}
/**
* A general gesture object.
* @param {Number} aTimeStamp An original pointer event's timeStamp that started
--- a/accessible/jsat/OutputGenerator.jsm
+++ b/accessible/jsat/OutputGenerator.jsm
@@ -172,20 +172,23 @@ var OutputGenerator = {
}
let description = aAccessible.description;
if (description) {
// Compare against the calculated name unconditionally, regardless of name rule,
// so we can make sure we don't speak duplicated descriptions
let tmpName = name || aAccessible.name;
if (tmpName && (description !== tmpName)) {
- name = name || "";
- name = this.outputOrder === OUTPUT_DESC_FIRST ?
- description + " - " + name :
- name + " - " + description;
+ if (name) {
+ name = this.outputOrder === OUTPUT_DESC_FIRST ?
+ description + " - " + name :
+ name + " - " + description;
+ } else {
+ name = description;
+ }
}
}
if (!name || !name.trim()) {
return;
}
aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? "push" : "unshift"](name);
},
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -164,17 +164,17 @@ VisualPresenter.prototype.viewportChange
if (Utils.isAliveAndVisible(currentAcc)) {
let bounds = (start === -1 && end === -1) ? Utils.getBounds(currentAcc) :
Utils.getTextBounds(currentAcc, start, end);
return {
type: this.type,
details: {
eventType: "viewport-change",
- bounds: bounds,
+ bounds,
padding: this.BORDER_PADDING
}
};
}
return null;
};
@@ -193,17 +193,17 @@ VisualPresenter.prototype.pivotChanged =
aContext.bounds : Utils.getTextBounds(aContext.accessibleForBounds,
aContext.startOffset,
aContext.endOffset);
return {
type: this.type,
details: {
eventType: "vc-change",
- bounds: bounds,
+ bounds,
padding: this.BORDER_PADDING
}
};
} catch (e) {
Logger.logException(e, "Failed to get bounds");
return null;
}
};
@@ -292,17 +292,17 @@ AndroidPresenter.prototype.pivotChanged
androidEvents.push({eventType: (isExploreByTouch) ?
this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
text: Utils.localize(UtteranceGenerator.genForContext(
aContext)),
bounds: aContext.bounds,
clickable: aContext.accessible.actionCount > 0,
checkable: state.contains(States.CHECKABLE),
checked: state.contains(States.CHECKED),
- brailleOutput: brailleOutput});
+ brailleOutput});
}
return {
type: this.type,
details: androidEvents
};
};
@@ -318,17 +318,17 @@ AndroidPresenter.prototype.actionInvoked
text = Utils.localize(UtteranceGenerator.genForAction(aObject,
aActionName));
}
return {
type: this.type,
details: [{
eventType: this.ANDROID_VIEW_CLICKED,
- text: text,
+ text,
checked: state.contains(States.CHECKED)
}]
};
};
AndroidPresenter.prototype.tabSelected =
function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
// Send a pivot change message with the full context utterance for this doc.
@@ -377,17 +377,17 @@ AndroidPresenter.prototype.textSelection
aText, aStart, aEnd, aOldStart, aOldEnd, aIsFromUserInput).details;
androidEvents.push({
eventType: this.ANDROID_VIEW_TEXT_SELECTION_CHANGED,
text: [aText],
fromIndex: aStart,
toIndex: aEnd,
itemCount: aText.length,
- brailleOutput: brailleOutput
+ brailleOutput
});
}
if (Utils.AndroidSdkVersion >= 16 && aIsFromUserInput) {
let [from, to] = aOldStart < aStart ?
[aOldStart, aStart] : [aStart, aOldStart];
androidEvents.push({
eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
--- a/accessible/jsat/Traversal.jsm
+++ b/accessible/jsat/Traversal.jsm
@@ -8,23 +8,23 @@
const Ci = Components.interfaces;
const Cu = Components.utils;
this.EXPORTED_SYMBOLS = ["TraversalRules", "TraversalHelper"]; // jshint ignore:line
Cu.import("resource://gre/modules/accessibility/Utils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Roles", // jshint ignore:line
+XPCOMUtils.defineLazyModuleGetter(this, "Roles", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Filters", // jshint ignore:line
+XPCOMUtils.defineLazyModuleGetter(this, "Filters", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "States", // jshint ignore:line
+XPCOMUtils.defineLazyModuleGetter(this, "States", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Prefilters", // jshint ignore:line
+XPCOMUtils.defineLazyModuleGetter(this, "Prefilters", // jshint ignore:line
"resource://gre/modules/accessibility/Constants.jsm");
var gSkipEmptyImages = new PrefCache("accessibility.accessfu.skip_empty_images");
function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter, aContainerRule) {
this._explicitMatchRoles = new Set(aRoles);
this._matchRoles = aRoles;
if (aRoles.length) {
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -30,15 +30,16 @@ if CONFIG['MOZ_XUL']:
TEST_DIRS += ['tests/mochitest']
BROWSER_CHROME_MANIFESTS += [
'tests/browser/bounds/browser.ini',
'tests/browser/browser.ini',
'tests/browser/e10s/browser.ini',
'tests/browser/events/browser.ini',
+ 'tests/browser/general/browser.ini',
'tests/browser/scroll/browser.ini',
'tests/browser/states/browser.ini',
'tests/browser/tree/browser.ini'
]
with Files("**"):
BUG_COMPONENT = ("Core", "Disability Access APIs")
--- a/accessible/other/moz.build
+++ b/accessible/other/moz.build
@@ -18,10 +18,10 @@ LOCAL_INCLUDES += [
'/accessible/base',
'/accessible/generic',
'/accessible/html',
'/accessible/xul',
]
FINAL_LIBRARY = 'xul'
-if CONFIG['GNU_CXX']:
+if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']
--- a/accessible/tests/browser/.eslintrc.js
+++ b/accessible/tests/browser/.eslintrc.js
@@ -21,17 +21,17 @@ module.exports = {
"curly": ["error", "multi-line"],
"default-case": "off",
"dot-location": ["error", "property"],
"eqeqeq": "off",
"func-names": "off",
"func-style": "off",
"handle-callback-err": ["error", "er"],
- "indent": ["error", 2, {"SwitchCase": 1}],
+ "indent-legacy": ["error", 2, {"SwitchCase": 1}],
"max-nested-callbacks": ["error", 4],
"max-params": "off",
"max-statements": "off",
"new-cap": ["error", {"capIsNew": false}],
"new-parens": "error",
"no-bitwise": "off",
"no-catch-shadow": "error",
"no-comma-dangle": "off",
@@ -71,17 +71,16 @@ module.exports = {
"no-use-before-define": "off",
"no-var": "off",
"no-warning-comments": "off",
"object-shorthand": "off",
"one-var": ["error", "never"],
"padded-blocks": ["error", "never"],
"quote-props": "off",
"radix": "error",
- "semi": ["error", "always"],
"semi-spacing": ["error", {"before": false, "after": true}],
"sort-vars": "off",
"space-in-brackets": "off",
"space-in-parens": ["error", "never"],
"space-unary-word-ops": "off",
"strict": ["error", "global"],
"valid-jsdoc": "off",
"vars-on-top": "off",
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -7,16 +7,17 @@ support-files =
[browser_shutdown_acc_reference.js]
[browser_shutdown_doc_acc_reference.js]
[browser_shutdown_multi_acc_reference_obj.js]
[browser_shutdown_multi_acc_reference_doc.js]
[browser_shutdown_multi_reference.js]
[browser_shutdown_parent_own_reference.js]
skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_pref.js]
[browser_shutdown_proxy_acc_reference.js]
skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
[browser_shutdown_proxy_doc_acc_reference.js]
skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
[browser_shutdown_multi_proxy_acc_reference_doc.js]
skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
[browser_shutdown_multi_proxy_acc_reference_obj.js]
skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_pref.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const PREF_ACCESSIBILITY_FORCE_DISABLED = "accessibility.force_disabled";
+
+add_task(async function testForceDisable() {
+ ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled by default");
+
+ info("Reset force disabled preference");
+ Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
+
+ info("Enable accessibility service via XPCOM");
+ let a11yInit = initPromise();
+ let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService);
+ await a11yInit;
+ ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled");
+
+ info("Force disable a11y service via preference");
+ let a11yShutdown = shutdownPromise();
+ Services.prefs.setIntPref(PREF_ACCESSIBILITY_FORCE_DISABLED, 1);
+ await a11yShutdown;
+ ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
+
+ info("Attempt to get an instance of a11y service and call its method.");
+ accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService);
+ try {
+ accService.getAccesssibleFor(document);
+ ok(false, "getAccesssibleFor should've triggered an exception.");
+ } catch (e) {
+ ok(true, "getAccesssibleFor triggers an exception as a11y service is shutdown.");
+ }
+ ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
+
+ info("Reset force disabled preference");
+ Services.prefs.clearUserPref(PREF_ACCESSIBILITY_FORCE_DISABLED);
+
+ info("Create a11y service again");
+ a11yInit = initPromise();
+ accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService);
+ await a11yInit;
+ ok(Services.appinfo.accessibilityEnabled, "Accessibility is enabled");
+
+ info("Remove all references to a11y service");
+ a11yShutdown = shutdownPromise();
+ accService = null;
+ forceGC();
+ await a11yShutdown;
+ ok(!Services.appinfo.accessibilityEnabled, "Accessibility is disabled");
+});
--- a/accessible/tests/browser/browser_shutdown_remote_no_reference.js
+++ b/accessible/tests/browser/browser_shutdown_remote_no_reference.js
@@ -20,29 +20,51 @@ add_task(async function() {
</html>`
}, async function(browser) {
info("Creating a service in parent and waiting for service to be created " +
"in content");
// Create a11y service in the main process. This will trigger creating of
// the a11y service in parent as well.
let parentA11yInit = initPromise();
let contentA11yInit = initPromise(browser);
+ let parentConsumersChanged = a11yConsumersChangedPromise();
+ let contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
Ci.nsIAccessibilityService);
ok(accService, "Service initialized in parent");
await Promise.all([parentA11yInit, contentA11yInit]);
+ await parentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: true, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers in parent are correct."));
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: false, MainProcess: true, PlatformAPI: false
+ }, "Accessibility service consumers in content are correct."));
+
+ Assert.deepEqual(JSON.parse(accService.getConsumers()), {
+ XPCOM: true, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers in parent are correct.");
info("Removing a service in parent and waiting for service to be shut " +
"down in content");
// Remove a11y service reference in the main process.
let parentA11yShutdown = shutdownPromise();
let contentA11yShutdown = shutdownPromise(browser);
+ parentConsumersChanged = a11yConsumersChangedPromise();
+ contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
accService = null;
ok(!accService, "Service is removed in parent");
// Force garbage collection that should trigger shutdown in both main and
// content process.
forceGC();
await Promise.all([parentA11yShutdown, contentA11yShutdown]);
+ await parentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: false, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers are correct."));
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: false, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers are correct."));
});
// Unsetting e10s related preferences.
await unsetE10sPrefs();
});
--- a/accessible/tests/browser/browser_shutdown_remote_own_reference.js
+++ b/accessible/tests/browser/browser_shutdown_remote_own_reference.js
@@ -20,56 +20,82 @@ add_task(async function() {
</html>`
}, async function(browser) {
info("Creating a service in parent and waiting for service to be created " +
"in content");
// Create a11y service in the main process. This will trigger creating of
// the a11y service in parent as well.
let parentA11yInit = initPromise();
let contentA11yInit = initPromise(browser);
+ let contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
Ci.nsIAccessibilityService);
ok(accService, "Service initialized in parent");
await Promise.all([parentA11yInit, contentA11yInit]);
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: false, MainProcess: true, PlatformAPI: false
+ }, "Accessibility service consumers in content are correct."));
info("Adding additional reference to accessibility service in content " +
"process");
+ contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
// Add a new reference to the a11y service inside the content process.
loadFrameScripts(browser, `let accService = Components.classes[
'@mozilla.org/accessibilityService;1'].getService(
Components.interfaces.nsIAccessibilityService);`);
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: true, MainProcess: true, PlatformAPI: false
+ }, "Accessibility service consumers in content are correct."));
+
+ const contentConsumers = await ContentTask.spawn(browser, {}, () =>
+ accService.getConsumers());
+ Assert.deepEqual(JSON.parse(contentConsumers), {
+ XPCOM: true, MainProcess: true, PlatformAPI: false
+ }, "Accessibility service consumers in parent are correct.");
info("Shutting down a service in parent and making sure the one in " +
"content stays alive");
let contentCanShutdown = false;
let parentA11yShutdown = shutdownPromise();
+ contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
// This promise will resolve only if contentCanShutdown flag is set to true.
// If 'a11y-init-or-shutdown' event with '0' flag (in content) comes before
// it can be shut down, the promise will reject.
let contentA11yShutdown = new Promise((resolve, reject) =>
shutdownPromise(browser).then(flag => contentCanShutdown ?
resolve() : reject("Accessible service was shut down incorrectly")));
// Remove a11y service reference in the main process and force garbage
// collection. This should not trigger shutdown in content since a11y
// service is used by XPCOM.
accService = null;
ok(!accService, "Service is removed in parent");
// Force garbage collection that should not trigger shutdown because there
// is a reference in a content process.
forceGC();
loadFrameScripts(browser, `Components.utils.forceGC();`);
await parentA11yShutdown;
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: true, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers in content are correct."));
// Have some breathing room between a11y service shutdowns.
await new Promise(resolve => executeSoon(resolve));
info("Removing a service in content");
// Now allow a11y service to shutdown in content.
contentCanShutdown = true;
+ contentConsumersChanged =
+ ContentTask.spawn(browser, {}, a11yConsumersChangedPromise);
// Remove last reference to a11y service in content and force garbage
// collection that should trigger shutdown.
loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
await contentA11yShutdown;
+ await contentConsumersChanged.then(data => Assert.deepEqual(data, {
+ XPCOM: false, MainProcess: false, PlatformAPI: false
+ }, "Accessibility service consumers in content are correct."));
// Unsetting e10s related preferences.
await unsetE10sPrefs();
});
});
--- a/accessible/tests/browser/events/browser_test_docload.js
+++ b/accessible/tests/browser/events/browser_test_docload.js
@@ -43,22 +43,22 @@ async function runTests(browser, accDoc)
browser.loadURI(`data:text/html;charset=utf-8,
<html><body id="body2">
<iframe id="iframe1" src="http://example.com"></iframe>
</body></html>`);
await onLoadEvents;
onLoadEvents = waitForEvents([
- [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:")],
+ [EVENT_DOCUMENT_LOAD_COMPLETE, urlChecker("about:about")],
[EVENT_STATE_CHANGE, busyChecker(false)],
[EVENT_REORDER, getAccessible(browser)]
]);
- browser.loadURI("about:");
+ browser.loadURI("about:about");
await onLoadEvents;
onLoadEvents = waitForEvents([
[EVENT_DOCUMENT_RELOAD, evt => evt.isFromUserInput],
[EVENT_REORDER, getAccessible(browser)],
[EVENT_STATE_CHANGE, busyChecker(false)]
]);
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/general/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+ !/accessible/tests/browser/shared-head.js
+ head.js
+ !/accessible/tests/mochitest/*.js
+
+[browser_test_doc_creation.js]
+[browser_test_urlbar.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/general/browser_test_doc_creation.js
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const tab1URL = `data:text/html,
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>First tab to be loaded</title>
+ </head>
+ <body>
+ <butotn>JUST A BUTTON</butotn>
+ </body>
+ </html>`;
+
+const tab2URL = `data:text/html,
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Second tab to be loaded</title>
+ </head>
+ <body>
+ <butotn>JUST A BUTTON</butotn>
+ </body>
+ </html>`;
+
+// Checking that, if there are open windows before accessibility was started,
+// root accessibles for open windows are created so that all root accessibles
+// are stored in application accessible children array.
+add_task(async function testDocumentCreation() {
+ let tab1 = await openNewTab(tab1URL);
+ let tab2 = await openNewTab(tab2URL);
+ let accService = await initAccessibilityService(); // eslint-disable-line no-unused-vars
+
+ info("Verifying that each tab content document is in accessible cache.");
+ for (const browser of [...gBrowser.browsers]) {
+ await ContentTask.spawn(browser, null, async () => {
+ let accServiceContent =
+ Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService);
+ ok(!!accServiceContent.getAccessibleFromCache(content.document),
+ "Document accessible is in cache.");
+ });
+ }
+
+ await BrowserTestUtils.removeTab(tab1);
+ await BrowserTestUtils.removeTab(tab2);
+
+ accService = null;
+ await shutdownAccessibilityService();
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/general/browser_test_urlbar.js
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Checking that the awesomebar popup gets COMBOBOX_LIST role instead of
+// LISTBOX, since its parent is a <panel> (see Bug 1422465)
+add_task(async function testAutocompleteRichResult() {
+ let tab = await openNewTab("data:text/html;charset=utf-8,");
+ let accService = await initAccessibilityService();
+
+ info("Opening the URL bar and entering a key to show the PopupAutoCompleteRichResult panel");
+ let urlbar = document.getElementById("urlbar");
+ urlbar.focus();
+ let urlbarPopup = document.getElementById("PopupAutoCompleteRichResult");
+ let shown = BrowserTestUtils.waitForEvent(urlbarPopup, "popupshown");
+ EventUtils.synthesizeKey("a", {});
+ await shown;
+
+ info("Waiting for accessibility to be created for the richlistbox");
+ let richlistbox = document.getAnonymousElementByAttribute(urlbarPopup, "anonid", "richlistbox");
+ await BrowserTestUtils.waitForCondition(() => accService.getAccessibleFor(richlistbox));
+
+ info("Confirming that the special case is handled in XULListboxAccessible");
+ let accessible = accService.getAccessibleFor(richlistbox);
+ is(accessible.role, ROLE_COMBOBOX_LIST, "Right role");
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
+registerCleanupFunction(async function() {
+ await shutdownAccessibilityService();
+});
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/general/head.js
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* exported initAccessibilityService, openNewTab, shutdownAccessibilityService */
+
+// Load the shared-head file first.
+/* import-globals-from ../shared-head.js */
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
+ this);
+
+const nsIAccessibleRole = Ci.nsIAccessibleRole; // eslint-disable-line no-unused-vars
+
+/* import-globals-from ../../mochitest/role.js */
+loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
+
+async function openNewTab(url) {
+ const forceNewProcess = true;
+
+ return BrowserTestUtils.openNewForegroundTab(
+ { gBrowser, url, forceNewProcess });
+}
+
+async function initAccessibilityService() {
+ info("Create accessibility service.");
+ let accService = Cc["@mozilla.org/accessibilityService;1"].getService(
+ Ci.nsIAccessibilityService);
+
+ await new Promise(resolve => {
+ if (Services.appinfo.accessibilityEnabled) {
+ resolve();
+ return;
+ }
+
+ let observe = (subject, topic, data) => {
+ if (data === "1") {
+ Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
+ resolve();
+ }
+ };
+ Services.obs.addObserver(observe, "a11y-init-or-shutdown");
+ });
+
+ return accService;
+}
+
+function shutdownAccessibilityService() {
+ forceGC();
+
+ return new Promise(resolve => {
+ if (!Services.appinfo.accessibilityEnabled) {
+ resolve();
+ return;
+ }
+
+ let observe = (subject, topic, data) => {
+ if (data === "0") {
+ Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
+ resolve();
+ }
+ };
+ Services.obs.addObserver(observe, "a11y-init-or-shutdown");
+ });
+}
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/head.js
@@ -1,28 +1,27 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* exported initPromise, shutdownPromise, waitForEvent, setE10sPrefs,
- unsetE10sPrefs, forceGC */
+ unsetE10sPrefs, a11yConsumersChangedPromise */
/**
* Set e10s related preferences in the test environment.
* @return {Promise} promise that resolves when preferences are set.
*/
function setE10sPrefs() {
return new Promise(resolve =>
SpecialPowers.pushPrefEnv({
set: [
["browser.tabs.remote.autostart", true],
- ["browser.tabs.remote.force-enable", true],
- ["extensions.e10sBlocksEnabling", false]
+ ["browser.tabs.remote.force-enable", true]
]
}, resolve));
}
/**
* Unset e10s related preferences in the test environment.
* @return {Promise} promise that resolves when preferences are unset.
*/
@@ -34,16 +33,30 @@ function unsetE10sPrefs() {
// Load the shared-head file first.
/* import-globals-from shared-head.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
this);
/**
+ * Returns a promise that resolves when 'a11y-consumers-changed' event is fired.
+ * @return {Promise} event promise evaluating to event's data
+ */
+function a11yConsumersChangedPromise() {
+ return new Promise(resolve => {
+ let observe = (subject, topic, data) => {
+ Services.obs.removeObserver(observe, "a11y-consumers-changed");
+ resolve(JSON.parse(data));
+ };
+ Services.obs.addObserver(observe, "a11y-consumers-changed");
+ });
+}
+
+/**
* Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired.
* @return {Promise} event promise evaluating to event's data
*/
function a11yInitOrShutdownPromise() {
return new Promise(resolve => {
let observe = (subject, topic, data) => {
Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
resolve(data);
@@ -112,30 +125,24 @@ function shutdownPromise(contentBrowser)
* Simpler verions of waitForEvent defined in
* accessible/tests/browser/events.js
*/
function waitForEvent(eventType, expectedId) {
return new Promise(resolve => {
let eventObserver = {
observe(subject) {
let event = subject.QueryInterface(Ci.nsIAccessibleEvent);
+ let id;
+ try {
+ id = event.accessible.id;
+ } catch (e) {
+ // This can throw NS_ERROR_FAILURE.
+ }
if (event.eventType === eventType &&
- event.accessible.id === expectedId) {
+ id === expectedId) {
Services.obs.removeObserver(this, "accessible-event");
resolve(event);
}
}
};
Services.obs.addObserver(eventObserver, "accessible-event");
});
}
-
-/**
- * Force garbage collection.
- */
-function forceGC() {
- SpecialPowers.gc();
- SpecialPowers.forceShrinkingGC();
- SpecialPowers.forceCC();
- SpecialPowers.gc();
- SpecialPowers.forceShrinkingGC();
- SpecialPowers.forceCC();
-}
--- a/accessible/tests/browser/shared-head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -6,17 +6,17 @@
/* import-globals-from ../mochitest/common.js */
/* import-globals-from events.js */
/* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus,
invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName,
addAccessibleTask, findAccessibleChildByID, isDefunct,
CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, snippetToURL,
- Cc, Cu, arrayFromChildren */
+ Cc, Cu, arrayFromChildren, forceGC */
const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
/**
* Current browser test directory path used to load subscripts.
*/
const CURRENT_DIR =
"chrome://mochitests/content/browser/accessible/tests/browser/";
@@ -358,8 +358,20 @@ function queryInterfaces(accessible, int
return accessible;
}
function arrayFromChildren(accessible) {
return Array.from({ length: accessible.childCount }, (c, i) =>
accessible.getChildAt(i));
}
+
+/**
+ * Force garbage collection.
+ */
+function forceGC() {
+ SpecialPowers.gc();
+ SpecialPowers.forceShrinkingGC();
+ SpecialPowers.forceCC();
+ SpecialPowers.gc();
+ SpecialPowers.forceShrinkingGC();
+ SpecialPowers.forceCC();
+}
--- a/accessible/tests/browser/tree/browser_aria_owns.js
+++ b/accessible/tests/browser/tree/browser_aria_owns.js
@@ -213,8 +213,41 @@ addAccessibleTask(`<div id="a"></div><di
document.documentElement.getBoundingClientRect();
document.body.setAttribute("aria-owns", "b a");
document.documentElement.remove();
});
testChildrenIds(accDoc, []);
}
);
+
+// Don't allow ordinal child to be placed after aria-owned child (bug 1405796)
+addAccessibleTask(`<div id="container"><div id="a">Hello</div></div>
+ <div><div id="c">There</div><div id="d">There</div></div>`,
+ async function(browser, accDoc) {
+ let containerAcc = findAccessibleChildByID(accDoc, "container");
+
+ testChildrenIds(containerAcc, ["a"]);
+
+ await contentSpawnMutation(browser, MOVE, function() {
+ document.getElementById("container").setAttribute("aria-owns", "c");
+ });
+
+ testChildrenIds(containerAcc, ["a", "c"]);
+
+ await contentSpawnMutation(browser, MOVE, function() {
+ let span = document.createElement("span");
+ document.getElementById("container").appendChild(span);
+
+ let b = document.createElement("div");
+ b.id = "b";
+ document.getElementById("container").appendChild(b);
+ });
+
+ testChildrenIds(containerAcc, ["a", "b", "c"]);
+
+ await contentSpawnMutation(browser, MOVE, function() {
+ document.getElementById("container").setAttribute("aria-owns", "c d");
+ });
+
+ testChildrenIds(containerAcc, ["a", "b", "c", "d"]);
+ }
+);
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/1072792.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.body.appendChild(x);">
+<table><tbody><div id="x"></div></tbody></table>
+</body>
+</html>
--- a/accessible/tests/crashtests/471493.xul
+++ b/accessible/tests/crashtests/471493.xul
@@ -1,29 +1,49 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="bug 471493 'crash [@ nsPropertyTable::GetPropertyInternal]'"
- onload="doTest();">
+ onload="doTest();" class="reftest-wait">
<script type="application/javascript">
<![CDATA[
+ function addA11yLoadEvent(accService, func) {
+ function waitForDocLoad() {
+ setTimeout(() => {
+ let accDoc = accService.getAccessibleFor(document);
+ let state = {};
+ accDoc.getState(state, {});
+
+ if (state.value & SpecialPowers.Ci.nsIAccessibleStates.STATE_BUSY) {
+ return waitForDocLoad();
+ }
+ setTimeout(func, 0);
+ }, 0);
+ }
+
+ waitForDocLoad();
+ }
+
function doTest()
{
var accService = SpecialPowers.Cc["@mozilla.org/accessibilityService;1"].
getService(SpecialPowers.Ci.nsIAccessibilityService);
var treecol = document.getElementById("col");
var x = treecol.boxObject.screenX;
var y = treecol.boxObject.screenY;
var tree = document.getElementById("tree");
- var treeAcc = accService.getAccessibleFor(tree);
- treeAcc.getChildAtPoint(x + 1, y + 1);
+ addA11yLoadEvent(accService, () => {
+ var treeAcc = accService.getAccessibleFor(tree);
+ treeAcc.getChildAtPoint(x + 1, y + 1);
+ document.documentElement.removeAttribute("class");
+ });
}
]]>
</script>
<tree id="tree" flex="1">
<treecols>
<treecol id="col" flex="1" primary="true" label="column"/>
<treecol id="scol" flex="1" label="column 2"/>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/884202.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+function f()
+{
+ window.removeEventListener("pagehide", f, false);
+ var root = document.documentElement;
+ document.removeChild(root);
+ document.appendChild(root);
+}
+
+function boom()
+{
+ window.addEventListener("pagehide", f, false);
+ window.location = "data:text/html,4";
+}
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/890760.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ window.getSelection().collapse(document.createTextNode("."), 0);
+}
+
+</script>
+</head>
+<body onload="setTimeout(boom, 0);"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/893515.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+function boom()
+{
+ document.designMode = 'on';
+ var otherDoc = document.implementation.createDocument("", "", null);
+ otherDoc.adoptNode(document.getElementById("t"));
+}
+</script>
+</head>
+<body onload="setTimeout(boom, 0);"><textarea id="t"></textarea></body>
+</html>
--- a/accessible/tests/crashtests/crashtests.list
+++ b/accessible/tests/crashtests/crashtests.list
@@ -1,3 +1,10 @@
-# Disabled because they cause assertions/crashes in later crashtests, see bug 918246
-skip load 448064.xhtml
-skip load 471493.xul
+load 448064.xhtml # This test instantiates a11y, so be careful about adding tests before it
+load 471493.xul
+asserts-if(!browserIsRemote,2) load 884202.html
+load 890760.html
+load 893515.html
+load 1072792.xhtml
+
+# last_test_to_unload_testsuite.xul MUST be the last test in the list because it
+# is responsible for shutting down accessibility service affecting later tests.
+load last_test_to_unload_testsuite.xul
new file mode 100644
--- /dev/null
+++ b/accessible/tests/crashtests/last_test_to_unload_testsuite.xul
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="bug 471493 'crash [@ nsPropertyTable::GetPropertyInternal]'"
+ onload="shutdown();" class="reftest-wait">
+
+ <script type="application/javascript">
+ <![CDATA[
+ function shutdown() {
+ !SpecialPowers.Services.appinfo.accessibilityEnabled &&
+ dump("### Error : Accessibility is expected to be enabled.\n");
+
+ // Force garbage collection. We try really hard to garbage collect
+ // everythin here to ensure that all a11y xpcom bits are shut down and
+ // avoid intermittent timeouts.
+ SpecialPowers.gc();
+ SpecialPowers.forceShrinkingGC();
+ SpecialPowers.forceCC();
+ SpecialPowers.gc();
+ SpecialPowers.forceShrinkingGC();
+ SpecialPowers.forceCC();
+
+ if (SpecialPowers.Services.appinfo.accessibilityEnabled) {
+ let observe = (subject, topic, data) => {
+ SpecialPowers.Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
+ data === "0" && document.documentElement.removeAttribute("class");
+ };
+ SpecialPowers.Services.obs.addObserver(observe, "a11y-init-or-shutdown");
+ } else {
+ document.documentElement.removeAttribute("class");
+ }
+ }
+ ]]>
+ </script>
+</window>
--- a/accessible/tests/mochitest/.eslintrc.js
+++ b/accessible/tests/mochitest/.eslintrc.js
@@ -6,19 +6,12 @@ module.exports = {
],
"rules": {
"mozilla/no-cpows-in-tests": "error",
"mozilla/reject-importGlobalProperties": "error",
// XXX These are rules that are enabled in the recommended configuration, but
// disabled here due to failures when initially implemented. They should be
// removed (and hence enabled) at some stage.
- "comma-spacing": "off",
- "no-cond-assign": "off",
- "no-lonely-if": "off",
"no-nested-ternary": "off",
- "no-new-object": "off",
- "no-redeclare": "off",
- "no-shadow": "off",
"no-undef": "off",
- "space-unary-ops": "off",
}
};
--- a/accessible/tests/mochitest/actions.js
+++ b/accessible/tests/mochitest/actions.js
@@ -59,26 +59,26 @@ function testActions(aArray) {
var events = actionObj.events;
var accOrElmOrIDOfTarget = actionObj.targetID ?
actionObj.targetID : accOrElmOrID;
var eventSeq = [];
if (events) {
var elm = getNode(accOrElmOrIDOfTarget);
if (events & MOUSEDOWN_EVENT)
- eventSeq.push(new checkerOfActionInvoker("mousedown", elm));
+ eventSeq.push(new checkerOfActionInvoker("mousedown", elm, actionObj));
if (events & MOUSEUP_EVENT)
- eventSeq.push(new checkerOfActionInvoker("mouseup", elm));
+ eventSeq.push(new checkerOfActionInvoker("mouseup", elm, actionObj));
if (events & CLICK_EVENT)
eventSeq.push(new checkerOfActionInvoker("click", elm, actionObj));
if (events & COMMAND_EVENT)
- eventSeq.push(new checkerOfActionInvoker("command", elm));
+ eventSeq.push(new checkerOfActionInvoker("command", elm, actionObj));
if (events & FOCUS_EVENT)
eventSeq.push(new focusChecker(elm));
}
if (actionObj.eventSeq)
eventSeq = eventSeq.concat(actionObj.eventSeq);
@@ -128,41 +128,45 @@ function actionInvoker(aAccOrElmOrId, aA
"Wrong action name of the accessible for " + prettyName(aAccOrElmOrId));
try {
acc.doAction(aActionIndex);
} catch (e) {
ok(false, "doAction(" + aActionIndex + ") failed with: " + e.name);
return INVOKER_ACTION_FAILED;
}
- }
+ };
this.eventSeq = aEventSeq;
this.getID = function actionInvoker_getID() {
return "invoke an action " + aActionName + " at index " + aActionIndex +
" on " + prettyName(aAccOrElmOrId);
- }
+ };
}
function checkerOfActionInvoker(aType, aTarget, aActionObj) {
this.type = aType;
this.target = aTarget;
+ if (aActionObj && "eventTarget" in aActionObj) {
+ this.eventTarget = aActionObj.eventTarget;
+ }
+
this.phase = false;
this.getID = function getID() {
return aType + " event handling";
- }
+ };
this.check = function check(aEvent) {
- if (aActionObj && "checkOnClickEvent" in aActionObj)
+ if (aType == "click" && aActionObj && "checkOnClickEvent" in aActionObj)
aActionObj.checkOnClickEvent(aEvent);
- }
+ };
}
var gActionDescrMap =
{
jump: "Jump",
press: "Press",
check: "Check",
uncheck: "Uncheck",
--- a/accessible/tests/mochitest/actions/test_anchors.html
+++ b/accessible/tests/mochitest/actions/test_anchors.html
@@ -21,17 +21,17 @@
// //////////////////////////////////////////////////////////////////////////
// Event checkers
function scrollingChecker(aAcc) {
this.type = EVENT_SCROLLING_START;
this.target = aAcc;
this.getID = function scrollingChecker_getID() {
return "scrolling start handling for " + prettyName(aAcc);