Merge draft head draft
authorGregory Szorc <gps@mozilla.com>
Fri, 04 Aug 2017 12:47:37 -0700
changeset 621579 f9594550d1c74b58654d60299e8110a0d8594c24
parent 621578 fa278154a6f11c849b652b8b99dffb2f6d891f59 (diff)
parent 551588 47a6aaf1132483516cd90dfed5fed6f76f917a02 (current diff)
child 621580 a3c7ee4d14cb58001f159372794d04af26e77547
push id72398
push usergszorc@mozilla.com
push dateFri, 04 Aug 2017 23:14:39 +0000
milestone57.0a1
Merge draft head
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -7,41 +7,39 @@
 ^widget/android/GeneratedJNIWrappers.h
 ^widget/android/fennec/FennecJNINatives.h
 ^widget/android/fennec/FennecJNIWrappers.cpp
 ^widget/android/fennec/FennecJNIWrappers.h
 
 # Generated from ./tools/rewriting/ThirdPartyPaths.txt
 # awk '{print "^"$1".*"}' ./tools/rewriting/ThirdPartyPaths.txt
 ^browser/components/translation/cld2/.*
-^build/stlport/.*
 ^db/sqlite3/src/.*
 ^dom/media/platforms/ffmpeg/libav.*
 ^extensions/spellcheck/hunspell/src/.*
-^gfx/2d/convolver.*
-^gfx/2d/image_operations.*
 ^gfx/angle/.*
 ^gfx/cairo/.*
 ^gfx/graphite2/.*
 ^gfx/harfbuzz/.*
 ^gfx/ots/.*
 ^gfx/qcms/.*
 ^gfx/skia/.*
+^gfx/vr/openvr/.*
 ^gfx/webrender.*
-^gfx/webrender_traits.*
+^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/ffvpx/.*
 ^media/libav/.*
 ^media/libcubeb/.*
 ^media/libjpeg/.*
 ^media/libmkv/.*
 ^media/libnestegg/.*
 ^media/libogg/.*
 ^media/libopus/.*
 ^media/libpng/.*
@@ -52,31 +50,53 @@
 ^media/libtremor/.*
 ^media/libvorbis/.*
 ^media/libvpx/.*
 ^media/libyuv/.*
 ^media/mtransport/third_party/.*
 ^media/openmax_dl/.*
 ^media/pocketsphinx/.*
 ^media/sphinxbase/.*
+^media/webrtc/signaling/src/sdp/sipcc/.*
 ^media/webrtc/trunk/.*
-^media/webrtc/signaling/src/sdp/sipcc/.*
-^memory/jemalloc/src/.*
 ^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/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/dromaeo/.*
+^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/which/.*
+^toolkit/components/jsoncpp/.*
 ^toolkit/components/protobuf/.*
 ^toolkit/crashreporter/google-breakpad/.*
--- a/.cron.yml
+++ b/.cron.yml
@@ -2,60 +2,86 @@
 # `taskcluster/taskgraph/cron/schema.py`.  For documentation, see
 # `taskcluster/docs/cron.rst`.
 
 jobs:
     - name: nightly-desktop
       job:
           type: decision-task
           treeherder-symbol: Nd
-          triggered-by: nightly
-          target-tasks-method: nightly_linux
+          target-tasks-method: nightly_desktop
       run-on-projects:
           - mozilla-central
-          - mozilla-aurora
           - date
       when:
           by-project:
             # Match buildbot starts for now
-            date: [{hour: 16, minute: 0}]
-            mozilla-central: [{hour: 11, minute: 0}]
-            mozilla-aurora: [{hour: 8, minute: 45}]  # Buildbot uses minute 40
+            date: [{hour: 15, minute: 0}]
+            mozilla-central: [{hour: 10, minute: 0}]
             # No default
 
+    - name: nightly-desktop-linux
+      job:
+          type: decision-task
+          treeherder-symbol: Nd-Ln
+          target-tasks-method: nightly_linux
+      run-on-projects:
+          - mozilla-central
+          - date
+      when: [] # never (hook only)
+
+    - name: nightly-desktop-osx
+      job:
+          type: decision-task
+          treeherder-symbol: Nd-OSX
+          target-tasks-method: nightly_macosx
+      run-on-projects:
+          - mozilla-central
+          - date
+      when: [] # never (hook only)
+
+    - name: nightly-desktop-win
+      job:
+          type: decision-task
+          treeherder-symbol: Nd-Win
+          target-tasks-method: nightly_win
+      run-on-projects:
+          - mozilla-central
+          - date
+      when: [] # never (hook only)
+
     - name: nightly-android
       job:
           type: decision-task
           treeherder-symbol: Na
-          triggered-by: nightly
           target-tasks-method: nightly_fennec
       run-on-projects:
           - mozilla-central
-          - mozilla-aurora
           - date
       when:
         by-project:
             # Match buildbot starts for now
-            date: [{hour: 16, minute: 0}]
-            mozilla-central: [{hour: 11, minute: 0}]
-            mozilla-aurora: [{hour: 8, minute: 45}]  # Buildbot uses minute 40
+            date: [{hour: 15, minute: 0}]
+            mozilla-central: [{hour: 10, minute: 0}]
             # No default
 
     - name: nightly-mochitest-valgrind
       job:
           type: decision-task
           treeherder-symbol: Vg
           target-tasks-method: mochitest_valgrind
       run-on-projects:
           - mozilla-central
       when:
           - {hour: 16, minute: 0}
           - {hour: 4, minute: 0}
 
-    - name: nightly-code-coverage
+    - name: nightly-dmd
       job:
           type: decision-task
-          treeherder-symbol: Nc
-          target-tasks-method: nightly_code_coverage
+          treeherder-symbol: Ndmd
+          target-tasks-method: nightly_dmd
       run-on-projects:
           - mozilla-central
       when:
-          - {hour: 18, minute: 0}
+          by-project:
+            mozilla-central: [{hour: 10, minute: 0}]
+            # No default
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,89 +3,96 @@
 
 # Exclude expected objdirs.
 obj*/**
 
 # We ignore all these directories by default, until we get them enabled.
 # If you are enabling a directory, please add directory specific exclusions
 # below.
 addon-sdk/**
-build/**
-caps/**
 chrome/**
 config/**
-db/**
 docshell/**
 editor/**
 embedding/**
-extensions/**
+extensions/cookie/**
+extensions/spellcheck/**
+extensions/universalchardet/**
 gfx/**
-gradle/**
-hal/**
 image/**
 intl/**
 ipc/**
 layout/**
 media/**
 memory/**
-mfbt/**
 modules/**
-mozglue/**
 netwerk/**
-nsprpub/**
-other-licenses/**
 parser/**
-probes/**
 python/**
 rdf/**
 servo/**
-startupcache/**
-testing/**
 tools/update-packaging/**
 uriloader/**
 view/**
 widget/**
 xpcom/**
+
+# We currently have no js files in these directories, so we ignore them by
+# default to aid ESLint's performance.
+build/**
+db/**
+gradle/**
+hal/**
+mfbt/**
+mozglue/**
+nsprpub/**
+other-licenses/**
+probes/**
+startupcache/**
 xpfe/**
-xulrunner/**
-
-# b2g exclusions (pref files).
-b2g/app/b2g.js
-b2g/graphene/graphene.js
-b2g/locales/en-US/b2g-l10n.js
 
 # browser/ exclusions
 browser/app/**
 browser/branding/**/firefox-branding.js
-browser/base/content/nsContextMenu.js
-browser/base/content/sanitizeDialog.js
-browser/base/content/test/general/file_csp_block_all_mixedcontent.html
+# 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/**
-browser/components/downloads/**
 # 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
-browser/components/tabview/**
 # 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/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
 
 # devtools/ exclusions
 devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/framework/**
+!devtools/client/framework/devtools.js
+!devtools/client/framework/devtools-browser.js
 !devtools/client/framework/selection.js
 !devtools/client/framework/target*
 !devtools/client/framework/toolbox*
 devtools/client/inspector/markup/test/doc_markup_events_*.html
 devtools/client/inspector/rules/test/doc_media_queries.html
 devtools/client/memory/test/chrome/*.html
 devtools/client/performance/components/test/test_jit_optimizations_01.html
 devtools/client/projecteditor/**
@@ -112,41 +119,33 @@ devtools/client/storage/test/*.html
 !devtools/client/storage/test/storage-cookies.html
 !devtools/client/storage/test/storage-overflow.html
 !devtools/client/storage/test/storage-search.html
 !devtools/client/storage/test/storage-unsecured-iframe.html
 !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
 devtools/client/webaudioeditor/**
 devtools/client/webconsole/net/**
 devtools/client/webconsole/test/**
-devtools/client/webconsole/console-output.js
 devtools/client/webconsole/hudservice.js
 devtools/client/webconsole/webconsole-connection-proxy.js
 devtools/client/webconsole/webconsole.js
 devtools/client/webide/**
 !devtools/client/webide/components/webideCli.js
-devtools/server/actors/webconsole.js
-devtools/server/actors/object.js
-devtools/server/actors/script.js
-devtools/server/actors/styleeditor.js
-devtools/server/actors/stylesheets.js
 devtools/server/tests/browser/storage-*.html
 !devtools/server/tests/browser/storage-unsecured-iframe.html
 devtools/server/tests/browser/stylesheets-nested-iframes.html
 devtools/server/tests/unit/xpcshell_debugging_script.js
 devtools/shared/platform/content/test/test_clipboard.html
 devtools/shared/qrcode/tests/mochitest/test_decode.html
 devtools/shared/tests/mochitest/*.html
 devtools/shared/webconsole/test/test_*.html
 
-# Ignore devtools pre-processed files
-devtools/client/framework/toolbox-process-window.js
-devtools/client/performance/system.js
-devtools/client/webide/webide-prefs.js
+# Ignore devtools preferences files
 devtools/client/preferences/**
+devtools/shim/devtools-startup-prefs.js
 
 # Ignore devtools third-party libs
 devtools/shared/jsbeautify/*
 devtools/shared/acorn/*
 devtools/shared/gcli/source/*
 devtools/shared/node-properties/*
 devtools/shared/pretty-fast/*
 devtools/shared/sourcemap/*
@@ -162,21 +161,24 @@ devtools/client/sourceeditor/tern/*
 devtools/client/sourceeditor/test/cm_mode_ruby.js
 devtools/client/sourceeditor/test/codemirror/*
 devtools/client/inspector/markup/test/lib_*
 devtools/client/jsonview/lib/require.js
 devtools/server/actors/utils/automation-timeline.js
 
 # Ignore devtools files testing sourcemaps / code style
 devtools/client/debugger/test/mochitest/code_binary_search.js
+devtools/client/debugger/test/mochitest/code_binary_search_absolute.js
 devtools/client/debugger/test/mochitest/code_math.min.js
 devtools/client/debugger/test/mochitest/code_math_bogus_map.js
 devtools/client/debugger/test/mochitest/code_ugly*
 devtools/client/debugger/test/mochitest/code_worker-source-map.js
 devtools/client/framework/test/code_ugly*
+devtools/client/inspector/markup/test/events_bundle.js
+devtools/client/netmonitor/test/xhr_bundle.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/animation/**
 dom/archivereader/**
 dom/asmjscache/**
@@ -207,20 +209,22 @@ dom/imptests/**
 dom/interfaces/**
 dom/ipc/**
 dom/json/**
 dom/jsurl/**
 dom/locales/**
 dom/manifest/**
 dom/mathml/**
 dom/media/**
+!dom/media/*.js*
 dom/messagechannel/**
 dom/network/**
 dom/notification/**
 dom/offline/**
+dom/payments/**
 dom/performance/**
 dom/permission/**
 dom/plugins/**
 dom/power/**
 dom/presentation/**
 dom/promise/**
 dom/push/**
 dom/quota/**
@@ -242,61 +246,88 @@ dom/webidl/**
 dom/workers/**
 dom/worklet/**
 dom/xbl/**
 dom/xhr/**
 dom/xml/**
 dom/xslt/**
 dom/xul/**
 
+# Third-party
+dom/media/webvtt/**
+
 # 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/tests/**
+js/src/Y.js
 
 # mobile/android/ exclusions
-mobile/android/tests/
+mobile/android/tests/browser/chrome/tp5/**
 
 # Uses `#filter substitution`
-mobile/android/b2gdroid/app/b2gdroid.js
 mobile/android/app/mobile.js
 mobile/android/chrome/content/healthreport-prefs.js
 
 # Uses `#expand`
 mobile/android/chrome/content/about.js
 
 # Not much JS to lint and non-standard at that
 mobile/android/installer/
 mobile/android/locales/
 
 # Non-standard `(catch ex if ...)`
 mobile/android/chrome/content/browser.js
 mobile/android/components/Snippets.js
 
-# Bug 1178739: Ignore this file as a quick fix for "Illegal yield expression"
-mobile/android/modules/HomeProvider.jsm
-
 # security/ exclusions (pref files).
 security/manager/ssl/security-prefs.js
 
-#NSS
+# NSS / taskcluster only.
 security/nss/**
 
 # services/ exclusions
 
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
+# Remote protocol exclusions
+testing/marionette/test_*.js
+testing/marionette/atom.js
+testing/marionette/legacyaction.js
+testing/marionette/client
+testing/marionette/doc
+testing/marionette/harness
+
+# other testing/ exclusions
+testing/mochitest/**
+# third party modules
+testing/modules/ajv-4.1.1.js
+testing/modules/sinon-2.3.2.js
+# octothorpe used for pref file comment causes parsing error
+testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
+testing/talos/talos/scripts/jszip.min.js
+testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
+testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js
+testing/talos/talos/tests/canvasmark/**
+testing/talos/talos/tests/dromaeo/**
+testing/talos/talos/tests/v8_7/**
+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
@@ -304,29 +335,28 @@ toolkit/components/help/**
 
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
 
 # Tests old non-star function generators
 toolkit/modules/tests/xpcshell/test_task.js
 
 # Not yet updated
-toolkit/components/osfile/**
+toolkit/components/url-classifier/**
 
 # External code:
 toolkit/components/microformats/test/**
 toolkit/components/microformats/microformat-shiv.js
 toolkit/components/reader/Readability.js
 toolkit/components/reader/JSDOMParser.js
 
 # Uses preprocessing
 toolkit/content/widgets/wizard.xml
 toolkit/components/jsdownloads/src/DownloadIntegration.jsm
-toolkit/components/url-classifier/**
+toolkit/components/osfile/osfile.jsm
 toolkit/components/urlformatter/nsURLFormatter.js
 toolkit/modules/AppConstants.jsm
 toolkit/mozapps/downloads/nsHelperAppDlg.js
-toolkit/mozapps/extensions/internal/AddonConstants.jsm
 toolkit/mozapps/update/tests/data/xpcshellConstantsPP.js
-toolkit/webapps/**
 
 # Third party
 toolkit/modules/third_party/**
+third_party/**
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,27 +1,19 @@
 "use strict";
 
 module.exports = {
-  // When adding items to this file please check for effects on sub-directories.
+  // New rules and configurations should generally be added in
+  // tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js to
+  // allow external repositories that use the plugin to pick them up as well.
+  "extends": [
+    "plugin:mozilla/recommended"
+  ],
   "plugins": [
     "mozilla"
   ],
-  "rules": {
-    "mozilla/avoid-removeChild": "error",
-    "mozilla/avoid-nsISupportsString-preferences": "error",
-    "mozilla/import-globals": "warn",
-    "mozilla/no-import-into-var-and-global": "error",
-    "mozilla/no-useless-parameters": "error",
-    "mozilla/no-useless-removeEventListener": "error",
-    "mozilla/use-default-preference-values": "error",
-    "mozilla/use-ownerGlobal": "error",
-
-    // No (!foo in bar) or (!object instanceof Class)
-    "no-unsafe-negation": "error",
-  },
-  "env": {
-    "es6": true
-  },
-  "parserOptions": {
-    "ecmaVersion": 8,
+  // 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" ]
   },
 };
--- a/.flake8
+++ b/.flake8
@@ -1,5 +1,4 @@
 [flake8]
 # See http://pep8.readthedocs.io/en/latest/intro.html#configuration
 ignore = E121, E123, E126, E129, E133, E226, E241, E242, E704, W503, E402
 max-line-length = 99
-filename = *.py, +.lint
--- a/.gdbinit_python
+++ b/.gdbinit_python
@@ -1,5 +1,5 @@
 python
 import sys
-sys.path.append('python/gdbpp/')
+sys.path.append('third_party/python/gdbpp/')
 import gdbpp
 end
--- a/.gitignore
+++ b/.gitignore
@@ -2,16 +2,17 @@
 
 # Filenames that should be ignored wherever they appear
 *~
 *.pyc
 *.pyo
 TAGS
 tags
 ID
+!/browser/extensions/screenshots/webextension/_locales/id/
 .DS_Store*
 *.pdb
 *.egg-info
 
 # Vim swap files.
 .*.sw[a-z]
 
 # Emacs directory variable files.
@@ -29,16 +30,19 @@ ID
 /.machrc
 
 # Empty marker file that's generated when we check out NSS
 security/manager/.nss.checkout
 
 # Build directories
 /obj*/
 
+# gecko.log is generated by various test harnesses
+/gecko.log
+
 # Build directories for js shell
 _DBG.OBJ/
 _OPT.OBJ/
 
 # SpiderMonkey configury
 js/src/configure
 js/src/old-configure
 js/src/autom4te.cache
@@ -53,27 +57,29 @@ parser/html/java/javaparser/
 # Ignore the files and directory that Eclipse IDE creates
 .project
 .cproject
 .settings/
 
 # Ignore the files and directory that JetBrains IDEs create.
 /.idea/
 *.iml
+# Android Monitor in Android Studio creates a captures/ directory.
+/captures/
 
 # Gradle cache.
 /.gradle/
 
 # Local Gradle configuration properties.
 /local.properties
 
 # Python virtualenv artifacts.
-python/psutil/**/*.so
-python/psutil/**/*.pyd
-python/psutil/build/
+third_party/python/psutil/**/*.so
+third_party/python/psutil/**/*.pyd
+third_party/python/psutil/build/
 
 # Ignore chrome.manifest files from the devtools loader
 devtools/client/chrome.manifest
 devtools/shared/chrome.manifest
 
 # Ignore node_modules directories in devtools
 devtools/**/node_modules
 
@@ -81,32 +87,35 @@ devtools/**/node_modules
 GTAGS
 GRTAGS
 GSYMS
 GPATH
 
 # Git clone directory for updating web-platform-tests
 testing/web-platform/sync/
 
+# Third party metadata for web-platform-tests
+testing/web-platform/products/
+
 # Android Gradle artifacts.
 mobile/android/gradle/.gradle
 
 # XCode project cruft
 embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
 embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
 
 # Ignore mozharness execution files
 testing/mozharness/.tox/
 testing/mozharness/build/
 testing/mozharness/logs/
 testing/mozharness/.coverage
 testing/mozharness/nosetests.xml
 
-# Ignore node_modules
-tools/lint/eslint/node_modules/
+# Ignore ESLint node_modules
+node_modules/
 
 # Ignore talos virtualenv and tp5n files.
 # The tp5n set is supposed to be decompressed at
 # testing/talos/talos/page_load_test/tp5n in order to run tests like tps
 # locally. Similarly, running talos requires a Python package virtual
 # environment. Both the virtual environment and tp5n files end up littering
 # the status command, so we ignore them.
 testing/talos/.Python
@@ -117,8 +126,13 @@ testing/talos/talos/tests/tp5n.zip
 testing/talos/talos/tests/tp5n
 testing/talos/talos/tests/devtools/damp.manifest.develop
 
 # Ignore files created when running a reftest.
 lextab.py
 
 # tup database
 /.tup
+
+# Ignore Visual Studio Code workspace files.
+.vscode/*
+!.vscode/extensions.json
+!.vscode/tasks.json
--- a/.hgignore
+++ b/.hgignore
@@ -27,16 +27,19 @@
 ^\.?machrc$
 
 # Empty marker file that's generated when we check out NSS
 ^security/manager/\.nss\.checkout$
 
 # Build directories
 ^obj
 
+# gecko.log is generated by various test harnesses
+^gecko\.log
+
 # Build directories for js shell
 _DBG\.OBJ/
 _OPT\.OBJ/
 ^js/src/.*-obj/
 
 # SpiderMonkey configury
 ^js/src/configure$
 ^js/src/old-configure$
@@ -56,27 +59,29 @@
 # Ignore the files and directory that Eclipse IDE creates
 \.project$
 \.cproject$
 \.settings/
 
 # Ignore the files and directory that JetBrains IDEs create.
 \.idea/
 \.iml$
+# Android Monitor in Android Studio creates a captures/ directory.
+^captures/
 
 # Gradle cache.
 ^.gradle/
 
 # Local Gradle configuration properties.
 ^local.properties$
 
 # Python stuff installed at build time.
-^python/psutil/.*\.so
-^python/psutil/.*\.pyd
-^python/psutil/build/
+^third_party/python/psutil/.*\.so
+^third_party/python/psutil/.*\.pyd
+^third_party/python/psutil/build/
 
 # Git repositories
 .git/
 
 # Ignore chrome.manifest files from the devtools loader
 ^devtools/client/chrome.manifest$
 ^devtools/shared/chrome.manifest$
 
@@ -90,16 +95,19 @@
 GTAGS
 GRTAGS
 GSYMS
 GPATH
 
 # Git clone directory for updating web-platform-tests
 ^testing/web-platform/sync/
 
+# Third party metadata for web-platform-tests
+^testing/web-platform/products/
+
 # Android Gradle artifacts.
 ^mobile/android/gradle/.gradle
 
 # XCode project cruft
 ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
 ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/xcuserdata
 
 # Ignore mozharness execution files
@@ -107,35 +115,42 @@ GPATH
 ^testing/mozharness/build/
 ^testing/mozharness/logs/
 ^testing/mozharness/.coverage
 ^testing/mozharness/nosetests.xml
 
 # Ignore tox generated dir
 .tox/
 
-# Ignore node_modules
-^tools/lint/eslint/node_modules/
+# Ignore ESLint node_modules
+^node_modules/
+^tools/lint/eslint/eslint-plugin-mozilla/node_modules/
 
 # Ignore talos virtualenv and tp5n files.
 # The tp5n set is supposed to be decompressed at
 # testing/talos/talos/tests/tp5n in order to run tests like tps
 # locally. Similarly, running talos requires a Python package virtual
 # environment. Both the virtual environment and tp5n files end up littering
 # the status command, so we ignore them.
 ^testing/talos/.Python
 ^testing/talos/bin/
 ^testing/talos/include/
 ^testing/talos/lib/
 ^testing/talos/talos/tests/tp5n.zip
 ^testing/talos/talos/tests/tp5n.tar.gz
 ^testing/talos/talos/tests/tp5n
 ^testing/talos/talos/tests/devtools/damp.manifest.develop
 ^talos-venv
+^py3venv
 
 # Ignore files created when running a reftest.
 ^lextab.py$
 
 # tup database
 ^\.tup
 
+# Ignore Visual Studio Code workspace files.
+\.vscode/(?!extensions\.json|tasks\.json)
+
 subinclude:servo/.hgignore
 
+# Ignore Infer output
+^infer-out/
--- a/.hgtags
+++ b/.hgtags
@@ -127,8 +127,10 @@ 67c66c2878aed17ae3096d7db483ddbb2293c503
 68d3781deda0d4d58ec9877862830db89669b3a5 FIREFOX_AURORA_47_BASE
 1c6385ae1fe7e37d8f23f958ce14582f07af729e FIREFOX_AURORA_48_BASE
 d98f20c25feeac4dd7ebbd1c022957df1ef58af4 FIREFOX_AURORA_49_BASE
 465d150bc8be5bbf9f02a8607d4552b6a5e1697c FIREFOX_AURORA_50_BASE
 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
new file mode 100644
--- /dev/null
+++ b/.inferconfig
@@ -0,0 +1,7 @@
+{
+    "infer-blacklist-path-regex": [
+        // This is full of issues, and is a dependency we need to discard
+        // sooner rather than later anyway:
+        "mobile/android/thirdparty/ch/boye/httpclientandroidlib"
+    ]
+}
\ No newline at end of file
--- a/.lldbinit
+++ b/.lldbinit
@@ -1,20 +1,20 @@
 # .lldbinit file for debugging Mozilla
 
 # -----------------------------------------------------------------------------
-# For documentation on all of the commands and type summaries defined here
-# and in the accompanying Python scripts, see python/lldbutils/README.txt.
+# For documentation on all of the commands and type summaries defined here and
+# in the accompanying Python scripts, see third_party/python/lldbutils/README.txt.
 # -----------------------------------------------------------------------------
 
 # Import the module that defines complex Gecko debugging commands.  This assumes
 # you are either running lldb from the top level source directory, the objdir,
 # or the dist/bin directory.  (.lldbinit files in the objdir and dist/bin set
 # topsrcdir appropriately.)
-script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "python/lldbutils")); import lldbutils; lldbutils.init()
+script topsrcdir = topsrcdir if locals().has_key("topsrcdir") else os.getcwd(); sys.path.append(os.path.join(topsrcdir, "third_party/python/lldbutils")); import lldbutils; lldbutils.init()
 
 # Mozilla's use of UNIFIED_SOURCES to include multiple source files into a
 # single compiled file breaks lldb breakpoint setting. This works around that.
 # See http://lldb.llvm.org/troubleshooting.html for more info.
 settings set target.inline-breakpoint-strategy always
 
 # Show the dynamic type of an object when using "expr".  This, for example,
 # will show a variable declared as "nsIFrame *" that points to an nsBlockFrame
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -1,125 +1,134 @@
----
-version: 0
-metadata:
-  name: 'Taskcluster tasks for Gecko'
-  description: "The taskcluster task graph for Gecko trees"
-  owner: mozilla-taskcluster-maintenance@mozilla.com
-  source: {{{source}}}
+# 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
+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}'
 
-scopes:
-  # Note the below scopes are insecure however these get overriden on the server
-  # side to whatever scopes are set by mozilla-taskcluster.
-  - queue:*
-  - docker-worker:*
-  - scheduler:*
+    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})'
 
-# This file undergoes substitution to create tasks.  For on-push tasks, that
-# substitution is done by mozilla-taskcluster.  For cron tasks, that substitution
-# is done by `taskcluster/taskgraph/cron/decision.py`.  If you change any of the
-# template parameters, please do so in all three places!
-#
-# Available template parameters:
-#
-# - now:            current time
-# - owner:          push user (email address)
-# - source:         URL of this YAML file
-# - url:            repository URL
-# - project:        alias for the destination repository (basename of
-#                   the repo url)
-# - level:          SCM level of the destination repository
-#                   (1 = try, 3 = core)
-# - revision:       hg revision of the head of the push
-# - comment:        comment of the push
-# - pushlog_id:     id in the pushlog table of the repository
-#
-# and functions:
-# - as_slugid:      convert a label into a slugId
-# - from_now:       generate a timestamp at a fixed offset from now
-# - shellquote:     quote the contents for injection into shell
+    provisionerId: "aws-provisioner-v1"
+    workerType: "gecko-decision"
+
+    tags:
+      $if: 'tasks_for == "hg-push"'
+      then: {createdForUser: "${ownerEmail}"}
 
-# The resulting tasks' taskGroupId will be equal to the taskId of the first
-# task listed here, which should be the decision task.  This gives other tools
-# an easy way to determine the ID of the decision task that created a
-# particular group.
+    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}"
 
-tasks:
-  - taskId: '{{#as_slugid}}decision task{{/as_slugid}}'
-    task:
-      created: '{{now}}'
-      deadline: '{{#from_now}}1 day{{/from_now}}'
-      expires: '{{#from_now}}365 day{{/from_now}}'
-      metadata:
-        owner: mozilla-taskcluster-maintenance@mozilla.com
-        source: {{{source}}}
-        name: "Gecko Decision Task"
-        description: |
-            The task that creates all of the other tasks in the task graph
+    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}'
 
-      workerType: "gecko-decision"
-      provisionerId: "aws-provisioner-v1"
+    dependencies: []
+    requires: all-completed
 
-      tags:
-        createdForUser: {{owner}}
+    priority: lowest
+    retries: 5
 
-      routes:
-        - "index.gecko.v2.{{project}}.latest.firefox.decision"
-        - "tc-treeherder.v2.{{project}}.{{revision}}.{{pushlog_id}}"
-        - "tc-treeherder-stage.v2.{{project}}.{{revision}}.{{pushlog_id}}"
-
-      payload:
-        env:
-          # checkout-gecko uses these to check out the source; the inputs
-          # to `mach taskgraph decision` are all on the command line.
-          GECKO_BASE_REPOSITORY: 'https://hg.mozilla.org/mozilla-unified'
-          GECKO_HEAD_REPOSITORY: '{{{url}}}'
-          GECKO_HEAD_REF: '{{revision}}'
-          GECKO_HEAD_REV: '{{revision}}'
-          HG_STORE_PATH: /home/worker/checkouts/hg-store
+    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: /home/worker/checkouts/hg-store
 
-        cache:
-          level-{{level}}-checkouts: /home/worker/checkouts
+      cache:
+        level-${repository.level}-checkouts: /home/worker/checkouts
 
-        features:
-          taskclusterProxy: true
-          chainOfTrust: true
+      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:0.1.8@sha256:195d8439c8e90d59311d877bd2a8964849b2e43bfc6c234092618518d8b2891b'
+      # 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:0.1.8@sha256:195d8439c8e90d59311d877bd2a8964849b2e43bfc6c234092618518d8b2891b'
 
-        maxRunTime: 1800
+      maxRunTime: 1800
 
-        # TODO use mozilla-unified for the base repository once the tc-vcs
-        # tar.gz archives are created or tc-vcs isn't being used.
-        command:
-          - /home/worker/bin/run-task
-          - '--vcs-checkout=/home/worker/checkouts/gecko'
-          - '--'
-          - bash
-          - -cx
-          - >
-              cd /home/worker/checkouts/gecko &&
-              ln -s /home/worker/artifacts artifacts &&
-              ./mach --log-no-times taskgraph decision
-              --pushlog-id='{{pushlog_id}}'
-              --pushdate='{{pushdate}}'
-              --project='{{project}}'
-              --message={{#shellquote}}{{{comment}}}{{/shellquote}}
-              --owner='{{owner}}'
-              --level='{{level}}'
-              --base-repository='https://hg.mozilla.org/mozilla-central'
-              --head-repository='{{{url}}}'
-              --head-ref='{{revision}}'
-              --head-rev='{{revision}}'
+      # TODO use mozilla-unified for the base repository once the tc-vcs
+      # tar.gz archives are created or tc-vcs isn't being used.
+      command:
+        - /home/worker/bin/run-task
+        - '--vcs-checkout=/home/worker/checkouts/gecko'
+        - '--'
+        - bash
+        - -cx
+        - $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 /home/worker/checkouts/gecko &&
+            ln -s /home/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: '/home/worker/artifacts'
-            expires: '{{#from_now}}364 days{{/from_now}}'
+      artifacts:
+        'public':
+          type: 'directory'
+          path: '/home/worker/artifacts'
+          expires: {$fromNow: '1 year'}
 
-      extra:
-        treeherder:
+    extra:
+      treeherder:
+        $if: 'tasks_for == "hg-push"'
+        then:
           symbol: D
+        else:
+          groupSymbol: cron
+          symbol: "${cron.job_symbol}"
new file mode 100644
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,18 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        // Trim only touched lines.
+        "NathanRidley.autotrim",
+        // JS Babel ES6/ES7 syntax hilight.
+        "dzannotti.vscode-babel-coloring",
+        // ESLint support.
+        "dbaeumer.vscode-eslint",
+        // C/C++ language support.
+        "ms-vscode.cpptools",
+        // Rust language support.
+        "kalitaalexey.vscode-rust",
+        // CSS support for HTML documents.
+        "ecmel.vscode-html-css"
+    ]
+}
new file mode 100644
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,152 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "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"
+      },
+      {
+        "taskName": "clobber-python",
+        "suppressTaskName": true,
+        "args": ["clobber", "python"]
+      },
+      {
+        "taskName": "configure"
+      },
+      {
+        "taskName": "build",
+        "isBuildCommand": true,
+        "problemMatcher": {
+          "owner": "cpp",
+          "fileLocation": "absolute",
+          "pattern": {
+            "regexp": "^.*?tools([^\\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"],
+        "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
+          }
+        }
+      },
+      {
+        "taskName": "run",
+        "args": ["-purgecaches"],
+        "showOutput": "always"
+      },
+      {
+        "taskName": "lint-wo",
+        "suppressTaskName": true,
+        "args": ["lint", "-wo"],
+        "problemMatcher": ["$eslint-stylish"]
+      },
+      {
+        "taskName": "eslint",
+        "problemMatcher": ["$eslint-stylish"]
+      },
+      {
+        "taskName": "eslint-fix",
+        "suppressTaskName": true,
+        "args": ["eslint", "--fix", "${file}"],
+        "problemMatcher": ["$eslint-stylish"]
+      },
+      {
+        "taskName": "test",
+        "args": ["${relativeFile}"],
+        "isTestCommand": true,
+        "showOutput": "always"
+      },
+      {
+        "taskName": "mochitest",
+        "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
+          }
+        }
+      },
+      {
+        "taskName": "reftest",
+        "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
+          }
+        }
+      },
+      {
+        "taskName": "xpcshell-test",
+        "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
+          }
+        }
+      }
+    ]
+}
--- a/AUTHORS
+++ b/AUTHORS
@@ -285,16 +285,17 @@ David Rajchenbach-Teller <dteller@mozill
 David Savage
 David S. Miller <davem@redhat.com>
 David Woodhouse <dwmw2@infradead.org>
 David Zbarsky <dzbarsky@gmail.com>
 Dean Tessman <dean_tessman@hotmail.com>
 <deneen@alum.bucknell.edu>
 Denis Antrushin <adu@sparc.spb.su>
 Denis Issoupov <denis@macadamian.com>
+Dennis Ek <contact@dennisek.se>
 Dennis Handly
 Derrick Rice <derrick.rice@gmail.com>
 <desale@netscape.com>
 diablohn
 Diane Trout <diane@ghic.org>
 Dietrich Ayala <dietrich@mozilla.com>
 Digital Creations 2, Inc
 Disruptive Innovations
@@ -792,16 +793,17 @@ Paul Rouget <paul@mozilla.com>
 Paul Sandoz <paul.sandoz@sun.com>
 Pavel Cvrcek
 Pawel Chmielowski
 PenPal
 Pete Collins <petejc@collab.net>
 Peter Annema <disttsc@bart.nl>
 Peter Bajusz <hyp-x@inf.bme.hu>
 Peter Lubczynski <peterl@netscape.com>
+Peter Moore <petemoore@gmx.net>
 Peter Naulls
 Peter Parente <parente@cs.unc.edu>
 Peter Seliger
 Peter Van der Beken <peter@propagandism.org>
 Peter van der Woude
 Peter Weilbacher <mozilla@weilbacher.org>
 Pete Zha <pete.zha@sun.com>
 Petr Kostka <petr.kostka@st.com>
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1343682 - backing out a previous version didn't stop the failures from it, so it appears to need a clobber both out and in
+Merge day clobber
\ No newline at end of file
--- a/Makefile.in
+++ b/Makefile.in
@@ -142,17 +142,22 @@ ifneq (,$(filter FasterMake+RecursiveMak
 install-manifests: faster
 .PHONY: faster
 faster: install-dist/idl
 	$(MAKE) -C faster FASTER_RECURSIVE_MAKE=1
 endif
 
 .PHONY: tup
 tup:
-	$(call BUILDSTATUS,TIERS make tup)
+	$(call BUILDSTATUS,TIERS $(if $(MOZ_ARTIFACT_BUILDS),artifact )make tup)
+ifdef MOZ_ARTIFACT_BUILDS
+	$(call BUILDSTATUS,TIER_START artifact)
+	$(MAKE) recurse_artifact
+	$(call BUILDSTATUS,TIER_FINISH artifact)
+endif
 	$(call BUILDSTATUS,TIER_START make)
 	$(MAKE) buildid.h source-repo.h
 	$(call BUILDSTATUS,TIER_FINISH make)
 	$(call BUILDSTATUS,TIER_START tup)
 	@$(TUP) $(if $(findstring s,$(filter-out --%,$(MAKEFLAGS))),,--verbose)
 	$(call BUILDSTATUS,TIER_FINISH tup)
 
 # process_install_manifest needs to be invoked with --no-remove when building
@@ -233,86 +238,60 @@ endif
 endif
 
 default all::
 	$(call BUILDSTATUS,TIERS $(TIERS) $(if $(MOZ_AUTOMATION),$(MOZ_AUTOMATION_TIERS)))
 
 include $(topsrcdir)/config/rules.mk
 
 ifdef SCCACHE_VERBOSE_STATS
-# This won't contain stats for both halves of a universal build, but I can live with that.
 default::
+	-$(CCACHE) --show-stats --stats-format=json > sccache-stats.json
 	@echo "===SCCACHE STATS==="
 	-$(CCACHE) --show-stats
 	@echo "==================="
+ifndef MOZ_PROFILE_GENERATE
+# Ideally we'd do that in the same file as we set the sccache.log location for
+# sccache, but it's too late in the build.
+	-gzip -9 $(DIST)/sccache.log
+endif
 endif
 
 distclean::
 	$(RM) $(DIST_GARBAGE)
 
-ifeq ($(OS_ARCH),WINNT)
-# we want to copy PDB files on Windows
-MAKE_SYM_STORE_ARGS := -c --vcs-info
-ifdef PDBSTR_PATH
-MAKE_SYM_STORE_ARGS += -i
-endif
-ifdef MSVC_HAS_DIA_SDK
-DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms.exe
-else
-DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms_vc$(_MSC_VER).exe
-endif
-# PDB files don't get moved to dist, so we need to scan the whole objdir
-MAKE_SYM_STORE_PATH := .
-endif
-ifeq ($(OS_ARCH),Darwin)
-# need to pass arch flags for universal builds
-MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
-MAKE_SYM_STORE_PATH := $(DIST)/bin
-DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
-endif
-ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
-MAKE_SYM_STORE_ARGS := -c --vcs-info
-DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
-MAKE_SYM_STORE_PATH := $(DIST)/bin
-endif
-MAKE_SYM_STORE_ARGS += --install-manifest=$(DEPTH)/_build_manifests/install/dist_include,$(DIST)/include
-
-SYM_STORE_SOURCE_DIRS := $(topsrcdir)
-
 ifdef MOZ_CRASHREPORTER
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 
 endif
 
-.PHONY: generatesymbols
-generatesymbols:
-	echo building symbol store
-	$(RM) -r $(DIST)/crashreporter-symbols
-	$(RM) '$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip'
-	$(RM) '$(DIST)/$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
-	$(NSINSTALL) -D $(DIST)/crashreporter-symbols
-	OBJCOPY='$(OBJCOPY)' \
-	$(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
-	  $(MAKE_SYM_STORE_ARGS)                                          \
-	  $(foreach dir,$(SYM_STORE_SOURCE_DIRS),-s $(dir))               \
-	  $(DUMP_SYMS_BIN)                                                \
-	  $(DIST)/crashreporter-symbols                                   \
-	  $(MAKE_SYM_STORE_PATH)
+.PHONY: prepsymbolsarchive
+prepsymbolsarchive:
 	echo packing symbols
 	$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
 
+ifndef MOZ_AUTOMATION
+prepsymbolsarchive: recurse_syms
+endif
+
 .PHONY: symbolsfullarchive
-symbolsfullarchive: generatesymbols
-	cd $(DIST)/crashreporter-symbols && \
-          zip -r5D '../$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip' . -x '*test*' -x '*Test*'
+symbolsfullarchive: prepsymbolsarchive
+	$(RM) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
+	$(call py_action,symbols_archive,'$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip' \
+                                     $(abspath $(DIST)/crashreporter-symbols) \
+                                     --exclude '*test*' \
+                                     --exclude '*Test*' \
+                                     --compress '**/*.sym')
 
 .PHONY: symbolsarchive
-symbolsarchive: generatesymbols
-	cd $(DIST)/crashreporter-symbols && \
-          zip -r5D '../$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' . -i '*.sym'
+symbolsarchive: prepsymbolsarchive
+	$(RM) '$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip'
+	$(call py_action,symbols_archive,'$(DIST)/$(PKG_PATH)$(SYMBOL_ARCHIVE_BASENAME).zip' \
+                                     $(abspath $(DIST)/crashreporter-symbols) \
+                                     --include '**/*.sym')
 
 ifdef MOZ_CRASHREPORTER
 buildsymbols: symbolsfullarchive symbolsarchive
 else
 buildsymbols:
 endif
 
 uploadsymbols:
--- a/accessible/.eslintrc.js
+++ b/accessible/.eslintrc.js
@@ -1,17 +1,17 @@
 "use strict";
 
 module.exports = {
-  "extends": [
-    "../.eslintrc.js"
-  ],
-  "globals": {
-    "Cc": true,
-    "Ci": true,
-    "Components": true,
-    "console": true,
-    "Cu": true,
-    "dump": true,
-    "Services": true,
-    "XPCOMUtils": true
+  "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/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -60,17 +60,17 @@ enum MaiInterfaceType {
     MAI_INTERFACE_ACTION,
     MAI_INTERFACE_VALUE,
     MAI_INTERFACE_EDITABLE_TEXT,
     MAI_INTERFACE_HYPERTEXT,
     MAI_INTERFACE_HYPERLINK_IMPL,
     MAI_INTERFACE_SELECTION,
     MAI_INTERFACE_TABLE,
     MAI_INTERFACE_TEXT,
-    MAI_INTERFACE_DOCUMENT, 
+    MAI_INTERFACE_DOCUMENT,
     MAI_INTERFACE_IMAGE, /* 10 */
     MAI_INTERFACE_TABLE_CELL
 };
 
 static GType GetAtkTypeForMai(MaiInterfaceType type)
 {
   switch (type) {
     case MAI_INTERFACE_COMPONENT:
@@ -97,23 +97,23 @@ static GType GetAtkTypeForMai(MaiInterfa
       return ATK_TYPE_IMAGE;
     case MAI_INTERFACE_TABLE_CELL:
       MOZ_ASSERT(false);
   }
   return G_TYPE_INVALID;
 }
 
 #define NON_USER_EVENT ":system"
-    
+
 // The atk interfaces we can expose without checking what version of ATK we are
 // dealing with.  At the moment AtkTableCell is the only interface we can't
 // always expose.
 static const GInterfaceInfo atk_if_infos[] = {
     {(GInterfaceInitFunc)componentInterfaceInitCB,
-     (GInterfaceFinalizeFunc) nullptr, nullptr}, 
+     (GInterfaceFinalizeFunc) nullptr, nullptr},
     {(GInterfaceInitFunc)actionInterfaceInitCB,
      (GInterfaceFinalizeFunc) nullptr, nullptr},
     {(GInterfaceInitFunc)valueInterfaceInitCB,
      (GInterfaceFinalizeFunc) nullptr, nullptr},
     {(GInterfaceInitFunc)editableTextInterfaceInitCB,
      (GInterfaceFinalizeFunc) nullptr, nullptr},
     {(GInterfaceInitFunc)hypertextInterfaceInitCB,
      (GInterfaceFinalizeFunc) nullptr, nullptr},
@@ -323,17 +323,17 @@ AccessibleWrap::GetAtkObject(void)
 
 // Get AtkObject from Accessible interface
 /* static */
 AtkObject *
 AccessibleWrap::GetAtkObject(Accessible* acc)
 {
     void *atkObjPtr = nullptr;
     acc->GetNativeInterface(&atkObjPtr);
-    return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;    
+    return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;
 }
 
 /* private */
 uint16_t
 AccessibleWrap::CreateMaiInterfaces(void)
 {
   uint16_t interfacesBits = 0;
 
@@ -367,17 +367,17 @@ AccessibleWrap::CreateMaiInterfaces(void
   // HyperLink interface.
   if (IsLink())
     interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
 
   if (!nsAccUtils::MustPrune(this)) {  // These interfaces require children
     // Table interface.
     if (AsTable())
       interfacesBits |= 1 << MAI_INTERFACE_TABLE;
- 
+
     if (AsTableCell())
       interfacesBits |= 1 << MAI_INTERFACE_TABLE_CELL;
 
     // Selection interface.
     if (IsSelect()) {
       interfacesBits |= 1 << MAI_INTERFACE_SELECTION;
     }
   }
@@ -692,16 +692,22 @@ getRoleCB(AtkObject *aAtkObj)
 #undef ROLE
 
   if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1))
     aAtkObj->role = ATK_ROLE_LIST;
   else if (aAtkObj->role == ATK_ROLE_TABLE_ROW && !IsAtkVersionAtLeast(2, 1))
     aAtkObj->role = ATK_ROLE_LIST_ITEM;
   else if (aAtkObj->role == ATK_ROLE_MATH && !IsAtkVersionAtLeast(2, 12))
     aAtkObj->role = ATK_ROLE_SECTION;
+  else if (aAtkObj->role == ATK_ROLE_COMMENT && !IsAtkVersionAtLeast(2, 12))
+    aAtkObj->role = ATK_ROLE_SECTION;
+  else if (aAtkObj->role == ATK_ROLE_LANDMARK && !IsAtkVersionAtLeast(2, 12))
+    aAtkObj->role = ATK_ROLE_SECTION;
+  else if (aAtkObj->role == ATK_ROLE_FOOTNOTE && !IsAtkVersionAtLeast(2, 25, 2))
+    aAtkObj->role = ATK_ROLE_SECTION;
   else if (aAtkObj->role == ATK_ROLE_STATIC && !IsAtkVersionAtLeast(2, 16))
     aAtkObj->role = ATK_ROLE_TEXT;
   else if ((aAtkObj->role == ATK_ROLE_MATH_FRACTION ||
             aAtkObj->role == ATK_ROLE_MATH_ROOT) && !IsAtkVersionAtLeast(2, 16))
     aAtkObj->role = ATK_ROLE_SECTION;
 
   return aAtkObj->role;
 }
@@ -743,27 +749,18 @@ ConvertToAtkAttributeSet(nsIPersistentPr
     //libspi will free it
     return objAttributeSet;
 }
 
 AtkAttributeSet*
 GetAttributeSet(Accessible* aAccessible)
 {
   nsCOMPtr<nsIPersistentProperties> attributes = aAccessible->Attributes();
-  if (attributes) {
-    // There is no ATK state for haspopup, must use object attribute to expose
-    // the same info.
-    if (aAccessible->State() & states::HASPOPUP) {
-      nsAutoString unused;
-      attributes->SetStringProperty(NS_LITERAL_CSTRING("haspopup"),
-                                    NS_LITERAL_STRING("true"), unused);
-    }
-
+  if (attributes)
     return ConvertToAtkAttributeSet(attributes);
-  }
 
   return nullptr;
 }
 
 AtkAttributeSet *
 getAttributesCB(AtkObject *aAtkObj)
 {
   AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
--- a/accessible/atk/DocAccessibleWrap.h
+++ b/accessible/atk/DocAccessibleWrap.h
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* For documentation of the accessibility architecture, 
+/* For documentation of the accessibility architecture,
  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
  */
 
 #ifndef mozilla_a11y_DocAccessibleWrap_h__
 #define mozilla_a11y_DocAccessibleWrap_h__
 
 #include "DocAccessible.h"
 
--- a/accessible/atk/Platform.cpp
+++ b/accessible/atk/Platform.cpp
@@ -21,17 +21,17 @@
 
 #if (MOZ_WIDGET_GTK == 3)
 extern "C" __attribute__((weak,visibility("default"))) int atk_bridge_adaptor_init(int*, char **[]);
 #endif
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
-int atkMajorVersion = 1, atkMinorVersion = 12;
+int atkMajorVersion = 1, atkMinorVersion = 12, atkMicroVersion = 0;
 
 GType (*gAtkTableCellGetTypeFunc)();
 
 extern "C" {
 typedef GType (* AtkGetTypeType) (void);
 typedef void (*GnomeAccessibilityInit) (void);
 typedef void (*GnomeAccessibilityShutdown) (void);
 }
@@ -163,18 +163,21 @@ a11y::PlatformInit()
 
   const char* (*atkGetVersion)() =
     (const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
   if (atkGetVersion) {
     const char* version = atkGetVersion();
     if (version) {
       char* endPtr = nullptr;
       atkMajorVersion = strtol(version, &endPtr, 10);
-      if (*endPtr == '.')
+      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)();
--- a/accessible/atk/TextLeafAccessibleWrap.h
+++ b/accessible/atk/TextLeafAccessibleWrap.h
@@ -6,15 +6,15 @@
 
 #ifndef mozilla_a11y_TextLeafAccessibleWrap_h__
 #define mozilla_a11y_TextLeafAccessibleWrap_h__
 
 #include "TextLeafAccessible.h"
 
 namespace mozilla {
 namespace a11y {
- 
+
 typedef class TextLeafAccessible TextLeafAccessibleWrap;
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/atk/nsMai.h
+++ b/accessible/atk/nsMai.h
@@ -70,27 +70,29 @@ typedef struct _MaiAtkSocketClass
 extern "C" GType (*gAtkTableCellGetTypeFunc)();
 
 mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
 mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
 mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);
 AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
 AtkObject* GetWrapperFor(mozilla::a11y::AccessibleOrProxy aObj);
 
-extern int atkMajorVersion, atkMinorVersion;
+extern int atkMajorVersion, atkMinorVersion, atkMicroVersion;
 
 /**
  * Return true if the loaded version of libatk-1.0.so is at least
- * aMajor.aMinor.0.
+ * aMajor.aMinor.aMicro.
  */
 static inline bool
-IsAtkVersionAtLeast(int aMajor, int aMinor)
+IsAtkVersionAtLeast(int aMajor, int aMinor, int aMicro=0)
 {
   return aMajor < atkMajorVersion ||
-         (aMajor == atkMajorVersion && aMinor <= atkMinorVersion);
+         (aMajor == atkMajorVersion &&
+          (aMinor < atkMinorVersion ||
+           (aMinor == atkMinorVersion && aMicro <= atkMicroVersion)));
 }
 
 // This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a
 // proxy.
 static const uintptr_t IS_PROXY = 1;
 
 /**
  * This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject
--- a/accessible/atk/nsMaiInterfaceText.cpp
+++ b/accessible/atk/nsMaiInterfaceText.cpp
@@ -121,17 +121,17 @@ ConvertToAtkTextAttributeSet(nsIPersiste
 
   // libatk-adaptor will free it
   return objAttributeSet;
 }
 
 static void
 ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString)
 {
-  // convert each char to "*" when it's "password text" 
+  // convert each char to "*" when it's "password text"
   if (accWrap->NativeRole() == roles::PASSWORD_TEXT) {
     for (uint32_t i = 0; i < aString.Length(); i++)
       aString.Replace(i, 1, NS_LITERAL_STRING("*"));
   }
 }
 
 extern "C" {
 
--- a/accessible/atk/nsStateMap.h
+++ b/accessible/atk/nsStateMap.h
@@ -18,28 +18,25 @@ The following accessible states aren't t
                          The nsIAccessible state is not currently supported.
   STATE_SELFVOICING:     No ATK equivalent -- the object has self-TTS.
                          The nsIAccessible state is not currently supported.
   STATE_LINKED:          The object is formatted as a hyperlink. Supported via ATK_ROLE_LINK.
   STATE_EXTSELECTABLE:   Indicates that an object extends its selection.
                          This is supported via STATE_MULTISELECTABLE.
   STATE_PROTECTED:       The object is a password-protected edit control.
                          Supported via ATK_ROLE_PASSWORD_TEXT
-  STATE_HASPOPUP:        Object displays a pop-up menu or window when invoked.
-                         No ATK equivalent. The accessible state is not
-                         currently supported.
   STATE_PINNED:          The object is pinned, usually indicating it is fixed in
                          place and has permanence. No ATK equivalent. The
                          accessible state is not currently supported.
 
 The following ATK states are not supported:
   ATK_STATE_ARMED:       No clear use case, used briefly when button is activated
   ATK_STATE_HAS_TOOLTIP: No clear use case, no IA2 equivalent
   ATK_STATE_ICONIFIED:   Mozilla does not have elements which are collapsable into icons
-  ATK_STATE_TRUNCATED:   No clear use case. Indicates that an object's onscreen content is truncated, 
+  ATK_STATE_TRUNCATED:   No clear use case. Indicates that an object's onscreen content is truncated,
                          e.g. a text value in a spreadsheet cell. No IA2 state.
 ******************************************************************************/
 
 enum EStateMapEntryType {
   kMapDirectly,
   kMapOpposite,   // For example, UNAVAILABLE is the opposite of ENABLED
   kNoStateChange, // Don't fire state change event
   kNoSuchState
@@ -90,17 +87,17 @@ static const AtkStateMap gAtkStateMap[] 
   { kNone,                                    kMapDirectly },   // states::LINKED                  = 1 << 22
   { ATK_STATE_VISITED,                        kMapDirectly },   // states::TRAVERSED               = 1 << 23
   { ATK_STATE_MULTISELECTABLE,                kMapDirectly },   // states::MULTISELECTABLE         = 1 << 24
   { kNone,                                    kMapDirectly },   // states::EXTSELECTABLE           = 1 << 25
   { ATK_STATE_REQUIRED,                       kMapDirectly },   // states::STATE_REQUIRED          = 1 << 26
   { kNone,                                    kMapDirectly },   // states::ALERT_MEDIUM            = 1 << 27
   { ATK_STATE_INVALID_ENTRY,                  kMapDirectly },   // states::INVALID                 = 1 << 28
   { kNone,                                    kMapDirectly },   // states::PROTECTED               = 1 << 29
-  { kNone,                                    kMapDirectly },   // states::HASPOPUP                = 1 << 30
+  { ATK_STATE_HAS_POPUP,                      kMapDirectly },   // states::HASPOPUP                = 1 << 30
   { ATK_STATE_SUPPORTS_AUTOCOMPLETION,        kMapDirectly },   // states::SUPPORTS_AUTOCOMPLETION = 1 << 31
   { ATK_STATE_DEFUNCT,                        kMapDirectly },   // states::DEFUNCT                 = 1 << 32
   { ATK_STATE_SELECTABLE_TEXT,                kMapDirectly },   // states::SELECTABLE_TEXT         = 1 << 33
   { ATK_STATE_EDITABLE,                       kMapDirectly },   // states::EDITABLE                = 1 << 34
   { ATK_STATE_ACTIVE,                         kMapDirectly },   // states::ACTIVE                  = 1 << 35
   { ATK_STATE_MODAL,                          kMapDirectly },   // states::MODAL                   = 1 << 36
   { ATK_STATE_MULTI_LINE,                     kMapDirectly },   // states::MULTI_LINE              = 1 << 37
   { ATK_STATE_HORIZONTAL,                     kMapDirectly },   // states::HORIZONTAL              = 1 << 38
@@ -108,10 +105,11 @@ static const AtkStateMap gAtkStateMap[] 
   { ATK_STATE_SINGLE_LINE,                    kMapDirectly },   // states::SINGLE_LINE             = 1 << 40
   { ATK_STATE_TRANSIENT,                      kMapDirectly },   // states::TRANSIENT               = 1 << 41
   { ATK_STATE_VERTICAL,                       kMapDirectly },   // states::VERTICAL                = 1 << 42
   { ATK_STATE_STALE,                          kMapDirectly },   // states::STALE                   = 1 << 43
   { ATK_STATE_ENABLED,                        kMapDirectly },   // states::ENABLED                 = 1 << 44
   { ATK_STATE_SENSITIVE,                      kMapDirectly },   // states::SENSITIVE               = 1 << 45
   { ATK_STATE_EXPANDABLE,                     kMapDirectly },   // states::EXPANDABLE              = 1 << 46
   { kNone,                                    kMapDirectly },   // states::PINNED                  = 1 << 47
-  { kNone,                                    kNoSuchState },   //                                 = 1 << 48
+  { ATK_STATE_ACTIVE,                         kMapDirectly },   // states::CURRENT                 = 1 << 48
+  { kNone,                                    kNoSuchState },   //                                 = 1 << 49
 };
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -64,17 +64,17 @@ static const nsRoleMapEntry sWAIRoleMaps
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eLandmark,
     kNoReqStates
   },
   { // article
     &nsGkAtoms::article,
-    roles::DOCUMENT,
+    roles::ARTICLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eReadonlyUntilEditable
   },
@@ -128,25 +128,25 @@ static const nsRoleMapEntry sWAIRoleMaps
     eNoValue,
     eSortAction,
     eNoLiveAttr,
     eTableCell,
     kNoReqStates,
     eARIASelectableIfDefined,
     eARIAReadonlyOrEditableIfDefined
   },
-  { // combobox
+  { // combobox, which consists of text input and popup
     &nsGkAtoms::combobox,
-    roles::COMBOBOX,
+    roles::EDITCOMBOBOX,
     kUseMapRole,
     eNoValue,
     eOpenCloseAction,
     eNoLiveAttr,
-    kGenericAccType,
-    states::COLLAPSED | states::HASPOPUP | states::VERTICAL,
+    eCombobox,
+    states::COLLAPSED | states::HASPOPUP,
     eARIAAutoComplete,
     eARIAReadonly,
     eARIAOrientation
   },
   { // complementary
     &nsGkAtoms::complementary,
     roles::NOTHING,
     kUseNativeRole,
@@ -181,16 +181,406 @@ static const nsRoleMapEntry sWAIRoleMaps
     roles::LIST,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eList,
     states::READONLY
   },
+  { // doc-abstract
+    &nsGkAtoms::docAbstract,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-acknowledgments
+    &nsGkAtoms::docAcknowledgments,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-afterword
+    &nsGkAtoms::docAfterword,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-appendix
+    &nsGkAtoms::docAppendix,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-backlink
+    &nsGkAtoms::docBacklink,
+    roles::LINK,
+    kUseMapRole,
+    eNoValue,
+    eJumpAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::LINKED
+  },
+  { // doc-biblioentry
+    &nsGkAtoms::docBiblioentry,
+    roles::LISTITEM,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::READONLY
+  },
+  { // doc-bibliography
+    &nsGkAtoms::docBibliography,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-biblioref
+    &nsGkAtoms::docBiblioref,
+    roles::LINK,
+    kUseMapRole,
+    eNoValue,
+    eJumpAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::LINKED
+  },
+  { // doc-chapter
+    &nsGkAtoms::docChapter,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-colophon
+    &nsGkAtoms::docColophon,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-conclusion
+    &nsGkAtoms::docConclusion,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-cover
+    &nsGkAtoms::docCover,
+    roles::GRAPHIC,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-credit
+    &nsGkAtoms::docCredit,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-credits
+    &nsGkAtoms::docCredits,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-dedication
+    &nsGkAtoms::docDedication,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-endnote
+    &nsGkAtoms::docEndnote,
+    roles::LISTITEM,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::READONLY
+  },
+  { // doc-endnotes
+    &nsGkAtoms::docEndnotes,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-epigraph
+    &nsGkAtoms::docEpigraph,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-epilogue
+    &nsGkAtoms::docEpilogue,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-errata
+    &nsGkAtoms::docErrata,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-example
+    &nsGkAtoms::docExample,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-footnote
+    &nsGkAtoms::docFootnote,
+    roles::FOOTNOTE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-foreword
+    &nsGkAtoms::docForeword,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-glossary
+    &nsGkAtoms::docGlossary,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-glossref
+    &nsGkAtoms::docGlossref,
+    roles::LINK,
+    kUseMapRole,
+    eNoValue,
+    eJumpAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::LINKED
+  },
+  { // doc-index
+    &nsGkAtoms::docIndex,
+    roles::NAVIGATION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-introduction
+    &nsGkAtoms::docIntroduction,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-noteref
+    &nsGkAtoms::docNoteref,
+    roles::LINK,
+    kUseMapRole,
+    eNoValue,
+    eJumpAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::LINKED
+  },
+  { // doc-notice
+    &nsGkAtoms::docNotice,
+    roles::NOTE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-pagebreak
+    &nsGkAtoms::docPagebreak,
+    roles::SEPARATOR,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-pagelist
+    &nsGkAtoms::docPagelist,
+    roles::NAVIGATION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-part
+    &nsGkAtoms::docPart,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-preface
+    &nsGkAtoms::docPreface,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-prologue
+    &nsGkAtoms::docPrologue,
+    roles::LANDMARK,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
+  { // doc-pullquote
+    &nsGkAtoms::docPullquote,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-qna
+    &nsGkAtoms::docQna,
+    roles::SECTION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-subtitle
+    &nsGkAtoms::docSubtitle,
+    roles::HEADING,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-tip
+    &nsGkAtoms::docTip,
+    roles::NOTE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
+  { // doc-toc
+    &nsGkAtoms::docToc,
+    roles::NAVIGATION,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    eLandmark,
+    kNoReqStates
+  },
   { // document
     &nsGkAtoms::document,
     roles::DOCUMENT,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
@@ -202,16 +592,26 @@ static const nsRoleMapEntry sWAIRoleMaps
     roles::GROUPING,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates
   },
+  { // figure
+    &nsGkAtoms::figure,
+    roles::FIGURE,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    kNoReqStates
+  },
   { // form
     &nsGkAtoms::form,
     roles::FORM,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eLandmark,
@@ -393,40 +793,41 @@ static const nsRoleMapEntry sWAIRoleMaps
   { // menuitem
     &nsGkAtoms::menuitem,
     roles::MENUITEM,
     kUseMapRole,
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kGenericAccType,
-    kNoReqStates,
-    eARIACheckedMixed
+    kNoReqStates
   },
   { // menuitemcheckbox
     &nsGkAtoms::menuitemcheckbox,
     roles::CHECK_MENU_ITEM,
     kUseMapRole,
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
-    eARIACheckableMixed
+    eARIACheckableMixed,
+    eARIAReadonly
   },
   { // menuitemradio
     &nsGkAtoms::menuitemradio,
     roles::RADIO_MENU_ITEM,
     kUseMapRole,
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
-    eARIACheckableBool
+    eARIACheckableBool,
+    eARIAReadonly
   },
   { // navigation
     &nsGkAtoms::navigation,
     roles::NOTHING,
     kUseNativeRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
@@ -501,26 +902,27 @@ static const nsRoleMapEntry sWAIRoleMaps
     &nsGkAtoms::radiogroup,
     roles::RADIO_GROUP,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
-    eARIAOrientation
+    eARIAOrientation,
+    eARIAReadonly
   },
   { // region
     &nsGkAtoms::region,
-    roles::PANE,
+    roles::REGION,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
-    kGenericAccType,
+    eLandmark,
     kNoReqStates
   },
   { // row
     &nsGkAtoms::row,
     roles::ROW,
     kUseMapRole,
     eNoValue,
     eNoAction,
@@ -585,17 +987,17 @@ static const nsRoleMapEntry sWAIRoleMaps
     eARIAAutoComplete,
     eARIAMultiline,
     eARIAReadonlyOrEditable
   },
   { // separator
     &nsGkAtoms::separator_,
     roles::SEPARATOR,
     kUseMapRole,
-    eNoValue,
+    eHasValueMinMaxIfFocusable,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     states::HORIZONTAL,
     eARIAOrientation
   },
   { // slider
     &nsGkAtoms::slider,
@@ -634,17 +1036,18 @@ static const nsRoleMapEntry sWAIRoleMaps
     &nsGkAtoms::_switch,
     roles::SWITCH,
     kUseMapRole,
     eNoValue,
     eCheckUncheckAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
-    eARIACheckableBool
+    eARIACheckableBool,
+    eARIAReadonly
   },
   { // tab
     &nsGkAtoms::tab,
     roles::PAGETAB,
     kUseMapRole,
     eNoValue,
     eSwitchAction,
     eNoLiveAttr,
@@ -679,16 +1082,26 @@ static const nsRoleMapEntry sWAIRoleMaps
     roles::PROPERTYPAGE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates
   },
+  { // term
+    &nsGkAtoms::term,
+    roles::TERM,
+    kUseMapRole,
+    eNoValue,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::READONLY
+  },
   { // textbox
     &nsGkAtoms::textbox,
     roles::ENTRY,
     kUseMapRole,
     eNoValue,
     eActivateAction,
     eNoLiveAttr,
     kGenericAccType,
@@ -744,17 +1157,17 @@ static const nsRoleMapEntry sWAIRoleMaps
   { // treegrid
     &nsGkAtoms::treegrid,
     roles::TREE_TABLE,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eSelect | eTable,
-    states::VERTICAL,
+    kNoReqStates,
     eARIAReadonlyOrEditable,
     eARIAMultiSelectable,
     eFocusableUntilDisabled,
     eARIAOrientation
   },
   { // treeitem
     &nsGkAtoms::treeitem,
     roles::OUTLINEITEM,
@@ -794,16 +1207,17 @@ nsRoleMapEntry aria::gEmptyRoleMap = {
 
 /**
  * Universal (Global) states:
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
 static const EStateRule sWAIUnivStateMap[] = {
   eARIABusy,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,  // Currently under spec review but precedent exists
   eARIAHasPopup,  // Note this is technically a "property"
   eARIAInvalid,
   eARIAModal,
   eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
--- a/accessible/base/ARIAMap.h
+++ b/accessible/base/ARIAMap.h
@@ -29,17 +29,25 @@ enum EValueRule
    * Value interface isn't exposed.
    */
   eNoValue,
 
   /**
    * Value interface is implemented, supports value, min and max from
    * aria-valuenow, aria-valuemin and aria-valuemax.
    */
-  eHasValueMinMax
+  eHasValueMinMax,
+
+  /**
+   * Value interface is implemented, but only if the element is focusable.
+   * For instance, in ARIA 1.1 the ability for authors to create adjustable
+   * splitters was provided by supporting the value interface on separators
+   * that are focusable. Non-focusable separators expose no value information.
+   */
+  eHasValueMinMaxIfFocusable
 };
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Action constants
 
 /**
  * Used to define if the role requires to expose action.
--- a/accessible/base/ARIAStateMap.cpp
+++ b/accessible/base/ARIAStateMap.cpp
@@ -140,16 +140,26 @@ aria::MapToState(EStateRule aRule, dom::
       static const TokenTypeData data(
         nsGkAtoms::aria_checked, eMixedType,
         states::CHECKABLE, states::CHECKED);
 
       MapTokenType(aElement, aState, data);
       return true;
     }
 
+    case eARIACurrent:
+    {
+      static const TokenTypeData data(
+        nsGkAtoms::aria_current, eBoolType,
+        0, states::CURRENT);
+
+      MapTokenType(aElement, aState, data);
+      return true;
+    }
+
     case eARIADisabled:
     {
       static const TokenTypeData data(
         nsGkAtoms::aria_disabled, eBoolType,
         0, states::UNAVAILABLE);
 
       MapTokenType(aElement, aState, data);
       return true;
@@ -349,20 +359,22 @@ MapEnumType(dom::Element* aElement, uint
   }
 }
 
 static void
 MapTokenType(dom::Element* aElement, uint64_t* aState,
              const TokenTypeData& aData)
 {
   if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
-    if ((aData.mType & eMixedType) &&
-        aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
+    if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::mixed, eCaseMatters)) {
-      *aState |= aData.mPermanentState | states::MIXED;
+      if (aData.mType & eMixedType)
+        *aState |= aData.mPermanentState | states::MIXED;
+      else // unsupported use of 'mixed' is an authoring error
+        *aState |= aData.mPermanentState | aData.mFalseState;
       return;
     }
 
     if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
                               nsGkAtoms::_false, eCaseMatters)) {
       *aState |= aData.mPermanentState | aData.mFalseState;
       return;
     }
--- a/accessible/base/ARIAStateMap.h
+++ b/accessible/base/ARIAStateMap.h
@@ -24,16 +24,17 @@ namespace aria {
 enum EStateRule
 {
   eARIANone,
   eARIAAutoComplete,
   eARIABusy,
   eARIACheckableBool,
   eARIACheckableMixed,
   eARIACheckedMixed,
+  eARIACurrent,
   eARIADisabled,
   eARIAExpanded,
   eARIAHasPopup,
   eARIAInvalid,
   eARIAModal,
   eARIAMultiline,
   eARIAMultiSelectable,
   eARIAOrientation,
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -87,20 +87,20 @@ DocManager::FindAccessibleInCache(nsINod
 
 void
 DocManager::RemoveFromXPCDocumentCache(DocAccessible* aDocument)
 {
   xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
   if (xpcDoc) {
     xpcDoc->Shutdown();
     mXPCDocumentCache.Remove(aDocument);
-  }
 
-  if (!HasXPCDocuments()) {
-    MaybeShutdownAccService(nsAccessibilityService::eXPCOM);
+    if (!HasXPCDocuments()) {
+      MaybeShutdownAccService(nsAccessibilityService::eXPCOM);
+    }
   }
 }
 
 void
 DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
                                      nsIDocument* aDOMDocument)
 {
   // We need to remove listeners in both cases, when document is being shutdown
--- a/accessible/base/FocusManager.cpp
+++ b/accessible/base/FocusManager.cpp
@@ -10,16 +10,17 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsEventShell.h"
 #include "Role.h"
 
 #include "nsFocusManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
 
 namespace mozilla {
 namespace a11y {
 
 FocusManager::FocusManager()
 {
 }
 
@@ -30,17 +31,17 @@ FocusManager::~FocusManager()
 Accessible*
 FocusManager::FocusedAccessible() const
 {
   if (mActiveItem)
     return mActiveItem;
 
   nsINode* focusedNode = FocusedDOMNode();
   if (focusedNode) {
-    DocAccessible* doc = 
+    DocAccessible* doc =
       GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
     return doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr;
   }
 
   return nullptr;
 }
 
 bool
@@ -53,17 +54,17 @@ FocusManager::IsFocused(const Accessible
   if (focusedNode) {
     // XXX: Before getting an accessible for node having a DOM focus make sure
     // they belong to the same document because it can trigger unwanted document
     // accessible creation for temporary about:blank document. Without this
     // peculiarity we would end up with plain implementation based on
     // FocusedAccessible() method call. Make sure this issue is fixed in
     // bug 638465.
     if (focusedNode->OwnerDoc() == aAccessible->GetNode()->OwnerDoc()) {
-      DocAccessible* doc = 
+      DocAccessible* doc =
         GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
       return aAccessible ==
         (doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr);
     }
   }
   return false;
 }
 
@@ -185,22 +186,42 @@ FocusManager::ActiveItemChanged(Accessib
     if (logging::IsEnabled(logging::eFocus))
       logging::ActiveWidget(widget);
 #endif
     if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
       return;
   }
   mActiveItem = aItem;
 
+  // If mActiveItem is null we may need to shift a11y focus back to a tab
+  // document. For example, when combobox popup is closed, then
+  // the focus should be moved back to the combobox.
+  if (!mActiveItem && XRE_IsParentProcess()) {
+    nsFocusManager* domfm = nsFocusManager::GetFocusManager();
+    if (domfm) {
+      nsIContent* focusedElm = domfm->GetFocusedContent();
+      if (EventStateManager::IsRemoteTarget(focusedElm)) {
+        dom::TabParent* tab = dom::TabParent::GetFrom(focusedElm);
+        if (tab) {
+          a11y::DocAccessibleParent* dap = tab->GetTopLevelDocAccessible();
+          if (dap) {
+            Unused << dap->SendRestoreFocus();
+          }
+        }
+      }
+    }
+  }
+
   // If active item is changed then fire accessible focus event on it, otherwise
   // if there's no an active item then fire focus event to accessible having
   // DOM focus.
   Accessible* target = FocusedAccessible();
-  if (target)
+  if (target) {
     DispatchFocusEvent(target->Document(), target);
+  }
 }
 
 void
 FocusManager::ForceFocusEvent()
 {
   nsINode* focusedNode = FocusedDOMNode();
   if (focusedNode) {
     DocAccessible* document =
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -391,17 +391,17 @@ static const char* sDocCreateTitle = "DO
 static const char* sDocDestroyTitle = "DOCDESTROY";
 static const char* sDocEventTitle = "DOCEVENT";
 static const char* sFocusTitle = "FOCUS";
 
 void
 logging::DocLoad(const char* aMsg, nsIWebProgress* aWebProgress,
                  nsIRequest* aRequest, uint32_t aStateFlags)
 {
-  MsgBegin(sDocLoadTitle, aMsg);
+  MsgBegin(sDocLoadTitle, "%s", aMsg);
 
   nsCOMPtr<mozIDOMWindowProxy> DOMWindow;
   aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));
   nsPIDOMWindowOuter* window = nsPIDOMWindowOuter::From(DOMWindow);
   if (!window) {
     MsgEnd();
     return;
   }
@@ -428,17 +428,17 @@ logging::DocLoad(const char* aMsg, nsIWe
   printf(", document is %sloading\n", (isDocLoading ? "" : "not "));
 
   MsgEnd();
 }
 
 void
 logging::DocLoad(const char* aMsg, nsIDocument* aDocumentNode)
 {
-  MsgBegin(sDocLoadTitle, aMsg);
+  MsgBegin(sDocLoadTitle, "%s", aMsg);
 
   DocAccessible* document = GetExistingDocAccessible(aDocumentNode);
   LogDocInfo(aDocumentNode, document);
 
   MsgEnd();
 }
 
 void
@@ -492,64 +492,64 @@ logging::DocLoadEventHandled(AccEvent* a
 
 void
 logging::DocCreate(const char* aMsg, nsIDocument* aDocumentNode,
                    DocAccessible* aDocument)
 {
   DocAccessible* document = aDocument ?
     aDocument : GetExistingDocAccessible(aDocumentNode);
 
-  MsgBegin(sDocCreateTitle, aMsg);
+  MsgBegin(sDocCreateTitle, "%s", aMsg);
   LogDocInfo(aDocumentNode, document);
   MsgEnd();
 }
 
 void
 logging::DocDestroy(const char* aMsg, nsIDocument* aDocumentNode,
                     DocAccessible* aDocument)
 {
   DocAccessible* document = aDocument ?
     aDocument : GetExistingDocAccessible(aDocumentNode);
 
-  MsgBegin(sDocDestroyTitle, aMsg);
+  MsgBegin(sDocDestroyTitle, "%s", aMsg);
   LogDocInfo(aDocumentNode, document);
   MsgEnd();
 }
 
 void
 logging::OuterDocDestroy(OuterDocAccessible* aOuterDoc)
 {
   MsgBegin(sDocDestroyTitle, "outerdoc shutdown");
   logging::Address("outerdoc", aOuterDoc);
   MsgEnd();
 }
 
 void
 logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
                                  Accessible* aTarget)
 {
-  MsgBegin(sFocusTitle, aMsg);
+  MsgBegin(sFocusTitle, "%s", aMsg);
   AccessibleNNode(aTargetDescr, aTarget);
   MsgEnd();
 }
 
 void
 logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
                                  nsINode* aTargetNode)
 {
-  MsgBegin(sFocusTitle, aMsg);
+  MsgBegin(sFocusTitle, "%s", aMsg);
   Node(aTargetDescr, aTargetNode);
   MsgEnd();
 }
 
 void
 logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
                                  nsISupports* aTargetThing)
 {
-  MsgBegin(sFocusTitle, aMsg);
+  MsgBegin(sFocusTitle, "%s", aMsg);
 
   if (aTargetThing) {
     nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTargetThing));
     if (targetNode)
       AccessibleNNode(aTargetDescr, targetNode);
     else
       printf("    %s: %p, window\n", aTargetDescr,
              static_cast<void*>(aTargetThing));
@@ -623,17 +623,17 @@ logging::TreeInfo(const char* aMsg, uint
       Accessible* acc = va_arg(vl, Accessible*);
       MsgBegin("TREE", "%s; doc: %p", aMsg, acc ? acc->Document() : nullptr);
       AccessibleInfo(descr, acc);
       while ((descr = va_arg(vl, const char*))) {
         AccessibleInfo(descr, va_arg(vl, Accessible*));
       }
     }
     else {
-      MsgBegin("TREE", aMsg);
+      MsgBegin("TREE", "%s", aMsg);
     }
     va_end(vl);
     MsgEnd();
 
     if (aExtraFlags & eStack) {
       Stack();
     }
   }
@@ -672,17 +672,17 @@ logging::TreeInfo(const char* aMsg, uint
   }
 }
 
 void
 logging::Tree(const char* aTitle, const char* aMsgText,
               Accessible* aRoot, GetTreePrefix aPrefixFunc,
               void* aGetTreePrefixData)
 {
-  logging::MsgBegin(aTitle, aMsgText);
+  logging::MsgBegin(aTitle, "%s", aMsgText);
 
   nsAutoString level;
   Accessible* root = aRoot;
   do {
     const char* prefix = aPrefixFunc ? aPrefixFunc(aGetTreePrefixData, root) : "";
     printf("%s", NS_ConvertUTF16toUTF8(level).get());
     logging::AccessibleInfo(prefix, root);
     if (root->FirstChild() && !root->FirstChild()->IsDoc()) {
@@ -712,17 +712,17 @@ logging::Tree(const char* aTitle, const 
 
   logging::MsgEnd();
 }
 
 void
 logging::DOMTree(const char* aTitle, const char* aMsgText,
                  DocAccessible* aDocument)
 {
-  logging::MsgBegin(aTitle, aMsgText);
+  logging::MsgBegin(aTitle, "%s", aMsgText);
   nsAutoString level;
   nsINode* root = aDocument->DocumentNode();
   do {
     printf("%s", NS_ConvertUTF16toUTF8(level).get());
     logging::Node("", root);
     if (root->GetFirstChild()) {
       level.Append(NS_LITERAL_STRING("  "));
       root = root->GetFirstChild();
@@ -1021,17 +1021,17 @@ logging::IsEnabled(const nsAString& aMod
     if (aModuleStr.EqualsASCII(sModuleMap[idx].mStr))
       return sModules & sModuleMap[idx].mModule;
   }
 
   return false;
 }
 
 void
-logging::Enable(const nsAFlatCString& aModules)
+logging::Enable(const nsCString& aModules)
 {
   EnableLogging(aModules.get());
 }
 
 
 void
 logging::CheckEnv()
 {
--- a/accessible/base/Logging.h
+++ b/accessible/base/Logging.h
@@ -4,16 +4,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 mozilla_a11y_logs_h__
 #define mozilla_a11y_logs_h__
 
 #include "nscore.h"
 #include "nsStringFwd.h"
+#include "mozilla/Attributes.h"
 
 class nsIDocument;
 class nsINode;
 class nsIRequest;
 class nsISelection;
 class nsISupports;
 class nsIWebProgress;
 
@@ -147,30 +148,30 @@ void Tree(const char* aTitle, const char
           GetTreePrefix aPrefixFunc = nullptr, void* aGetTreePrefixData = nullptr);
 void DOMTree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc);
 
 /**
  * Log the message ('title: text' format) on new line. Print the start and end
  * boundaries of the message body designated by '{' and '}' (2 spaces indent for
  * body).
  */
-void MsgBegin(const char* aTitle, const char* aMsgText, ...);
+void MsgBegin(const char* aTitle, const char* aMsgText, ...) MOZ_FORMAT_PRINTF(2, 3);
 void MsgEnd();
 
 /**
  * Print start and end boundaries of the message body designated by '{' and '}'
  * (2 spaces indent for body).
  */
 void SubMsgBegin();
 void SubMsgEnd();
 
 /**
  * Log the entry into message body (4 spaces indent).
  */
-void MsgEntry(const char* aEntryText, ...);
+void MsgEntry(const char* aEntryText, ...) MOZ_FORMAT_PRINTF(1, 2);
 
 /**
  * Log the text, two spaces offset is used.
  */
 void Text(const char* aText);
 
 /**
  * Log the accessible object address as message entry (4 spaces indent).
@@ -203,17 +204,17 @@ void DOMEvent(const char* aDescr, nsINod
 /**
  * Log the call stack, two spaces offset is used.
  */
 void Stack();
 
 /**
  * Enable logging of the specified modules, all other modules aren't logged.
  */
-void Enable(const nsAFlatCString& aModules);
+void Enable(const nsCString& aModules);
 
 /**
  * Enable logging of modules specified by A11YLOG environment variable,
  * all other modules aren't logged.
  */
 void CheckEnv();
 
 } // namespace logging
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -14,17 +14,17 @@ MARKUPMAP(abbr,
           0)
 
 MARKUPMAP(acronym,
           New_HyperText,
           0)
 
 MARKUPMAP(article,
           New_HyperText,
-          roles::DOCUMENT,
+          roles::ARTICLE,
           Attr(xmlroles, article))
 
 MARKUPMAP(aside,
           New_HyperText,
           roles::NOTE)
 
 MARKUPMAP(blockquote,
           New_HyperText,
@@ -90,16 +90,20 @@ MARKUPMAP(h4,
 MARKUPMAP(h5,
           New_HyperText,
           roles::HEADING)
 
 MARKUPMAP(h6,
           New_HyperText,
           roles::HEADING)
 
+MARKUPMAP(input,
+          New_HTMLInput,
+          0)
+
 MARKUPMAP(label,
           New_HTMLLabel,
           roles::LABEL)
 
 MARKUPMAP(legend,
           New_HTMLLegend,
           roles::LABEL)
 
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -585,18 +585,17 @@ NotificationController::ProcessMutationE
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector: private
 
 void
 NotificationController::WillRefresh(mozilla::TimeStamp aTime)
 {
-  PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
-  Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
+  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;
 
@@ -652,18 +651,20 @@ NotificationController::WillRefresh(mozi
 
     nsIFrame* textFrame = textNode->GetPrimaryFrame();
     if (!textFrame) {
       NS_ASSERTION(!textAcc,
                    "Text node isn't rendered but accessible is kept alive!");
       continue;
     }
 
+  #ifdef A11Y_LOG
     nsIContent* containerElm = containerNode->IsElement() ?
       containerNode->AsElement() : nullptr;
+  #endif
 
     nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
         UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
         nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
 
     // Remove text accessible if rendered text is empty.
     if (textAcc) {
       if (text.mString.IsEmpty()) {
@@ -671,17 +672,17 @@ NotificationController::WillRefresh(mozi
         if (logging::IsEnabled(logging::eTree | logging::eText)) {
           logging::MsgBegin("TREE", "text node lost its content; doc: %p", mDocument);
           logging::Node("container", containerElm);
           logging::Node("content", textNode);
           logging::MsgEnd();
         }
   #endif
 
-        mDocument->ContentRemoved(containerElm, textNode);
+        mDocument->ContentRemoved(textAcc);
         continue;
       }
 
       // Update text of the accessible and fire text change events.
   #ifdef A11Y_LOG
       if (logging::IsEnabled(logging::eText)) {
         logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
         logging::Node("container", containerElm);
@@ -725,24 +726,31 @@ NotificationController::WillRefresh(mozi
   for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
     mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
     if (!mDocument) {
       return;
     }
   }
   mContentInsertions.Clear();
 
-  // Bind hanging child documents.
+  // Bind hanging child documents unless we are using IPC and the
+  // document has no IPC actor.  If we fail to bind the child doc then
+  // shut it down.
   uint32_t hangingDocCnt = mHangingChildDocuments.Length();
   nsTArray<RefPtr<DocAccessible>> newChildDocs;
   for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
     DocAccessible* childDoc = mHangingChildDocuments[idx];
     if (childDoc->IsDefunct())
       continue;
 
+    if (IPCAccessibilityActive() && !mDocument->IPCDoc()) {
+      childDoc->Shutdown();
+      continue;
+    }
+
     nsIContent* ownerContent = mDocument->DocumentNode()->
       FindContentForSubDocument(childDoc->DocumentNode());
     if (ownerContent) {
       Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
       if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
         if (mDocument->AppendChildDocument(childDoc)) {
           newChildDocs.AppendElement(Move(mHangingChildDocuments[idx]));
           continue;
@@ -750,16 +758,18 @@ NotificationController::WillRefresh(mozi
 
         outerDocAcc->RemoveChild(childDoc);
       }
 
       // Failed to bind the child document, destroy it.
       childDoc->Shutdown();
     }
   }
+
+  // Clear the hanging documents list, even if we didn't bind them.
   mHangingChildDocuments.Clear();
   MOZ_ASSERT(mDocument, "Illicit document shutdown");
   if (!mDocument) {
     return;
   }
 
   // If the document is ready and all its subdocuments are completely loaded
   // then process the document load.
@@ -790,20 +800,16 @@ NotificationController::WillRefresh(mozi
     if (!mDocument)
       return;
   }
 
   // Process invalidation list of the document after all accessible tree
   // modification are done.
   mDocument->ProcessInvalidationList();
 
-  // We cannot rely on DOM tree to keep aria-owns relations updated. Make
-  // a validation to remove dead links.
-  mDocument->ValidateARIAOwned();
-
   // Process relocation list.
   for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
     if (mRelocations[idx]->IsInDocument()) {
       mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
     }
   }
   mRelocations.Clear();
 
@@ -862,17 +868,17 @@ NotificationController::WillRefresh(mozi
       uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
       MOZ_DIAGNOSTIC_ASSERT(id);
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
       }
 
-      ipcDoc = new DocAccessibleChild(childDoc);
+      ipcDoc = new DocAccessibleChild(childDoc, parentIPCDoc->Manager());
       childDoc->SetIPCDoc(ipcDoc);
 
 #if defined(XP_WIN)
       parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
                                                      AccessibleWrap::GetChildIDFor(childDoc));
 #else
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
--- a/accessible/base/Platform.h
+++ b/accessible/base/Platform.h
@@ -39,16 +39,20 @@ void PreInit();
 #if defined(MOZ_ACCESSIBILITY_ATK) || defined(XP_MACOSX)
 /**
  * Is platform accessibility enabled.
  * Only used on linux with atk and MacOS for now.
  */
 bool ShouldA11yBeEnabled();
 #endif
 
+#if defined(XP_WIN)
+bool IsHandlerRegistered();
+#endif
+
 /**
  * Called to initialize platform specific accessibility support.
  * Note this is called after internal accessibility support is initialized.
  */
 void PlatformInit();
 
 /**
  * Shutdown platform accessibility.
@@ -69,17 +73,25 @@ void ProxyCreated(ProxyAccessible* aProx
 void ProxyDestroyed(ProxyAccessible*);
 
 /**
  * Callied when an event is fired on a proxied accessible.
  */
 void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType);
 void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState,
                            bool aEnabled);
+
+#if defined(XP_WIN)
+void ProxyFocusEvent(ProxyAccessible* aTarget,
+                     const LayoutDeviceIntRect& aCaretRect);
+void ProxyCaretMoveEvent(ProxyAccessible* aTarget,
+                         const LayoutDeviceIntRect& aCaretRect);
+#else
 void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset);
+#endif
 void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
                           int32_t aStart, uint32_t aLen, bool aIsInsert,
                           bool aFromUser);
 void ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible* aParent,
                         bool aInsert, bool aFromUser);
 void ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible* aWidget,
                          uint32_t aType);
 } // namespace a11y
--- a/accessible/base/RelationTypeMap.h
+++ b/accessible/base/RelationTypeMap.h
@@ -118,37 +118,43 @@ RELATIONTYPE(CONTAINING_DOCUMENT,
              IA2_RELATION_CONTAINING_DOCUMENT)
 
 RELATIONTYPE(CONTAINING_TAB_PANE,
              "containing tab pane",
              ATK_RELATION_NULL,
              NAVRELATION_CONTAINING_TAB_PANE,
              IA2_RELATION_CONTAINING_TAB_PANE)
 
+RELATIONTYPE(CONTAINING_WINDOW,
+             "containing window",
+             ATK_RELATION_NULL,
+             NAVRELATION_CONTAINING_WINDOW,
+             IA2_RELATION_CONTAINING_WINDOW)
+
 RELATIONTYPE(CONTAINING_APPLICATION,
              "containing application",
              ATK_RELATION_NULL,
              NAVRELATION_CONTAINING_APPLICATION,
              IA2_RELATION_CONTAINING_APPLICATION)
 
 RELATIONTYPE(DETAILS,
              "details",
-             ATK_RELATION_NULL,
+             ATK_RELATION_DETAILS,
              NAVRELATION_DETAILS,
              IA2_RELATION_DETAILS)
 
 RELATIONTYPE(DETAILS_FOR,
              "details for",
-             ATK_RELATION_NULL,
+             ATK_RELATION_DETAILS_FOR,
              NAVRELATION_DETAILS_FOR,
              IA2_RELATION_DETAILS_FOR)
 
 RELATIONTYPE(ERRORMSG,
              "error",
-             ATK_RELATION_NULL,
+             ATK_RELATION_ERROR_MESSAGE,
              NAVRELATION_ERROR,
              IA2_RELATION_ERROR)
 
 RELATIONTYPE(ERRORMSG_FOR,
              "error for",
-             ATK_RELATION_NULL,
+             ATK_RELATION_ERROR_FOR,
              NAVRELATION_ERROR_FOR,
              IA2_RELATION_ERROR_FOR)
--- a/accessible/base/Role.h
+++ b/accessible/base/Role.h
@@ -125,17 +125,17 @@ enum Role {
   PANE = 16,
 
   /**
    * Represents a graphical image used to represent data.
    */
   CHART = 17,
 
   /**
-   * Represents a dialog box or message box. It is used for xul:dialog, 
+   * Represents a dialog box or message box. It is used for xul:dialog,
    * role="dialog".
    */
   DIALOG = 18,
 
   /**
    * Represents a window border.
    */
   BORDER = 19,
@@ -290,30 +290,30 @@ enum Role {
    */
   PUSHBUTTON = 43,
 
   /**
    * Represents a check box control. It is used for xul:checkbox,
    * html:input@type="checkbox", role="checkbox".
    */
   CHECKBUTTON = 44,
-  
+
   /**
    * Represents an option button, also called a radio button. It is one of a
    * group of mutually exclusive options. All objects sharing a single parent
    * that have this attribute are assumed to be part of single mutually
    * exclusive group. It is used for xul:radio, html:input@type="radio",
    * role="radio".
    */
   RADIOBUTTON = 45,
-  
+
   /**
-   * Represents a combo box; an edit control with an associated list box that
-   * provides a set of predefined choices. It is used for html:select,
-   * xul:menulist, role="combobox".
+   * Represents a combo box; a popup button with an associated list box that
+   * provides a set of predefined choices. It is used for html:select with a
+   * size of 1 and xul:menulist. See also ROLE_EDITCOMBOBOX.
    */
   COMBOBOX = 46,
 
   /**
    * Represents the calendar control.
    */
   DROPLIST = 47,
 
@@ -348,17 +348,17 @@ enum Role {
    * with the spin box. It is used for xul:spinbuttons.
    */
   SPINBUTTON = 52,
 
   /**
    * Represents a graphical image used to diagram data. It is used for svg:svg.
    */
   DIAGRAM = 53,
-  
+
   /**
    * Represents an animation control, which contains content that changes over
    * time, such as a control that displays a series of bitmap frames.
    */
   ANIMATION = 54,
 
   /**
    * Represents a mathematical equation. It is used by MATHML, where there is a
@@ -977,17 +977,56 @@ enum Role {
    */
   DETAILS = 167,
 
   /**
    * The html:summary element.
    */
   SUMMARY = 168,
 
-  LAST_ROLE = SUMMARY
+  /**
+   * An ARIA landmark. See related NAVIGATION role.
+   */
+  LANDMARK = 169,
+
+  /**
+   * A specific type of ARIA landmark. The ability to distinguish navigation
+   * landmarks from other types of landmarks is, for example, needed on macOS
+   * where specific AXSubrole and AXRoleDescription for navigation landmarks
+   * are used.
+   */
+  NAVIGATION = 170,
+
+  /**
+   * An object that contains the text of a footnote.
+   */
+  FOOTNOTE = 171,
+
+  /**
+   * A complete or self-contained composition in a document, page, application,
+   * or site and that is, in principle, independently distributable or reusable,
+   * e.g. in syndication.
+   */
+  ARTICLE = 172,
+
+  /**
+   * A perceivable section containing content that is relevant to a specific,
+   * author-specified purpose and sufficiently important that users will likely
+   * want to be able to navigate to the section easily and to have it listed in
+   * a summary of the page.
+   */
+  REGION = 173,
+
+  /**
+   * Represents a control with a text input and a popup with a set of predefined
+   * choices. It is used for ARIA's combobox role. See also COMBOBOX.
+   */
+  EDITCOMBOBOX = 174,
+
+  LAST_ROLE = EDITCOMBOBOX
 };
 
 } // namespace role
 
 typedef enum mozilla::a11y::roles::Role role;
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -301,18 +301,18 @@ ROLE(OUTLINE,
      ATK_ROLE_TREE,
      NSAccessibilityOutlineRole,
      ROLE_SYSTEM_OUTLINE,
      ROLE_SYSTEM_OUTLINE,
      eNoNameRule)
 
 ROLE(OUTLINEITEM,
      "outlineitem",
-     ATK_ROLE_LIST_ITEM,
-     NSAccessibilityRowRole,  //XXX: use OutlineRow as subrole.
+     ATK_ROLE_TREE_ITEM,
+     NSAccessibilityRowRole,
      ROLE_SYSTEM_OUTLINEITEM,
      ROLE_SYSTEM_OUTLINEITEM,
      eNameFromSubtreeRule)
 
 ROLE(PAGETAB,
      "pagetab",
      ATK_ROLE_PAGE_TAB,
      NSAccessibilityRadioButtonRole,
@@ -379,16 +379,17 @@ ROLE(CHECKBUTTON,
 ROLE(RADIOBUTTON,
      "radiobutton",
      ATK_ROLE_RADIO_BUTTON,
      NSAccessibilityRadioButtonRole,
      ROLE_SYSTEM_RADIOBUTTON,
      ROLE_SYSTEM_RADIOBUTTON,
      eNameFromSubtreeRule)
 
+// Equivalent of HTML select element with size="1". See also EDITCOMBOBOX.
 ROLE(COMBOBOX,
      "combobox",
      ATK_ROLE_COMBO_BOX,
      NSAccessibilityPopUpButtonRole,
      ROLE_SYSTEM_COMBOBOX,
      ROLE_SYSTEM_COMBOBOX,
      eNameFromValueRule)
 
@@ -997,17 +998,17 @@ ROLE(EMBEDDED_OBJECT,
      ATK_ROLE_PANEL,
      NSAccessibilityGroupRole,
      USE_ROLE_STRING,
      IA2_ROLE_EMBEDDED_OBJECT,
      eNoNameRule)
 
 ROLE(NOTE,
      "note",
-     ATK_ROLE_SECTION,
+     ATK_ROLE_COMMENT,
      NSAccessibilityGroupRole,
      USE_ROLE_STRING,
      IA2_ROLE_NOTE,
      eNameFromSubtreeIfReqRule)
 
 ROLE(FIGURE,
      "figure",
      ATK_ROLE_PANEL,
@@ -1029,17 +1030,17 @@ ROLE(DEFINITION_LIST,
      ATK_ROLE_LIST,
      NSAccessibilityListRole,
      ROLE_SYSTEM_LIST,
      ROLE_SYSTEM_LIST,
      eNameFromSubtreeIfReqRule)
 
 ROLE(TERM,
      "term",
-     ATK_ROLE_LIST_ITEM,
+     ATK_ROLE_DESCRIPTION_TERM,
      NSAccessibilityGroupRole,
      ROLE_SYSTEM_LISTITEM,
      ROLE_SYSTEM_LISTITEM,
      eNameFromSubtreeRule)
 
 ROLE(DEFINITION,
      "definition",
      ATK_ROLE_PARAGRAPH,
@@ -1363,8 +1364,57 @@ ROLE(DETAILS,
 ROLE(SUMMARY,
      "summary",
      ATK_ROLE_PUSH_BUTTON,
      NSAccessibilityGroupRole,
      ROLE_SYSTEM_PUSHBUTTON,
      ROLE_SYSTEM_PUSHBUTTON,
      eNameFromSubtreeRule)
 
+ROLE(LANDMARK,
+     "landmark",
+     ATK_ROLE_LANDMARK,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_LANDMARK,
+     eNoNameRule)
+
+ROLE(NAVIGATION,
+     "navigation",
+     ATK_ROLE_LANDMARK,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_LANDMARK,
+     eNoNameRule)
+
+ROLE(FOOTNOTE,
+     "footnote",
+     ATK_ROLE_FOOTNOTE,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_FOOTNOTE,
+     eNoNameRule)
+
+ROLE(ARTICLE,
+     "article",
+     ATK_ROLE_ARTICLE,
+     NSAccessibilityGroupRole,
+     ROLE_SYSTEM_DOCUMENT,
+     ROLE_SYSTEM_DOCUMENT,
+     eNoNameRule)
+
+ROLE(REGION,
+     "region",
+     ATK_ROLE_LANDMARK,
+     NSAccessibilityGroupRole,
+     USE_ROLE_STRING,
+     IA2_ROLE_LANDMARK,
+     eNoNameRule)
+
+// A composite widget with a text input and popup. Used for ARIA role combobox.
+// See also COMBOBOX.
+ROLE(EDITCOMBOBOX,
+     "editcombobox",
+     ATK_ROLE_COMBO_BOX,
+     NSAccessibilityComboBoxRole,
+     ROLE_SYSTEM_COMBOBOX,
+     ROLE_SYSTEM_COMBOBOX,
+     eNameFromValueRule)
--- a/accessible/base/SelectionManager.cpp
+++ b/accessible/base/SelectionManager.cpp
@@ -43,60 +43,59 @@ SelectionManager::SelectionManager() :
   mCaretOffset(-1), mAccWithCaret(nullptr)
 {
 
 }
 
 void
 SelectionManager::ClearControlSelectionListener()
 {
-  if (!mCurrCtrlFrame)
-    return;
-
-  const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
-  NS_ASSERTION(frameSel, "No frame selection for the element!");
-
-  mCurrCtrlFrame = nullptr;
-  if (!frameSel)
-    return;
 
   // Remove 'this' registered as selection listener for the normal selection.
-  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
-  normalSel->RemoveSelectionListener(this);
+  nsCOMPtr<nsISelection> normalSel = do_QueryReferent(mCurrCtrlNormalSel);
+  if (normalSel) {
+    normalSel->AsSelection()->RemoveSelectionListener(this);
+    mCurrCtrlNormalSel = nullptr;
+  }
 
   // Remove 'this' registered as selection listener for the spellcheck
   // selection.
-  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
-  spellSel->RemoveSelectionListener(this);
+  nsCOMPtr<nsISelection> spellSel = do_QueryReferent(mCurrCtrlSpellSel);
+  if (spellSel) {
+    spellSel->AsSelection()->RemoveSelectionListener(this);
+    mCurrCtrlSpellSel = nullptr;
+  }
 }
 
 void
 SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
 {
   // When focus moves such that the caret is part of a new frame selection
   // this removes the old selection listener and attaches a new one for
   // the current focus.
   ClearControlSelectionListener();
 
-  mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
-  if (!mCurrCtrlFrame)
+  nsIFrame* controlFrame = aFocusedElm->GetPrimaryFrame();
+  if (!controlFrame)
     return;
 
-  const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
+  const nsFrameSelection* frameSel = controlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for focused element!");
   if (!frameSel)
     return;
 
   // Register 'this' as selection listener for the normal selection.
-  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
-  normalSel->AddSelectionListener(this);
+  nsCOMPtr<nsISelection> normalSel = frameSel->GetSelection(SelectionType::eNormal);
+  normalSel->AsSelection()->AddSelectionListener(this);
+  mCurrCtrlNormalSel = do_GetWeakReference(normalSel);
 
   // Register 'this' as selection listener for the spell check selection.
-  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
-  spellSel->AddSelectionListener(this);
+  nsCOMPtr<nsISelection> spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
+  spellSel->AsSelection()->AddSelectionListener(this);
+  mCurrCtrlSpellSel = do_GetWeakReference(spellSel);
 }
 
 void
 SelectionManager::AddDocSelectionListener(nsIPresShell* aPresShell)
 {
   const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
 
   // Register 'this' as selection listener for the normal selection.
--- a/accessible/base/SelectionManager.h
+++ b/accessible/base/SelectionManager.h
@@ -116,17 +116,18 @@ protected:
 
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(SelData* aSelData);
 
 private:
   // Currently focused control.
-  WeakFrame mCurrCtrlFrame;
   int32_t mCaretOffset;
   HyperTextAccessible* mAccWithCaret;
+  nsWeakPtr mCurrCtrlNormalSel;
+  nsWeakPtr mCurrCtrlSpellSel;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/States.h
+++ b/accessible/base/States.h
@@ -68,17 +68,17 @@ namespace states {
    * The expandable object's children are displayed, the opposite of collapsed,
    * applied to trees, list and other controls.
    * @see COLLAPSED state
    */
   const uint64_t EXPANDED = ((uint64_t) 0x1) << 9;
 
   /**
    * The expandable object's children are not displayed, the opposite of
-   * expanded, applied to tree lists and other controls, 
+   * expanded, applied to tree lists and other controls,
    * @see EXPANDED state.
    */
   const uint64_t COLLAPSED = ((uint64_t) 0x1) << 10;
 
   /**
    * The control or document can not accept input at this time.
    */
   const uint64_t BUSY = ((uint64_t) 0x1) << 11;
@@ -272,14 +272,20 @@ namespace states {
    * @see EXPANDED and COLLAPSED states.
    */
   const uint64_t EXPANDABLE = ((uint64_t) 0x1) << 46;
 
   /**
    * The object is pinned, usually indicating it is fixed in place and has permanence.
    */
   const uint64_t PINNED = ((uint64_t) 0x1) << 47;
+
+  /**
+   * The object is the current item within a container or set of related elements.
+   */
+  const uint64_t CURRENT = ((uint64_t) 0x1) << 48;
+
 } // namespace states
 } // namespace a11y
 } // namespace mozilla
 
 #endif
-	
+
--- a/accessible/base/StyleInfo.cpp
+++ b/accessible/base/StyleInfo.cpp
@@ -13,19 +13,17 @@
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 StyleInfo::StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell) :
   mElement(aElement)
 {
   mStyleContext =
-    nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
-                                                         nullptr,
-                                                         aPresShell);
+    nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, aPresShell);
 }
 
 void
 StyleInfo::Display(nsAString& aValue)
 {
   aValue.Truncate();
   AppendASCIItoUTF16(
     nsCSSProps::ValueToKeyword(mStyleContext->StyleDisplay()->mDisplay,
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -47,16 +47,28 @@ TreeWalker::
   MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
 
   mChildFilter |= mContext->NoXBLKids() ?
     nsIContent::eAllButXBL : nsIContent::eAllChildren;
 
   MOZ_COUNT_CTOR(TreeWalker);
 }
 
+TreeWalker::
+  TreeWalker(DocAccessible* aDocument, nsIContent* aAnchorNode) :
+  mDoc(aDocument), mContext(nullptr), mAnchorNode(aAnchorNode),
+  mARIAOwnsIdx(0),
+  mChildFilter(nsIContent::eSkipPlaceholderContent | nsIContent::eAllChildren),
+  mFlags(eWalkCache),
+  mPhase(eAtStart)
+{
+  MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
+  MOZ_COUNT_CTOR(TreeWalker);
+}
+
 TreeWalker::~TreeWalker()
 {
   MOZ_COUNT_DTOR(TreeWalker);
 }
 
 Accessible*
 TreeWalker::Scope(nsIContent* aAnchorNode)
 {
--- a/accessible/base/TreeWalker.h
+++ b/accessible/base/TreeWalker.h
@@ -42,16 +42,21 @@ public:
    *
    * @param aContext [in] container accessible for the given node, used to
    *                   define accessible context
    * @param aAnchorNode [in] the node the search will be prepared relative to
    * @param aFlags   [in] flags (see enum above)
    */
   TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags = eWalkCache);
 
+  /**
+   * Navigates the accessible children within the anchor node subtree.
+   */
+  TreeWalker(DocAccessible* aDocument, nsIContent* aAnchorNode);
+
   ~TreeWalker();
 
   /**
    * Resets the walker state, and sets the given node as an anchor. Returns a
    * first accessible element within the node including the node itself.
    */
   Accessible* Scope(nsIContent* aAnchorNode);
 
--- a/accessible/base/nsAccUtils.cpp
+++ b/accessible/base/nsAccUtils.cpp
@@ -79,17 +79,17 @@ nsAccUtils::GetDefaultLevel(Accessible* 
 
   if (role == roles::OUTLINEITEM)
     return 1;
 
   if (role == roles::ROW) {
     Accessible* parent = aAccessible->Parent();
     // It is a row inside flatten treegrid. Group level is always 1 until it
     // is overriden by aria-level attribute.
-    if (parent && parent->Role() == roles::TREE_TABLE) 
+    if (parent && parent->Role() == roles::TREE_TABLE)
       return 1;
   }
 
   return 0;
 }
 
 int32_t
 nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible)
@@ -410,17 +410,17 @@ nsAccUtils::TextLength(Accessible* aAcce
   }
 
   TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
   if (textLeaf)
     return textLeaf->Text().Length();
 
   // For list bullets (or anything other accessible which would compute its own
   // text. They don't have their own frame.
-  // XXX In the future, list bullets may have frame and anon content, so 
+  // XXX In the future, list bullets may have frame and anon content, so
   // we should be able to remove this at that point
   nsAutoString text;
   aAccessible->AppendTextTo(text); // Get all the text
   return text.Length();
 }
 
 bool
 nsAccUtils::MustPrune(Accessible* aAccessible)
--- a/accessible/base/nsAccUtils.h
+++ b/accessible/base/nsAccUtils.h
@@ -85,19 +85,19 @@ public:
    * @param aTopContent    node to end at
    */
   static void SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
                                          nsIContent* aStartContent,
                                          mozilla::dom::Element* aTopEl);
 
   /**
    * Any ARIA property of type boolean or NMTOKEN is undefined if the ARIA
-   * property is not present, or is "" or "undefined". Do not call 
+   * property is not present, or is "" or "undefined". Do not call
    * this method for properties of type string, decimal, IDREF or IDREFS.
-   * 
+   *
    * Return true if the ARIA property is defined, otherwise false
    */
   static bool HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom);
 
   /**
    * Return atomic value of ARIA attribute of boolean or NMTOKEN type.
    */
   static nsIAtom* GetARIAToken(mozilla::dom::Element* aElement, nsIAtom* aAttr);
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -53,16 +53,17 @@
 #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"
 #include "nsTreeUtils.h"
 #include "nsXBLPrototypeBinding.h"
@@ -190,16 +191,29 @@ New_HTMLDefinition(nsIContent* aContent,
   if (aContext->IsList())
     return new HyperTextAccessibleWrap(aContent, aContext->Document());
   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)) {
+    return new HTMLCheckboxAccessible(aContent, aContext->Document());
+  }
+  if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                            nsGkAtoms::radio, eIgnoreCase)) {
+    return new HTMLRadioButtonAccessible(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()); }
 
 static Accessible* New_HTMLSummary(nsIContent* aContent, Accessible* aContext)
   { return new HTMLSummaryAccessible(aContent, aContext->Document()); }
@@ -394,16 +408,17 @@ nsAccessibilityService::GetRootDocumentA
   return nullptr;
 }
 
 #ifdef XP_WIN
 static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
 static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
 
 class PluginTimerCallBack final : public nsITimerCallback
+                                , public nsINamed
 {
   ~PluginTimerCallBack() {}
 
 public:
   explicit PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
 
   NS_DECL_ISUPPORTS
 
@@ -426,21 +441,27 @@ 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
+  {
+    aName.AssignLiteral("PluginTimerCallBack");
+    return NS_OK;
+  }
+
 private:
   nsCOMPtr<nsIContent> mContent;
 };
 
-NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback)
+NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback, nsINamed)
 #endif
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreatePluginAccessible(nsPluginFrame* aFrame,
                                                nsIContent* aContent,
                                                Accessible* aContext)
 {
   // nsPluginFrame means a plugin, so we need to use the accessibility support
@@ -519,17 +540,17 @@ nsAccessibilityService::DeckPanelSwitche
     if (logging::IsEnabled(logging::eTree)) {
       logging::MsgBegin("TREE", "deck panel unselected");
       logging::Node("container", panelNode);
       logging::Node("content", aDeckNode);
       logging::MsgEnd();
     }
 #endif
 
-    document->ContentRemoved(aDeckNode, panelNode);
+    document->ContentRemoved(panelNode);
   }
 
   if (aCurrentBoxFrame) {
     nsIContent* panelNode = aCurrentBoxFrame->GetContent();
 #ifdef A11Y_LOG
     if (logging::IsEnabled(logging::eTree)) {
       logging::MsgBegin("TREE", "deck panel selected");
       logging::Node("container", panelNode);
@@ -577,36 +598,17 @@ nsAccessibilityService::ContentRemoved(n
     logging::MsgBegin("TREE", "content removed; doc: %p", document);
     logging::Node("container node", aChildNode->GetFlattenedTreeParent());
     logging::Node("content node", aChildNode);
     logging::MsgEnd();
   }
 #endif
 
   if (document) {
-    // Flatten hierarchy may be broken at this point so we cannot get a true
-    // container by traversing up the DOM tree. Find a parent of first accessible
-    // from the subtree of the given DOM node, that'll be a container. If no
-    // accessibles in subtree then we don't care about the change.
-    Accessible* child = document->GetAccessible(aChildNode);
-    if (!child) {
-      Accessible* container = document->GetContainerAccessible(aChildNode);
-      a11y::TreeWalker walker(container ? container : document, aChildNode,
-                              a11y::TreeWalker::eWalkCache);
-      child = walker.Next();
-    }
-
-    if (child) {
-      MOZ_DIAGNOSTIC_ASSERT(child->Parent(), "Unattached accessible from tree");
-      document->ContentRemoved(child->Parent(), aChildNode);
-#ifdef A11Y_LOG
-      if (logging::IsEnabled(logging::eTree))
-        logging::AccessibleNNode("real container", child->Parent());
-#endif
-    }
+    document->ContentRemoved(aChildNode);
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eTree)) {
     logging::MsgEnd();
     logging::Stack();
   }
 #endif
@@ -746,17 +748,17 @@ nsAccessibilityService::GetStringRole(ui
 
 #undef ROLE
 }
 
 void
 nsAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState,
                                         nsISupports** aStringStates)
 {
-  RefPtr<DOMStringList> stringStates = 
+  RefPtr<DOMStringList> stringStates =
     GetStringStates(nsAccUtils::To64State(aState, aExtraState));
 
   // unknown state
   if (!stringStates->Length()) {
     stringStates->Add(NS_LITERAL_STRING("unknown"));
   }
 
   stringStates.forget(aStringStates);
@@ -1156,19 +1158,19 @@ nsAccessibilityService::CreateAccessible
     // XBL bindings may use @role attribute to point the accessible type
     // they belong to.
     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()) {
-      nsIAtom* frameType = frame->GetType();
-      if (frameType == nsGkAtoms::boxFrame ||
-          frameType == nsGkAtoms::scrollFrame) {
+      LayoutFrameType frameType = frame->Type();
+      if (frameType == LayoutFrameType::Box ||
+          frameType == LayoutFrameType::Scroll) {
         newAcc = new XULTabpanelAccessible(content, document);
       }
     }
   }
 
   if (!newAcc) {
     if (content->IsSVGElement()) {
       SVGGeometryFrame* geometryFrame = do_QueryFrame(frame);
@@ -1629,22 +1631,22 @@ nsAccessibilityService::CreateAccessible
       // accessible HTML table or a direct child of accessible of HTML table.
       Accessible* table = aContext->IsTable() ? aContext : nullptr;
       if (!table && aContext->Parent() && aContext->Parent()->IsTable())
         table = aContext->Parent();
 
       if (table) {
         nsIContent* parentContent = aContent->GetParent();
         nsIFrame* parentFrame = parentContent->GetPrimaryFrame();
-        if (parentFrame->GetType() != nsGkAtoms::tableWrapperFrame) {
+        if (!parentFrame->IsTableWrapperFrame()) {
           parentContent = parentContent->GetParent();
           parentFrame = parentContent->GetPrimaryFrame();
         }
 
-        if (parentFrame->GetType() == nsGkAtoms::tableWrapperFrame &&
+        if (parentFrame->IsTableWrapperFrame() &&
             table->GetContent() == parentContent) {
           newAcc = new HTMLTableRowAccessible(aContent, document);
         }
       }
       break;
     }
     case eHTMLTextFieldType:
       newAcc = new HTMLTextFieldAccessible(aContent, document);
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -101,17 +101,17 @@ public:
    * of the application accessible.
    */
   Accessible* AddNativeRootAccessible(void* aAtkAccessible);
   void RemoveNativeRootAccessible(Accessible* aRootAccessible);
 
   bool HasAccessible(nsIDOMNode* aDOMNode);
 
   /**
-   * Get a string equivalent for an accessilbe role value.
+   * Get a string equivalent for an accessible role value.
    */
   void GetStringRole(uint32_t aRole, nsAString& aString);
 
   /**
    * Get a string equivalent for an accessible state/extra state.
    */
   already_AddRefed<mozilla::dom::DOMStringList>
     GetStringStates(uint64_t aStates) const;
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -439,17 +439,17 @@ nsCoreUtils::IsErrorPage(nsIDocument *aD
 {
   nsIURI *uri = aDocument->GetDocumentURI();
   bool isAboutScheme = false;
   uri->SchemeIs("about", &isAboutScheme);
   if (!isAboutScheme)
     return false;
 
   nsAutoCString path;
-  uri->GetPath(path);
+  uri->GetPathQueryRef(path);
 
   NS_NAMED_LITERAL_CSTRING(neterror, "neterror");
   NS_NAMED_LITERAL_CSTRING(certerror, "certerror");
 
   return StringBeginsWith(path, neterror) || StringBeginsWith(path, certerror);
 }
 
 bool
@@ -628,19 +628,19 @@ nsCoreUtils::ScrollTo(nsIPresShell* aPre
 {
   nsIPresShell::ScrollAxis vertical, horizontal;
   ConvertScrollTypeToPercents(aScrollType, &vertical, &horizontal);
   aPresShell->ScrollContentIntoView(aContent, vertical, horizontal,
                                     nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
 }
 
 bool
-nsCoreUtils::IsWhitespaceString(const nsSubstring& aString)
+nsCoreUtils::IsWhitespaceString(const nsAString& aString)
 {
-  nsSubstring::const_char_iterator iterBegin, iterEnd;
+  nsAString::const_char_iterator iterBegin, iterEnd;
 
   aString.BeginReading(iterBegin);
   aString.EndReading(iterEnd);
 
   while (iterBegin != iterEnd && IsWhitespace(*iterBegin))
     ++iterBegin;
 
   return iterBegin == iterEnd;
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -294,17 +294,17 @@ public:
       aContent->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 nsSubstring& aString);
+  static bool IsWhitespaceString(const nsAString& aString);
 
   /**
    * Returns true if the given character is whitespace symbol.
    */
   static bool IsWhitespace(char16_t aChar)
   {
     return aChar == ' ' || aChar == '\n' ||
       aChar == '\r' || aChar == '\t' || aChar == 0xa0;
--- a/accessible/base/nsEventShell.cpp
+++ b/accessible/base/nsEventShell.cpp
@@ -55,17 +55,17 @@ nsEventShell::FireEvent(uint32_t aEventT
   NS_ENSURE_TRUE_VOID(aAccessible);
 
   RefPtr<AccEvent> event = new AccEvent(aEventType, aAccessible,
                                           aIsFromUserInput);
 
   FireEvent(event);
 }
 
-void 
+void
 nsEventShell::GetEventAttributes(nsINode *aNode,
                                  nsIPersistentProperties *aAttributes)
 {
   if (aNode != sEventTargetNode)
     return;
 
   nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::eventFromInput,
                          sEventFromUserInput ? NS_LITERAL_STRING("true") :
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -130,42 +130,42 @@ nsTextEquivUtils::AppendTextEquivFromTex
             display->mDisplay == StyleDisplay::TableCell) {
           isHTMLBlock = true;
           if (!aString->IsEmpty()) {
             aString->Append(char16_t(' '));
           }
         }
       }
     }
-    
+
     if (aContent->TextLength() > 0) {
       nsIFrame *frame = aContent->GetPrimaryFrame();
       if (frame) {
         nsIFrame::RenderedText text = frame->GetRenderedText(0,
             UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
             nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
         aString->Append(text.mString);
       } else {
         // If aContent is an object that is display: none, we have no a frame.
         aContent->AppendTextTo(*aString);
       }
       if (isHTMLBlock && !aString->IsEmpty()) {
         aString->Append(char16_t(' '));
       }
     }
-    
+
     return NS_OK;
   }
-  
+
   if (aContent->IsHTMLElement() &&
       aContent->NodeInfo()->Equals(nsGkAtoms::br)) {
     aString->AppendLiteral("\r\n");
     return NS_OK;
   }
-  
+
   return NS_OK_NO_NAME_CLAUSE_HANDLED;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsTextEquivUtils. Private.
 
 nsresult
 nsTextEquivUtils::AppendFromAccessibleChildren(Accessible* aAccessible,
--- a/accessible/base/nsTextEquivUtils.h
+++ b/accessible/base/nsTextEquivUtils.h
@@ -116,17 +116,17 @@ public:
 
 private:
   /**
    * Iterates accessible children and calculates text equivalent from each
    * child.
    */
   static nsresult AppendFromAccessibleChildren(Accessible* aAccessible,
                                                nsAString *aString);
-  
+
   /**
    * Calculates text equivalent from the given accessible and its subtree if
    * allowed.
    */
   static nsresult AppendFromAccessible(Accessible* aAccessible,
                                        nsAString *aString);
 
   /**
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -90,17 +90,23 @@ Accessible::HasGenericType(AccGenericTyp
 
 inline bool
 Accessible::HasNumericValue() const
 {
   if (mStateFlags & eHasNumericValue)
     return true;
 
   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
-  return roleMapEntry && roleMapEntry->valueRule != eNoValue;
+  if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
+    return false;
+
+  if (roleMapEntry->valueRule == eHasValueMinMaxIfFocusable)
+    return InteractiveState() & states::FOCUSABLE;
+
+  return true;
 }
 
 inline void
 Accessible::ScrollTo(uint32_t aHow) const
 {
   if (mContent)
     nsCoreUtils::ScrollTo(mDoc->PresShell(), mContent, aHow);
 }
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -313,33 +313,39 @@ Accessible::TranslateString(const nsStri
   nsCOMPtr<nsIStringBundle> stringBundle;
   stringBundleService->CreateBundle(
     "chrome://global-platform/locale/accessible.properties",
     getter_AddRefs(stringBundle));
   if (!stringBundle)
     return;
 
   nsXPIDLString xsValue;
-  nsresult rv = stringBundle->GetStringFromName(aKey.get(), getter_Copies(xsValue));
+  nsresult rv =
+    stringBundle->GetStringFromName(NS_ConvertUTF16toUTF8(aKey).get(),
+                                    getter_Copies(xsValue));
   if (NS_SUCCEEDED(rv))
     aStringOut.Assign(xsValue);
 }
 
 uint64_t
 Accessible::VisibilityState()
 {
   nsIFrame* frame = GetFrame();
   if (!frame)
     return states::INVISIBLE;
 
   // Walk the parent frame chain to see if there's invisible parent or the frame
   // is in background tab.
   if (!frame->StyleVisibility()->IsVisible())
     return states::INVISIBLE;
 
+  // Offscreen state if the document's visibility state is not visible.
+  if (Document()->IsHidden())
+    return states::OFFSCREEN;
+
   nsIFrame* curFrame = frame;
   do {
     nsView* view = curFrame->GetView();
     if (view && view->GetVisibility() == nsViewVisibility_kHide)
       return states::INVISIBLE;
 
     if (nsLayoutUtils::IsPopup(curFrame))
       return 0;
@@ -380,17 +386,17 @@ Accessible::VisibilityState()
     curFrame = parentFrame;
   } while (curFrame);
 
   // Zero area rects can occur in the first frame of a multi-frame text flow,
   // in which case the rendered text is not empty and the frame should not be
   // marked invisible.
   // XXX Can we just remove this check? Why do we need to mark empty
   // text invisible?
-  if (frame->GetType() == nsGkAtoms::textFrame &&
+  if (frame->IsTextFrame() &&
       !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
       frame->GetRect().IsEmpty()) {
     nsIFrame::RenderedText text = frame->GetRenderedText(0,
         UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
         nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
     if (text.mString.IsEmpty()) {
       return states::INVISIBLE;
     }
@@ -631,17 +637,18 @@ Accessible::RelativeBounds(nsIFrame** aB
   if (frame && mContent) {
     bool* hasHitRegionRect = static_cast<bool*>(mContent->GetProperty(nsGkAtoms::hitregion));
 
     if (hasHitRegionRect && mContent->IsElement()) {
       // This is for canvas fallback content
       // Find a canvas frame the found hit region is relative to.
       nsIFrame* canvasFrame = frame->GetParent();
       if (canvasFrame) {
-        canvasFrame = nsLayoutUtils::GetClosestFrameOfType(canvasFrame, nsGkAtoms::HTMLCanvasFrame);
+        canvasFrame = nsLayoutUtils::GetClosestFrameOfType(
+          canvasFrame, LayoutFrameType::HTMLCanvas);
       }
 
       // make the canvas the bounding frame
       if (canvasFrame) {
         *aBoundingFrame = canvasFrame;
         dom::HTMLCanvasElement *canvas =
           dom::HTMLCanvasElement::FromContent(canvasFrame->GetContent());
 
@@ -885,16 +892,22 @@ Accessible::HandleAccEvent(AccEvent* aEv
         case nsIAccessibleEvent::EVENT_SELECTION_ADD:
         case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
           AccSelChangeEvent* selEvent = downcast_accEvent(aEvent);
           uint64_t widgetID = selEvent->Widget()->IsDoc() ? 0 :
             reinterpret_cast<uintptr_t>(selEvent->Widget());
           ipcDoc->SendSelectionEvent(id, widgetID, aEvent->GetEventType());
           break;
         }
+#if defined(XP_WIN)
+        case nsIAccessibleEvent::EVENT_FOCUS: {
+          ipcDoc->SendFocusEvent(id);
+          break;
+        }
+#endif
         default:
           ipcDoc->SendEvent(id, aEvent->GetEventType());
       }
     }
   }
 
   if (nsCoreUtils::AccEventObserversExist()) {
     nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
@@ -929,16 +942,20 @@ Accessible::Attributes()
   while(attribIter.Next(name, value))
     attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
 
   if (IsARIAHidden()) {
     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
                            NS_LITERAL_STRING("true"));
   }
 
+  // XXX: In ARIA 1.1, the value of aria-haspopup became a token (bug 1355449).
+  if (aria::UniversalStatesFor(mContent->AsElement()) & states::HASPOPUP)
+    nsAccUtils::SetAccAttr(attributes, nsGkAtoms::haspopup, NS_LITERAL_STRING("true"));
+
   // If there is no aria-live attribute then expose default value of 'live'
   // object attribute used for ARIA role of this accessible.
   const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
   if (roleMapEntry) {
     if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
       nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
                              NS_LITERAL_STRING("search"));
     }
@@ -1394,16 +1411,32 @@ Accessible::SetCurValue(double aValue)
 
   return NS_SUCCEEDED(
     mContent->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
+  //
+  // XXX: While the name computation algorithm can be non-trivial in the general
+  // case, it should not be especially bad here: If the author hasn't used the
+  // region role, this calculation won't occur. And the region role's name
+  // calculation rule excludes name from content. That said, this use case is
+  // another example of why we should consider caching the accessible name. See:
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1378235.
+  if (aRole == roles::REGION) {
+    nsAutoString name;
+    Name(name);
+    return name.IsEmpty() ? NativeRole() : aRole;
+  }
+
   // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
   // 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;
     }
@@ -1414,24 +1447,24 @@ Accessible::ARIATransformRole(role aRole
                               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->Role() == roles::COMBOBOX) {
+    if (mParent && mParent->IsCombobox()) {
       return roles::COMBOBOX_LIST;
     } else {
       // Listbox is owned by a combobox
       Relation rel = RelationByType(RelationType::NODE_CHILD_OF);
       Accessible* targetAcc = nullptr;
       while ((targetAcc = rel.Next()))
-        if (targetAcc->Role() == roles::COMBOBOX)
+        if (targetAcc->IsCombobox())
           return roles::COMBOBOX_LIST;
     }
 
   } else if (aRole == roles::OPTION) {
     if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
       return roles::COMBOBOX_OPTION;
 
   } else if (aRole == roles::MENUITEM) {
@@ -1783,18 +1816,23 @@ Accessible::GetNativeInterface(void** aN
 }
 
 void
 Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
 {
   class Runnable final : public mozilla::Runnable
   {
   public:
-    Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
-      mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
+    Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx)
+      : mozilla::Runnable("Runnable")
+      , mAcc(aAcc)
+      , mContent(aContent)
+      , mIdx(aIdx)
+    {
+    }
 
     NS_IMETHOD Run() override
     {
       if (mAcc)
         mAcc->DispatchClickEvent(mContent, mIdx);
 
       return NS_OK;
     }
@@ -1883,17 +1921,17 @@ Accessible::AppendTextTo(nsAString& aTex
 
   nsIFrame *frame = GetFrame();
   if (!frame)
     return;
 
   NS_ASSERTION(mParent,
                "Called on accessible unbound from tree. Result can be wrong.");
 
-  if (frame->GetType() == nsGkAtoms::brFrame) {
+  if (frame->IsBrFrame()) {
     aText += kForcedNewLineChar;
   } else if (mParent && nsAccUtils::MustPrune(mParent)) {
     // Expose the embedded object accessible as imaginary embedded object
     // character if its parent hypertext accessible doesn't expose children to
     // AT.
     aText += kImaginaryEmbeddedObjectChar;
   } else {
     aText += kEmbeddedObjectChar;
@@ -2130,17 +2168,18 @@ Accessible::InsertChildAt(uint32_t aInde
 
 bool
 Accessible::RemoveChild(Accessible* aChild)
 {
   MOZ_DIAGNOSTIC_ASSERT(aChild, "No child was given");
   MOZ_DIAGNOSTIC_ASSERT(aChild->mParent, "No parent");
   MOZ_DIAGNOSTIC_ASSERT(aChild->mParent == this, "Wrong parent");
   MOZ_DIAGNOSTIC_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
-  MOZ_DIAGNOSTIC_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() || aChild->IsDoc(),
+  MOZ_DIAGNOSTIC_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() ||
+                        aChild->IsDoc() || IsApplication(),
                         "Illicit children change");
 
   int32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
   if (mChildren.SafeElementAt(index) != aChild) {
     MOZ_ASSERT_UNREACHABLE("A wrong child index");
     index = mChildren.IndexOf(aChild);
     if (index == -1) {
       MOZ_ASSERT_UNREACHABLE("No child was found");
@@ -2791,46 +2830,46 @@ KeyBinding::ToPlatformFormat(nsAString& 
     stringBundleService->CreateBundle(
       "chrome://global-platform/locale/platformKeys.properties",
       getter_AddRefs(keyStringBundle));
 
   if (!keyStringBundle)
     return;
 
   nsAutoString separator;
-  keyStringBundle->GetStringFromName(u"MODIFIER_SEPARATOR",
+  keyStringBundle->GetStringFromName("MODIFIER_SEPARATOR",
                                      getter_Copies(separator));
 
   nsAutoString modifierName;
   if (mModifierMask & kControl) {
-    keyStringBundle->GetStringFromName(u"VK_CONTROL",
+    keyStringBundle->GetStringFromName("VK_CONTROL",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kAlt) {
-    keyStringBundle->GetStringFromName(u"VK_ALT",
+    keyStringBundle->GetStringFromName("VK_ALT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kShift) {
-    keyStringBundle->GetStringFromName(u"VK_SHIFT",
+    keyStringBundle->GetStringFromName("VK_SHIFT",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   if (mModifierMask & kMeta) {
-    keyStringBundle->GetStringFromName(u"VK_META",
+    keyStringBundle->GetStringFromName("VK_META",
                                        getter_Copies(modifierName));
 
     aValue.Append(modifierName);
     aValue.Append(separator);
   }
 
   aValue.Append(mKey);
 }
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -49,18 +49,17 @@ ApplicationAccessible::Name(nsString& aN
 
   nsCOMPtr<nsIStringBundle> bundle;
   nsresult rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties",
                                             getter_AddRefs(bundle));
   if (NS_FAILED(rv))
     return eNameOK;
 
   nsXPIDLString appName;
-  rv = bundle->GetStringFromName(u"brandShortName",
-                                 getter_Copies(appName));
+  rv = bundle->GetStringFromName("brandShortName", getter_Copies(appName));
   if (NS_FAILED(rv) || appName.IsEmpty()) {
     NS_WARNING("brandShortName not found, using default app name");
     appName.AssignLiteral("Gecko based application");
   }
 
   aName.Assign(appName);
   return eNameOK;
 }
--- a/accessible/generic/DocAccessible-inl.h
+++ b/accessible/generic/DocAccessible-inl.h
@@ -130,18 +130,17 @@ DocAccessible::NotifyOfLoad(uint32_t aLo
       new AccStateChangeEvent(this, states::BUSY, false);
     FireDelayedEvent(stateEvent);
   }
 }
 
 inline void
 DocAccessible::MaybeNotifyOfValueChange(Accessible* aAccessible)
 {
-  a11y::role role = aAccessible->Role();
-  if (role == roles::ENTRY || role == roles::COMBOBOX)
+  if (aAccessible->IsCombobox() || aAccessible->Role() == roles::ENTRY)
     FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE, aAccessible);
 }
 
 inline Accessible*
 DocAccessible::GetAccessibleEvenIfNotInMapOrContainer(nsINode* aNode) const
 {
   Accessible* acc = GetAccessibleEvenIfNotInMap(aNode);
   return acc ? acc : GetContainerAccessible(aNode);
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -644,32 +644,35 @@ DocAccessible::ScrollPositionDidChange(n
   const uint32_t kScrollPosCheckWait = 50;
   if (mScrollWatchTimer) {
     mScrollWatchTimer->SetDelay(kScrollPosCheckWait);  // Create new timer, to avoid leaks
   }
   else {
     mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (mScrollWatchTimer) {
       NS_ADDREF_THIS(); // Kung fu death grip
-      mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
-                                              kScrollPosCheckWait,
-                                              nsITimer::TYPE_REPEATING_SLACK);
+      mScrollWatchTimer->InitWithNamedFuncCallback(
+        ScrollTimerCallback,
+        this,
+        kScrollPosCheckWait,
+        nsITimer::TYPE_REPEATING_SLACK,
+        "a11y::DocAccessible::ScrollPositionDidChange");
     }
   }
   mScrollPositionChangedTicks = 1;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIObserver
 
 NS_IMETHODIMP
 DocAccessible::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData)
 {
-  if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {    
+  if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
     // State editable will now be set, readonly is now clear
     // Normally we only fire delayed events created from the node, not an
     // accessible object. See the AccStateChangeEvent constructor for details
     // about this exceptional case.
     RefPtr<AccEvent> event =
       new AccStateChangeEvent(this, states::EDITABLE, true);
     FireDelayedEvent(event);
   }
@@ -812,17 +815,17 @@ DocAccessible::AttributeChangedImpl(Acce
   // DOM attribute & resulting layout to actually change. Otherwise,
   // assistive technology will retrieve the wrong state/value/selection info.
 
   // XXX todo
   // We still need to handle special HTML cases here
   // For example, if an <img>'s usemap attribute is modified
   // Otherwise it may just be a state change, for example an object changing
   // its visibility
-  // 
+  //
   // XXX todo: report aria state changes for "undefined" literal value changes
   // filed as bug 472142
   //
   // XXX todo:  invalidate accessible when aria state changes affect exposed role
   // filed as bug 472143
 
   // Universal boolean properties that don't require a role. Fire the state
   // change when disabled or aria-disabled attribute is set.
@@ -1046,16 +1049,23 @@ DocAccessible::ARIAAttributeChanged(Acce
   if (aAttribute == nsGkAtoms::aria_valuenow &&
       (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
        elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
                         nsGkAtoms::_empty, eCaseMatters))) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
     return;
   }
 
+  if (aAttribute == nsGkAtoms::aria_current) {
+    RefPtr<AccEvent> event =
+      new AccStateChangeEvent(aAccessible, states::CURRENT);
+    FireDelayedEvent(event);
+    return;
+  }
+
   if (aAttribute == nsGkAtoms::aria_owns) {
     mNotificationController->ScheduleRelocation(aAccessible);
   }
 }
 
 void
 DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
 {
@@ -1165,20 +1175,17 @@ DocAccessible::ContentRemoved(nsIDocumen
     logging::Node("container node", aContainerNode);
     logging::Node("content node", aChildNode);
     logging::MsgEnd();
   }
 #endif
   // This one and content removal notification from layout may result in
   // double processing of same subtrees. If it pops up in profiling, then
   // consider reusing a document node cache to reject these notifications early.
-  Accessible* container = GetAccessibleOrContainer(aContainerNode);
-  if (container) {
-    UpdateTreeOnRemoval(container, aChildNode);
-  }
+  ContentRemoved(aChildNode);
 }
 
 void
 DocAccessible::ParentChainChanged(nsIContent* aContent)
 {
 }
 
 
@@ -1326,16 +1333,18 @@ DocAccessible::UnbindFromDocument(Access
 #endif
   }
 
   // Remove an accessible from node-to-accessible map if it exists there.
   if (aAccessible->IsNodeMapEntry() &&
       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
 
+  aAccessible->mStateFlags |= eIsNotInDocument;
+
   // Update XPCOM part.
   xpcAccessibleDocument* xpcDoc = GetAccService()->GetCachedXPCDocument(this);
   if (xpcDoc)
     xpcDoc->NotifyOfShutdown(aAccessible);
 
   void* uniqueID = aAccessible->UniqueID();
 
   NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
@@ -1377,17 +1386,17 @@ DocAccessible::RecreateAccessible(nsICon
 #endif
 
   // XXX: we shouldn't recreate whole accessible subtree, instead we should
   // subclass hide and show events to handle them separately and implement their
   // coalescence with normal hide and show events. Note, in this case they
   // should be coalesced with normal show/hide events.
 
   nsIContent* parent = aContent->GetFlattenedTreeParent();
-  ContentRemoved(parent, aContent);
+  ContentRemoved(aContent);
   ContentInserted(parent, aContent, aContent->GetNextSibling());
 }
 
 void
 DocAccessible::ProcessInvalidationList()
 {
   // Invalidate children of container accessible for each element in
   // invalidation list. Allow invalidation list insertions while container
@@ -1453,17 +1462,17 @@ DocAccessible::NotifyOfLoading(bool aIsR
 {
   // Mark the document accessible as loading, if it stays alive then we'll mark
   // it as loaded when we receive proper notification.
   mLoadState &= ~eDOMLoaded;
 
   if (!IsLoadEventTarget())
     return;
 
-  if (aIsReloading) {
+  if (aIsReloading && !mLoadEventType) {
     // Fire reload and state busy events on existing document accessible while
     // event from user input flag can be calculated properly and accessible
     // is alive. When new document gets loaded then this one is destroyed.
     RefPtr<AccEvent> reloadEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
     nsEventShell::FireEvent(reloadEvent);
   }
 
@@ -1477,18 +1486,21 @@ DocAccessible::NotifyOfLoading(bool aIsR
 void
 DocAccessible::DoInitialUpdate()
 {
   if (nsCoreUtils::IsTabDocument(mDocumentNode)) {
     mDocFlags |= eTabDocument;
     if (IPCAccessibilityActive()) {
       nsIDocShell* docShell = mDocumentNode->GetDocShell();
       if (RefPtr<dom::TabChild> tabChild = dom::TabChild::GetFrom(docShell)) {
-        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this);
+        DocAccessibleChild* ipcDoc = new DocAccessibleChild(this, tabChild);
         SetIPCDoc(ipcDoc);
+        if (IsRoot()) {
+          tabChild->SetTopLevelDocAccessibleChild(ipcDoc);
+        }
 
 #if defined(XP_WIN)
         IAccessibleHolder holder(CreateHolderFromAccessible(this));
         int32_t childID = AccessibleWrap::GetChildIDFor(this);
 #else
         int32_t holder = 0, childID = 0;
 #endif
         tabChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0, childID,
@@ -1967,45 +1979,71 @@ DocAccessible::FireEventsOnInsertion(Acc
         break;
       }
     }
     while ((ancestor = ancestor->Parent()));
   }
 }
 
 void
-DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode)
+DocAccessible::ContentRemoved(Accessible* aChild)
 {
-  // If child node is not accessible then look for its accessible children.
-  Accessible* child = GetAccessible(aChildNode);
+  Accessible* parent = aChild->Parent();
+  MOZ_DIAGNOSTIC_ASSERT(parent, "Unattached accessible from tree");
+
 #ifdef A11Y_LOG
   logging::TreeInfo("process content removal", 0,
-                    "container", aContainer, "child", aChildNode);
+                    "container", parent, "child", aChild, nullptr);
 #endif
 
-  TreeMutation mt(aContainer);
-  if (child) {
-    mt.BeforeRemoval(child);
-    MOZ_ASSERT(aContainer == child->Parent(), "Wrong parent");
-    aContainer->RemoveChild(child);
-    UncacheChildrenInSubtree(child);
+  // XXX: event coalescence may kill us
+  RefPtr<Accessible> kungFuDeathGripChild(aChild);
+
+  TreeMutation mt(parent);
+  mt.BeforeRemoval(aChild);
+
+  if (aChild->IsDefunct()) {
+    MOZ_ASSERT_UNREACHABLE("Event coalescence killed the accessible");
     mt.Done();
     return;
   }
 
-  TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
-  while (Accessible* child = walker.Next()) {
-    mt.BeforeRemoval(child);
-    MOZ_ASSERT(aContainer == child->Parent(), "Wrong parent");
-    aContainer->RemoveChild(child);
-    UncacheChildrenInSubtree(child);
+  MOZ_DIAGNOSTIC_ASSERT(aChild->Parent(), "Alive but unparented #1");
+
+  if (aChild->IsRelocated()) {
+    nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(parent);
+    MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+    owned->RemoveElement(aChild);
+    if (owned->Length() == 0) {
+      mARIAOwnsHash.Remove(parent);
+    }
   }
+  MOZ_DIAGNOSTIC_ASSERT(aChild->Parent(), "Unparented #2");
+  parent->RemoveChild(aChild);
+  UncacheChildrenInSubtree(aChild);
+
   mt.Done();
 }
 
+void
+DocAccessible::ContentRemoved(nsIContent* aContentNode)
+{
+  // If child node is not accessible then look for its accessible children.
+  Accessible* acc = GetAccessible(aContentNode);
+  if (acc) {
+    ContentRemoved(acc);
+  }
+
+  dom::AllChildrenIterator iter =
+    dom::AllChildrenIterator(aContentNode, nsIContent::eAllChildren, true);
+  while (nsIContent* childNode = iter.GetNextChild()) {
+    ContentRemoved(childNode);
+  }
+}
+
 bool
 DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
 {
   if (!aElement->HasID())
     return false;
 
   AttrRelProviderArray* list =
     mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
@@ -2020,117 +2058,92 @@ DocAccessible::RelocateARIAOwnedIfNeeded
       }
     }
   }
 
   return false;
 }
 
 void
-DocAccessible::ValidateARIAOwned()
-{
-  for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
-    Accessible* owner = it.Key();
-    nsTArray<RefPtr<Accessible> >* children = it.UserData();
-
-    // Owner is about to die, put children back if applicable.
-    if (!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
-        !owner->IsInDocument()) {
-      PutChildrenBack(children, 0);
-      it.Remove();
-      continue;
-    }
-
-    for (uint32_t idx = 0; idx < children->Length(); idx++) {
-      Accessible* child = children->ElementAt(idx);
-      if (!child->IsInDocument()) {
-        children->RemoveElementAt(idx);
-        idx--;
-        continue;
-      }
-
-      NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
-
-      // If DOM node doesn't have a frame anymore then shutdown its accessible.
-      if (child->Parent() && !child->GetFrame()) {
-        UpdateTreeOnRemoval(child->Parent(), child->GetContent());
-        children->RemoveElementAt(idx);
-        idx--;
-        continue;
-      }
-
-      NS_ASSERTION(child->Parent() == owner,
-                   "Illigally stolen ARIA owned child!");
-    }
-
-    if (children->Length() == 0) {
-      it.Remove();
-    }
-  }
-}
-
-void
 DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
 {
-  nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
-
   MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
   MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns relocation", logging::eVerbose, aOwner);
 #endif
 
+  nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.LookupOrAdd(aOwner);
+
   IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
-  uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
+  uint32_t idx = 0;
   while (nsIContent* childEl = iter.NextElem()) {
     Accessible* child = GetAccessible(childEl);
+    auto insertIdx = aOwner->ChildCount() - owned->Length() + idx;
 
     // Make an attempt to create an accessible if it wasn't created yet.
     if (!child) {
       if (aOwner->IsAcceptableChild(childEl)) {
         child = GetAccService()->CreateAccessible(childEl, aOwner);
         if (child) {
           TreeMutation imut(aOwner);
           aOwner->InsertChildAt(insertIdx, child);
           imut.AfterInsertion(child);
           imut.Done();
 
           child->SetRelocated(true);
-          children->InsertElementAt(arrayIdx, child);
+          owned->InsertElementAt(idx, child);
+          idx++;
 
           // Create subtree before adjusting the insertion index, since subtree
           // creation may alter children in the container.
           CreateSubtree(child);
           FireEventsOnInsertion(aOwner);
-
-          insertIdx = child->IndexInParent() + 1;
-          arrayIdx++;
         }
       }
       continue;
     }
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns traversal", logging::eVerbose,
                     "candidate", child, nullptr);
 #endif
 
     // Same child on same position, no change.
-    if (child->Parent() == aOwner &&
-        child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
-      NS_ASSERTION(child == children->ElementAt(arrayIdx), "Not in sync!");
-      insertIdx++; arrayIdx++;
-      continue;
+    if (child->Parent() == aOwner) {
+      int32_t indexInParent = child->IndexInParent();
+
+      // The child is being placed in its current index,
+      // eg. aria-owns='id1 id2 id3' is changed to aria-owns='id3 id2 id1'.
+      if (indexInParent == static_cast<int32_t>(insertIdx)) {
+        MOZ_ASSERT(child->IsRelocated(),
+                   "A child, having an index in parent from aria ownded indices range, has to be aria owned");
+        MOZ_ASSERT(owned->ElementAt(idx) == child,
+                   "Unexpected child in ARIA owned array");
+        idx++;
+        continue;
+      }
+
+      // The child is being inserted directly after its current index,
+      // resulting in a no-move case. This will happen when a parent aria-owns
+      // its last ordinal child:
+      // <ul aria-owns='id2'><li id='id1'></li><li id='id2'></li></ul>
+      if (indexInParent == static_cast<int32_t>(insertIdx) - 1) {
+        MOZ_ASSERT(!child->IsRelocated(), "Child should be in its ordinal position");
+        child->SetRelocated(true);
+        owned->InsertElementAt(idx, child);
+        idx++;
+        continue;
+      }
     }
 
-    NS_ASSERTION(children->SafeElementAt(arrayIdx) != child, "Already in place!");
+    MOZ_ASSERT(owned->SafeElementAt(idx) != child, "Already in place!");
 
-    nsTArray<RefPtr<Accessible> >::index_type idx = children->IndexOf(child);
-    if (idx < arrayIdx) {
+    if (owned->IndexOf(child) < idx) {
       continue; // ignore second entry of same ID
     }
 
     // A new child is found, check for loops.
     if (child->Parent() != aOwner) {
       Accessible* parent = aOwner;
       while (parent && parent != child && !parent->IsDoc()) {
         parent = parent->Parent();
@@ -2138,34 +2151,34 @@ DocAccessible::DoARIAOwnsRelocation(Acce
       // A referred child cannot be a parent of the owner.
       if (parent == child) {
         continue;
       }
     }
 
     if (MoveChild(child, aOwner, insertIdx)) {
       child->SetRelocated(true);
-      children->InsertElementAt(arrayIdx, child);
-      arrayIdx++;
-      insertIdx = child->IndexInParent() + 1;
+      owned->InsertElementAt(idx, child);
+      idx++;
     }
   }
 
   // Put back children that are not seized anymore.
-  PutChildrenBack(children, arrayIdx);
-  if (children->Length() == 0) {
+  PutChildrenBack(owned, idx);
+  if (owned->Length() == 0) {
     mARIAOwnsHash.Remove(aOwner);
   }
 }
 
 void
 DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
                                uint32_t aStartIdx)
 {
-  nsTArray<RefPtr<Accessible> > containers;
+  MOZ_ASSERT(aStartIdx <= aChildren->Length(), "Wrong removal index");
+
   for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
     Accessible* child = aChildren->ElementAt(idx);
     if (!child->IsInDocument()) {
       continue;
     }
 
     // Remove the child from the owner
     Accessible* owner = child->Parent();
@@ -2177,50 +2190,82 @@ DocAccessible::PutChildrenBack(nsTArray<
 #ifdef A11Y_LOG
     logging::TreeInfo("aria owns put child back", 0,
                       "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 = GetContainerAccessible(child->GetContent());
+    Accessible* origContainer = AccessibleOrTrueContainer(content->GetParentNode());
     if (origContainer) {
       TreeWalker walker(origContainer);
-      if (walker.Seek(child->GetContent())) {
+      if (walker.Seek(content)) {
         Accessible* prevChild = walker.Prev();
-        idxInParent = prevChild ? prevChild->IndexInParent() + 1 : 0;
+        if (prevChild) {
+          idxInParent = prevChild->IndexInParent() + 1;
+          MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
+          origContainer = prevChild->Parent();
+        }
+        else {
+          idxInParent = 0;
+        }
       }
     }
-    MoveChild(child, origContainer, idxInParent);
+
+    // The child may have already be in its ordinal place for 2 reasons:
+    // 1. It was the last ordinal child, and the first aria-owned child.
+    //    given:      <ul id="list" aria-owns="b"><li id="a"></li><li id="b"></li></ul>
+    //    after load: $("list").setAttribute("aria-owns", "");
+    // 2. The preceding adopted children were just reclaimed, eg:
+    //    given:      <ul id="list"><li id="b"></li></ul>
+    //    after load: $("list").setAttribute("aria-owns", "a b");
+    //    later:      $("list").setAttribute("aria-owns", "");
+    if (origContainer != owner || child->IndexInParent() != idxInParent) {
+      MoveChild(child, origContainer, idxInParent);
+    } else {
+      MOZ_ASSERT(!child->PrevSibling() || !child->PrevSibling()->IsRelocated(),
+                 "No relocated child should appear before this one");
+      MOZ_ASSERT(!child->NextSibling() || child->NextSibling()->IsRelocated(),
+                 "No ordinal child should appear after this one");
+    }
   }
 
   aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
 }
 
 bool
 DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
                          int32_t aIdxInParent)
 {
   MOZ_ASSERT(aChild, "No child");
   MOZ_ASSERT(aChild->Parent(), "No parent");
+  MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
+             "Wrong insertion point for a moving child");
 
   Accessible* curParent = aChild->Parent();
 
 #ifdef A11Y_LOG
   logging::TreeInfo("move child", 0,
                     "old parent", curParent, "new parent", aNewParent,
                     "child", aChild, nullptr);
 #endif
 
-  // If the child was taken from from an ARIA owns element.
+  // Forget aria-owns info in case of ARIA owned element. The caller is expected
+  // to update it if needed.
   if (aChild->IsRelocated()) {
-    nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
-    children->RemoveElement(aChild);
+    aChild->SetRelocated(false);
+    nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(curParent);
+    MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+    owned->RemoveElement(aChild);
+    if (owned->Length() == 0) {
+      mARIAOwnsHash.Remove(curParent);
+    }
   }
 
   NotificationController::MoveGuard mguard(mNotificationController);
 
   if (curParent == aNewParent) {
     MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
     curParent->MoveChild(aIdxInParent, aChild);
 
@@ -2230,23 +2275,31 @@ DocAccessible::MoveChild(Accessible* aCh
 #endif
     return true;
   }
 
   if (!aNewParent->IsAcceptableChild(aChild->GetContent())) {
     return false;
   }
 
+  MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
+             "Wrong insertion point for a moving child");
+
+  // If the child cannot be re-inserted into the tree, then make sure to remove
+  // it from its present parent and then shutdown it.
+  bool hasInsertionPoint = (aIdxInParent != -1) ||
+    (aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()));
+
   TreeMutation rmut(curParent);
-  rmut.BeforeRemoval(aChild, TreeMutation::kNoShutdown);
+  rmut.BeforeRemoval(aChild, hasInsertionPoint && TreeMutation::kNoShutdown);
   curParent->RemoveChild(aChild);
   rmut.Done();
 
   // No insertion point for the child.
-  if (aIdxInParent == -1) {
+  if (!hasInsertionPoint) {
     return true;
   }
 
   TreeMutation imut(aNewParent);
   aNewParent->InsertChildAt(aIdxInParent, aChild);
   imut.AfterInsertion(aChild);
   imut.Done();
 
@@ -2303,20 +2356,30 @@ DocAccessible::CacheChildrenInSubtree(Ac
 }
 
 void
 DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
 {
   aRoot->mStateFlags |= eIsNotInDocument;
   RemoveDependentIDsFor(aRoot);
 
+  nsTArray<RefPtr<Accessible> >* owned = mARIAOwnsHash.Get(aRoot);
   uint32_t count = aRoot->ContentChildCount();
   for (uint32_t idx = 0; idx < count; idx++) {
     Accessible* child = aRoot->ContentChildAt(idx);
 
+    if (child->IsRelocated()) {
+      MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash");
+      owned->RemoveElement(child);
+      if (owned->Length() == 0) {
+        mARIAOwnsHash.Remove(aRoot);
+        owned = nullptr;
+      }
+    }
+
     // Removing this accessible from the document doesn't mean anything about
     // accessibles for subdocuments, so skip removing those from the tree.
     if (!child->IsDoc()) {
       UncacheChildrenInSubtree(child);
     }
   }
 
   if (aRoot->IsNodeMapEntry() &&
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -132,16 +132,21 @@ public:
   {
     // eDOMLoaded flag check is used for error pages as workaround to make this
     // method return correct result since error pages do not receive 'pageshow'
     // event and as consequence nsIDocument::IsShowing() returns false.
     return mDocumentNode && mDocumentNode->IsVisible() &&
       (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded));
   }
 
+  bool IsHidden() const
+  {
+    return mDocumentNode->Hidden();
+  }
+
   /**
    * Document load states.
    */
   enum LoadState {
     // initial tree construction is pending
     eTreeConstructionPending = 0,
     // initial tree construction done
     eTreeConstructed = 1,
@@ -152,17 +157,17 @@ public:
     // document and all its subdocuments are ready
     eCompletelyLoaded = eReady | 1 << 2
   };
 
   /**
    * Return true if the document has given document state.
    */
   bool HasLoadState(LoadState aState) const
-    { return (mLoadState & static_cast<uint32_t>(aState)) == 
+    { return (mLoadState & static_cast<uint32_t>(aState)) ==
         static_cast<uint32_t>(aState); }
 
   /**
    * Return a native window handler or pointer depending on platform.
    */
   virtual void* GetNativeWindow() const;
 
   /**
@@ -337,28 +342,20 @@ public:
   /**
    * Notify the document accessible that content was inserted.
    */
   void ContentInserted(nsIContent* aContainerNode,
                        nsIContent* aStartChildNode,
                        nsIContent* aEndChildNode);
 
   /**
-   * Notify the document accessible that content was removed.
+   * Update the tree on content removal.
    */
-  void ContentRemoved(Accessible* aContainer, nsIContent* aChildNode)
-  {
-    // Update the whole tree of this document accessible when the container is
-    // null (document element is removed).
-    UpdateTreeOnRemoval((aContainer ? aContainer : this), aChildNode);
-  }
-  void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode)
-  {
-    ContentRemoved(AccessibleOrTrueContainer(aContainerNode), aChildNode);
-  }
+  void ContentRemoved(Accessible* aAccessible);
+  void ContentRemoved(nsIContent* aContentNode);
 
   /**
    * Updates accessible tree when rendered text is changed.
    */
   void UpdateText(nsIContent* aTextNode);
 
   /**
    * Recreate an accessible, results in hide/show events pair.
@@ -508,26 +505,16 @@ protected:
    *
    * While children are cached we may encounter the case there's no accessible
    * for referred content by related accessible. Store these related nodes to
    * invalidate their containers later.
    */
   void ProcessInvalidationList();
 
   /**
-   * Update the accessible tree for content removal.
-   */
-  void UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode);
-
-  /**
-   * Validates all aria-owns connections and updates the tree accordingly.
-   */
-  void ValidateARIAOwned();
-
-  /**
    * Steals or puts back accessible subtrees.
    */
   void DoARIAOwnsRelocation(Accessible* aOwner);
 
   /**
    * Moves children back under their original parents.
    */
   void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -60,17 +60,17 @@ NS_IMPL_ISUPPORTS_INHERITED0(HyperTextAc
 role
 HyperTextAccessible::NativeRole()
 {
   a11y::role r = GetAccService()->MarkupRole(mContent);
   if (r != roles::NOTHING)
     return r;
 
   nsIFrame* frame = GetFrame();
-  if (frame && frame->GetType() == nsGkAtoms::inlineFrame)
+  if (frame && frame->IsInlineFrame())
     return roles::TEXT;
 
   return roles::TEXT_CONTAINER;
 }
 
 uint64_t
 HyperTextAccessible::NativeState()
 {
@@ -91,17 +91,17 @@ HyperTextAccessible::NativeState()
 }
 
 nsIntRect
 HyperTextAccessible::GetBoundsInFrame(nsIFrame* aFrame,
                                       uint32_t aStartRenderedOffset,
                                       uint32_t aEndRenderedOffset)
 {
   nsPresContext* presContext = mDoc->PresContext();
-  if (aFrame->GetType() != nsGkAtoms::textFrame) {
+  if (!aFrame->IsTextFrame()) {
     return aFrame->GetScreenRectInAppUnits().
       ToNearestPixels(presContext->AppUnitsPerDevPixel());
   }
 
   // Substring must be entirely within the same text node.
   int32_t startContentOffset, endContentOffset;
   nsresult rv = RenderedToContentOffset(aFrame, aStartRenderedOffset, &startContentOffset);
   NS_ENSURE_SUCCESS(rv, nsIntRect());
@@ -534,17 +534,17 @@ HyperTextAccessible::FindOffset(uint32_t
   nsIFrame* childFrame = child->GetFrame();
   if (!childFrame) {
     NS_ERROR("No child frame");
     return 0;
   }
 
   int32_t innerContentOffset = innerOffset;
   if (child->IsTextLeaf()) {
-    NS_ASSERTION(childFrame->GetType() == nsGkAtoms::textFrame, "Wrong frame!");
+    NS_ASSERTION(childFrame->IsTextFrame(), "Wrong frame!");
     RenderedToContentOffset(childFrame, innerOffset, &innerContentOffset);
   }
 
   nsIFrame* frameAtOffset = childFrame;
   int32_t unusedOffsetInFrame = 0;
   childFrame->GetChildFrameContainingOffset(innerContentOffset, true,
                                             &unusedOffsetInFrame,
                                             &frameAtOffset);
@@ -912,17 +912,17 @@ HyperTextAccessible::TextAttributes(bool
   int32_t offsetInAcc = offset - startOffset;
 
   TextAttrsMgr textAttrsMgr(this, aIncludeDefAttrs, accAtOffset,
                             accAtOffsetIdx);
   textAttrsMgr.GetAttributes(attributes, &startOffset, &endOffset);
 
   // Compute spelling attributes on text accessible only.
   nsIFrame *offsetFrame = accAtOffset->GetFrame();
-  if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
+  if (offsetFrame && offsetFrame->IsTextFrame()) {
     int32_t nodeOffset = 0;
     RenderedToContentOffset(offsetFrame, offsetInAcc, &nodeOffset);
 
     // Set 'misspelled' text attribute.
     GetSpellTextAttr(accAtOffset->GetNode(), nodeOffset,
                      &startOffset, &endOffset, attributes);
   }
 
@@ -1104,17 +1104,17 @@ already_AddRefed<nsIPersistentProperties
 HyperTextAccessible::NativeAttributes()
 {
   nsCOMPtr<nsIPersistentProperties> attributes =
     AccessibleWrap::NativeAttributes();
 
   // 'formatting' attribute is deprecated, 'display' attribute should be
   // instead.
   nsIFrame *frame = GetFrame();
-  if (frame && frame->GetType() == nsGkAtoms::blockFrame) {
+  if (frame && frame->IsBlockFrame()) {
     nsAutoString unused;
     attributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"),
                                   NS_LITERAL_STRING("block"), unused);
   }
 
   if (FocusMgr()->IsFocused(this)) {
     int32_t lineNumber = CaretLineNumber();
     if (lineNumber >= 1) {
@@ -1218,17 +1218,17 @@ HyperTextAccessible::OffsetAtPoint(int32
     nsIFrame *frame = primaryFrame;
     while (frame) {
       nsIContent *content = frame->GetContent();
       NS_ENSURE_TRUE(content, -1);
       nsPoint pointInFrame = pointInHyperText - frame->GetOffsetTo(hyperFrame);
       nsSize frameSize = frame->GetSize();
       if (pointInFrame.x < frameSize.width && pointInFrame.y < frameSize.height) {
         // Finished
-        if (frame->GetType() == nsGkAtoms::textFrame) {
+        if (frame->IsTextFrame()) {
           nsIFrame::ContentOffsets contentOffsets =
             frame->GetContentOffsetsFromPointExternal(pointInFrame, nsIFrame::IGNORE_SELECTION_STYLE);
           if (contentOffsets.IsNull() || contentOffsets.content != content) {
             return -1; // Not found
           }
           uint32_t addToOffset;
           nsresult rv = ContentToRenderedOffset(primaryFrame,
                                                 contentOffsets.offset,
@@ -1606,18 +1606,18 @@ HyperTextAccessible::SelectionBoundsAt(i
 
   uint32_t rangeCount = ranges.Length();
   if (aSelectionNum < 0 || aSelectionNum >= static_cast<int32_t>(rangeCount))
     return false;
 
   nsRange* range = ranges[aSelectionNum];
 
   // Get start and end points.
-  nsINode* startNode = range->GetStartParent();
-  nsINode* endNode = range->GetEndParent();
+  nsINode* startNode = range->GetStartContainer();
+  nsINode* endNode = range->GetEndContainer();
   int32_t startOffset = range->StartOffset(), endOffset = range->EndOffset();
 
   // Make sure start is before end, by swapping DOM points.  This occurs when
   // the user selects backwards in the text.
   int32_t rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
                                                        startNode, startOffset);
   if (rangeCompare < 0) {
     nsINode* tempNode = startNode;
@@ -1782,32 +1782,33 @@ HyperTextAccessible::SelectionRanges(nsT
   dom::Selection* sel = DOMSelection();
   if (!sel)
     return;
 
   aRanges->SetCapacity(sel->RangeCount());
 
   for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
     nsRange* DOMRange = sel->GetRangeAt(idx);
-    HyperTextAccessible* startParent =
-      nsAccUtils::GetTextContainer(DOMRange->GetStartParent());
-    HyperTextAccessible* endParent =
-      nsAccUtils::GetTextContainer(DOMRange->GetEndParent());
-    if (!startParent || !endParent)
+    HyperTextAccessible* startContainer =
+      nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
+    HyperTextAccessible* endContainer =
+      nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
+    if (!startContainer || !endContainer) {
       continue;
+    }
 
     int32_t startOffset =
-      startParent->DOMPointToOffset(DOMRange->GetStartParent(),
-                                    DOMRange->StartOffset(), false);
+      startContainer->DOMPointToOffset(DOMRange->GetStartContainer(),
+                                       DOMRange->StartOffset(), false);
     int32_t endOffset =
-      endParent->DOMPointToOffset(DOMRange->GetEndParent(),
-                                  DOMRange->EndOffset(), true);
+      endContainer->DOMPointToOffset(DOMRange->GetEndContainer(),
+                                     DOMRange->EndOffset(), true);
 
     TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
-                    startParent, startOffset, endParent, endOffset);
+                    startContainer, startOffset, endContainer, endOffset);
     *(aRanges->AppendElement()) = Move(tr);
   }
 }
 
 void
 HyperTextAccessible::VisibleRanges(nsTArray<a11y::TextRange>* aRanges) const
 {
 }
@@ -1966,18 +1967,17 @@ HyperTextAccessible::ContentToRenderedOf
     return NS_OK;
   }
 
   if (IsTextField()) {
     *aRenderedOffset = aContentOffset;
     return NS_OK;
   }
 
-  NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
-               "Need text frame for offset conversion");
+  NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
                "Call on primary frame only");
 
   nsIFrame::RenderedText text = aFrame->GetRenderedText(aContentOffset,
       aContentOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
       nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
   *aRenderedOffset = text.mOffsetWithinNodeRenderedText;
 
@@ -1991,18 +1991,17 @@ HyperTextAccessible::RenderedToContentOf
   if (IsTextField()) {
     *aContentOffset = aRenderedOffset;
     return NS_OK;
   }
 
   *aContentOffset = 0;
   NS_ENSURE_TRUE(aFrame, NS_ERROR_FAILURE);
 
-  NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
-               "Need text frame for offset conversion");
+  NS_ASSERTION(aFrame->IsTextFrame(), "Need text frame for offset conversion");
   NS_ASSERTION(aFrame->GetPrevContinuation() == nullptr,
                "Call on primary frame only");
 
   nsIFrame::RenderedText text = aFrame->GetRenderedText(aRenderedOffset,
       aRenderedOffset + 1, nsIFrame::TextOffsetType::OFFSETS_IN_RENDERED_TEXT,
       nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
   *aContentOffset = text.mOffsetWithinNodeText;
 
@@ -2095,17 +2094,17 @@ HyperTextAccessible::GetDOMPointByFrameO
     nsIContent* content = aAccessible->GetContent();
     NS_ASSERTION(content, "Shouldn't operate on defunct accessible!");
 
     nsIContent* parent = content->GetParent();
 
     aPoint->idx = parent->IndexOf(content) + 1;
     aPoint->node = parent;
 
-  } else if (aFrame->GetType() == nsGkAtoms::textFrame) {
+  } else if (aFrame->IsTextFrame()) {
     nsIContent* content = aFrame->GetContent();
     NS_ENSURE_STATE(content);
 
     nsIFrame *primaryFrame = content->GetPrimaryFrame();
     nsresult rv = RenderedToContentOffset(primaryFrame, aOffset, &(aPoint->idx));
     NS_ENSURE_SUCCESS(rv, rv);
 
     aPoint->node = content;
@@ -2147,27 +2146,27 @@ HyperTextAccessible::GetSpellTextAttr(ns
   uint32_t startOffset = 0, endOffset = 0;
   for (int32_t idx = 0; idx < rangeCount; idx++) {
     nsRange* range = domSel->GetRangeAt(idx);
     if (range->Collapsed())
       continue;
 
     // See if the point comes after the range in which case we must continue in
     // case there is another range after this one.
-    nsINode* endNode = range->GetEndParent();
+    nsINode* endNode = range->GetEndContainer();
     int32_t endNodeOffset = range->EndOffset();
     if (nsContentUtils::ComparePoints(aNode, aNodeOffset,
                                       endNode, endNodeOffset) >= 0)
       continue;
 
     // At this point our point is either in this range or before it but after
     // the previous range.  So we check to see if the range starts before the
     // point in which case the point is in the missspelled range, otherwise it
     // must be before the range and after the previous one if any.
-    nsINode* startNode = range->GetStartParent();
+    nsINode* startNode = range->GetStartContainer();
     int32_t startNodeOffset = range->StartOffset();
     if (nsContentUtils::ComparePoints(startNode, startNodeOffset, aNode,
                                       aNodeOffset) <= 0) {
       startOffset = DOMPointToOffset(startNode, startNodeOffset);
 
       endOffset = DOMPointToOffset(endNode, endNodeOffset);
 
       if (startOffset > *aStartOffset)
@@ -2184,17 +2183,17 @@ HyperTextAccessible::GetSpellTextAttr(ns
       return;
     }
 
     // This range came after the point.
     endOffset = DOMPointToOffset(startNode, startNodeOffset);
 
     if (idx > 0) {
       nsRange* prevRange = domSel->GetRangeAt(idx - 1);
-      startOffset = DOMPointToOffset(prevRange->GetEndParent(),
+      startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
                                      prevRange->EndOffset());
     }
 
     if (startOffset > *aStartOffset)
       *aStartOffset = startOffset;
 
     if (endOffset < *aEndOffset)
       *aEndOffset = endOffset;
@@ -2202,17 +2201,17 @@ HyperTextAccessible::GetSpellTextAttr(ns
     return;
   }
 
   // We never found a range that ended after the point, therefore we know that
   // the point is not in a range, that we do not need to compute an end offset,
   // and that we should use the end offset of the last range to compute the
   // start offset of the text attribute range.
   nsRange* prevRange = domSel->GetRangeAt(rangeCount - 1);
-  startOffset = DOMPointToOffset(prevRange->GetEndParent(),
+  startOffset = DOMPointToOffset(prevRange->GetEndContainer(),
                                  prevRange->EndOffset());
 
   if (startOffset > *aStartOffset)
     *aStartOffset = startOffset;
 }
 
 bool
 HyperTextAccessible::IsTextRole()
--- a/accessible/generic/ImageAccessible.cpp
+++ b/accessible/generic/ImageAccessible.cpp
@@ -106,17 +106,17 @@ ImageAccessible::ActionCount()
   return HasLongDesc() ? actionCount + 1 : actionCount;
 }
 
 void
 ImageAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
 {
   aName.Truncate();
   if (IsLongDescIndex(aIndex) && HasLongDesc())
-    aName.AssignLiteral("showlongdesc"); 
+    aName.AssignLiteral("showlongdesc");
   else
     LinkableAccessible::ActionNameAt(aIndex, aName);
 }
 
 bool
 ImageAccessible::DoAction(uint8_t aIndex)
 {
   // Get the long description uri and open in a new window.
--- a/accessible/generic/OuterDocAccessible.h
+++ b/accessible/generic/OuterDocAccessible.h
@@ -9,17 +9,17 @@
 #include "AccessibleWrap.h"
 
 namespace mozilla {
 namespace a11y {
 class DocAccessibleParent;
 
 /**
  * Used for <browser>, <frame>, <iframe>, <page> or editor> elements.
- * 
+ *
  * In these variable names, "outer" relates to the OuterDocAccessible as
  * opposed to the DocAccessibleWrap which is "inner". The outer node is
  * a something like tags listed above, whereas the inner node corresponds to
  * the inner document root.
  */
 
 class OuterDocAccessible final : public AccessibleWrap
 {
--- a/accessible/generic/RootAccessible.cpp
+++ b/accessible/generic/RootAccessible.cpp
@@ -99,17 +99,17 @@ RootAccessible::NativeRole()
   return DocAccessibleWrap::NativeRole();
 }
 
 // RootAccessible protected member
 #ifdef MOZ_XUL
 uint32_t
 RootAccessible::GetChromeFlags()
 {
-  // Return the flag set for the top level window as defined 
+  // Return the flag set for the top level window as defined
   // by nsIWebBrowserChrome::CHROME_WINDOW_[FLAGNAME]
   // Not simple: nsIXULWindow is not just a QI from nsIDOMWindow
   nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
   NS_ENSURE_TRUE(docShell, 0);
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   NS_ENSURE_TRUE(treeOwner, 0);
   nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
@@ -198,17 +198,17 @@ RootAccessible::AddEventListeners()
 
   return DocAccessible::AddEventListeners();
 }
 
 nsresult
 RootAccessible::RemoveEventListeners()
 {
   nsCOMPtr<EventTarget> target = mDocumentNode;
-  if (target) { 
+  if (target) {
     for (const char* const* e = kEventTypes,
                    * const* e_end = ArrayEnd(kEventTypes);
          e < e_end; ++e) {
       nsresult rv = target->RemoveEventListener(NS_ConvertASCIItoUTF16(*e), this, true);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
@@ -280,17 +280,17 @@ RootAccessible::ProcessDOMEvent(nsIDOMEv
     HandlePopupHidingEvent(origTargetNode);
     return;
   }
 
   DocAccessible* targetDocument = GetAccService()->
     GetDocAccessible(origTargetNode->OwnerDoc());
   NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
 
-  Accessible* accessible = 
+  Accessible* accessible =
     targetDocument->GetAccessibleOrContainer(origTargetNode);
   if (!accessible)
     return;
 
 #ifdef MOZ_XUL
   XULTreeAccessible* treeAcc = accessible->AsXULTree();
   if (treeAcc) {
     if (eventType.EqualsLiteral("TreeRowCountChanged")) {
@@ -512,32 +512,30 @@ RootAccessible::HandlePopupShownEvent(Ac
     // Don't fire menupopup events for combobox and autocomplete lists.
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
                             aAccessible);
     return;
   }
 
   if (role == roles::TOOLTIP) {
     // There is a single <xul:tooltip> node which Mozilla moves around.
-    // The accessible for it stays the same no matter where it moves. 
-    // AT's expect to get an EVENT_SHOW for the tooltip. 
+    // The accessible for it stays the same no matter where it moves.
+    // AT's expect to get an EVENT_SHOW for the tooltip.
     // In event callback the tooltip's accessible will be ready.
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SHOW, aAccessible);
     return;
   }
 
   if (role == roles::COMBOBOX_LIST) {
     // Fire expanded state change event for comboboxes and autocompeletes.
     Accessible* combobox = aAccessible->Parent();
     if (!combobox)
       return;
 
-    roles::Role comboboxRole = combobox->Role();
-    if (comboboxRole == roles::COMBOBOX || 
-	comboboxRole == roles::AUTOCOMPLETE) {
+    if (combobox->IsCombobox() || combobox->IsAutoComplete()) {
       RefPtr<AccEvent> event =
         new AccStateChangeEvent(combobox, states::EXPANDED, true);
       if (event)
         nsEventShell::FireEvent(event);
     }
   }
 }
 
--- a/accessible/generic/TableAccessible.h
+++ b/accessible/generic/TableAccessible.h
@@ -51,31 +51,31 @@ public:
    * Return the index of the cell at the given row and column.
    */
   virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx)
     { return ColCount() * aRowIdx + aColIdx; }
 
   /**
    * Return the column index of the cell with the given index.
    */
-  virtual int32_t ColIndexAt(uint32_t aCellIdx) 
+  virtual int32_t ColIndexAt(uint32_t aCellIdx)
     { return aCellIdx % ColCount(); }
 
   /**
    * Return the row index of the cell with the given index.
    */
-  virtual int32_t RowIndexAt(uint32_t aCellIdx) 
+  virtual int32_t RowIndexAt(uint32_t aCellIdx)
     { return aCellIdx / ColCount(); }
 
   /**
    * Get the row and column indices for the cell at the given index.
    */
   virtual void RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx,
-                                  int32_t* aColIdx) 
-    { 
+                                  int32_t* aColIdx)
+    {
       uint32_t colCount = ColCount();
       *aRowIdx = aCellIdx / colCount;
       *aColIdx = aCellIdx % colCount;
     }
 
   /**
    * Return the number of columns occupied by the cell at the given row and
    * column indices.
--- a/accessible/generic/TextLeafAccessible.h
+++ b/accessible/generic/TextLeafAccessible.h
@@ -5,17 +5,17 @@
 
 #ifndef mozilla_a11y_TextLeafAccessible_h__
 #define mozilla_a11y_TextLeafAccessible_h__
 
 #include "BaseAccessibles.h"
 
 namespace mozilla {
 namespace a11y {
- 
+
 /**
  * Generic class used for text nodes.
  */
 class TextLeafAccessible : public LinkableAccessible
 {
 public:
   TextLeafAccessible(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~TextLeafAccessible();
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -84,17 +84,17 @@ HTMLCheckboxAccessible::NativeState()
   if (!input)
     return state;
 
   if (input->Indeterminate())
     return state | states::MIXED;
 
   if (input->Checked())
     return state | states::CHECKED;
- 
+
   return state;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLCheckboxAccessible: Widgets
 
 bool
 HTMLCheckboxAccessible::IsWidget() const
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -147,17 +147,17 @@ 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);
   if (!aName.IsEmpty())
     return eNameOK;
 
-  // CASE #2 -- no label parameter, get the first child, 
+  // 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);
     aName.CompressWhitespace();
     return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
   }
 
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -971,17 +971,17 @@ HTMLTableAccessible::IsProbablyLayoutTab
   // Check for legitimate data table attributes.
   nsAutoString summary;
   if (mContent->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()) 
+  if (caption && caption->Role() == roles::CAPTION && caption->HasChildren())
     RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures");
 
   for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
        childElm = childElm->GetNextSibling()) {
     if (!childElm->IsHTMLElement())
       continue;
 
     if (childElm->IsAnyOfHTMLElements(nsGkAtoms::col,
@@ -1104,19 +1104,18 @@ HTMLTableAccessible::IsProbablyLayoutTab
 
   // Two column rules
   if (rowCount * colCount <= 10) {
     RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
   }
 
   if (HasDescendant(NS_LITERAL_STRING("embed")) ||
       HasDescendant(NS_LITERAL_STRING("object")) ||
-      HasDescendant(NS_LITERAL_STRING("applet")) ||
       HasDescendant(NS_LITERAL_STRING("iframe"))) {
-    RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
+    RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, or iframe, typical of advertisements");
   }
 
   RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLCaptionAccessible
new file mode 100644
--- /dev/null
+++ b/accessible/interfaces/msaa/AccessibleMarshalThunk.c
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ISimpleDOM.h"
+
+HRESULT STDMETHODCALLTYPE
+ISimpleDOMNode_get_localInterface_Proxy(ISimpleDOMNode * This,
+                                        void **localInterface)
+{
+  return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+ISimpleDOMNode_get_localInterface_Stub(ISimpleDOMNode * This,
+                                       IUnknown **localInterface)
+{
+  return E_NOTIMPL;
+}
+
--- a/accessible/interfaces/msaa/ISimpleDOMNode.idl
+++ b/accessible/interfaces/msaa/ISimpleDOMNode.idl
@@ -163,11 +163,14 @@ interface ISimpleDOMNode : IUnknown
   [propget] HRESULT nextSibling([out, retval] ISimpleDOMNode **node);
   [propget] HRESULT childAt([in] unsigned childIndex, 
                             [out, retval] ISimpleDOMNode **node);
 
   [propget] HRESULT innerHTML([out, retval] BSTR *innerHTML);
 
   [propget, local] HRESULT localInterface([out][retval] void **localInterface);
 
+  [propget, call_as(get_localInterface)]
+  HRESULT remoteLocalInterface([out][retval] IUnknown **localInterface);
+
   [propget] HRESULT language([out, retval] BSTR *language);
 }
 
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 GeckoSharedLibrary('AccessibleMarshal', linkage=None)
 
 SOURCES += [
     '!dlldata.c',
     '!ISimpleDOM_i.c',
     '!ISimpleDOM_p.c',
+    'AccessibleMarshalThunk.c',
 ]
 
 DEFINES['REGISTER_PROXY_DLL'] = True
 # The following line is required to preserve compatibility with older versions
 # of AccessibleMarshal.dll.
 DEFINES['PROXY_CLSID'] = 'IID_ISimpleDOMNode'
 
 DEFFILE = SRCDIR + '/AccessibleMarshal.def'
--- a/accessible/interfaces/nsIAccessibleRelation.idl
+++ b/accessible/interfaces/nsIAccessibleRelation.idl
@@ -115,16 +115,21 @@ interface nsIAccessibleRelation : nsISup
   const unsigned long RELATION_CONTAINING_DOCUMENT = 0x11;
 
   /**
    * The target object is the topmost containing document object in the tab pane.
    */
   const unsigned long RELATION_CONTAINING_TAB_PANE = 0x12;
 
   /**
+   * The target object is the containing window object.
+   */
+  const unsigned long RELATION_CONTAINING_WINDOW = 0x13;
+
+  /**
    * The target object is the containing application object.
    */
   const unsigned long RELATION_CONTAINING_APPLICATION = 0x14;
 
   /**
    * The target object provides the detailed, extended description for this
    * object. It provides more detailed information than would normally be
    * provided using the DESCRIBED_BY relation. A common use for this relation is
--- a/accessible/interfaces/nsIAccessibleRole.idl
+++ b/accessible/interfaces/nsIAccessibleRole.idl
@@ -294,19 +294,19 @@ interface nsIAccessibleRole : nsISupport
    * group of mutually exclusive options. All objects sharing a single parent
    * that have this attribute are assumed to be part of single mutually
    * exclusive group. It is used for xul:radio, html:input@type="radio",
    * role="radio".
    */
   const unsigned long ROLE_RADIOBUTTON = 45;
 
   /**
-   * Represents a combo box; an edit control with an associated list box that
-   * provides a set of predefined choices. It is used for html:select,
-   * xul:menulist, role="combobox".
+   * Represents a combo box; a popup button with an associated list box that
+   * provides a set of predefined choices. It is used for html:select with a
+   * size of 1 and xul:menulist. See also ROLE_EDITCOMBOBOX.
    */
   const unsigned long ROLE_COMBOBOX = 46;
 
   /**
    * Represents the calendar control.
    */
   const unsigned long ROLE_DROPLIST = 47;
 
@@ -972,9 +972,47 @@ interface nsIAccessibleRole : nsISupport
    */
   const unsigned long ROLE_DETAILS = 167;
 
   /**
    * A text container exposing brief amount of information. See related
    * SUMMARY role.
    */
   const unsigned long ROLE_SUMMARY = 168;
+
+  /**
+   * An ARIA landmark. See related NAVIGATION role.
+   */
+  const unsigned long ROLE_LANDMARK = 169;
+
+  /**
+   * A specific type of ARIA landmark. The ability to distinguish navigation
+   * landmarks from other types of landmarks is needed because macOS has a
+   * specific AXSubrole and AXRoleDescription for navigation landmarks.
+   */
+  const unsigned long ROLE_NAVIGATION = 170;
+
+  /**
+   * An object that contains the text of a footnote.
+   */
+  const unsigned long ROLE_FOOTNOTE = 171;
+
+  /**
+   * A complete or self-contained composition in a document, page, application,
+   * or site and that is, in principle, independently distributable or reusable,
+   * e.g. in syndication.
+   */
+  const unsigned long ROLE_ARTICLE = 172;
+
+  /**
+   * A perceivable section containing content that is relevant to a specific,
+   * author-specified purpose and sufficiently important that users will likely
+   * want to be able to navigate to the section easily and to have it listed in
+   * a summary of the page.
+   */
+  const unsigned long ROLE_REGION = 173;
+
+  /**
+   * Represents a control with a text input and a popup with a set of predefined
+   * choices. It is used for ARIA's combobox role. See also ROLE_COMBOBOX.
+   */
+  const unsigned long ROLE_EDITCOMBOBOX = 174;
 };
--- a/accessible/interfaces/nsIAccessibleStates.idl
+++ b/accessible/interfaces/nsIAccessibleStates.idl
@@ -67,10 +67,11 @@ interface nsIAccessibleStates : nsISuppo
   const unsigned long  EXT_STATE_SINGLE_LINE             = 0x00000200;  // This text object can only contain 1 line of text    
   const unsigned long  EXT_STATE_TRANSIENT               = 0x00000400;  // 
   const unsigned long  EXT_STATE_VERTICAL                = 0x00000800;  // Especially used for sliders and scrollbars  
   const unsigned long  EXT_STATE_STALE                   = 0x00001000;  // Object not dead, but not up-to-date either
   const unsigned long  EXT_STATE_ENABLED                 = 0x00002000;  // A widget that is not unavailable
   const unsigned long  EXT_STATE_SENSITIVE               = 0x00004000;  // Same as ENABLED for now
   const unsigned long  EXT_STATE_EXPANDABLE              = 0x00008000;  // If COLLAPSED or EXPANDED
   const unsigned long  EXT_STATE_PINNED                  = 0x00010000;  // Indicates object is pinned.
+  const unsigned long  EXT_STATE_CURRENT                 = 0x00020000;  // Indicates object is the current item in its container
 };
 
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -28,18 +28,17 @@ DocAccessibleParent::RecvShowEvent(const
                                    const bool& aFromUser)
 {
   if (mShutdown)
     return IPC_OK();
 
   MOZ_ASSERT(CheckDocTree());
 
   if (aData.NewTree().IsEmpty()) {
-    NS_ERROR("no children being added");
-    return IPC_FAIL_NO_REASON(this);
+    return IPC_FAIL(this, "No children being added");
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
   // XXX This should really never happen, but sometimes we fail to fire the
   // required show events.
   if (!parent) {
     NS_ERROR("adding child to unknown accessible");
@@ -153,18 +152,17 @@ DocAccessibleParent::RecvHideEvent(const
   if (mShutdown)
     return IPC_OK();
 
   MOZ_ASSERT(CheckDocTree());
 
   // We shouldn't actually need this because mAccessibles shouldn't have an
   // entry for the document itself, but it doesn't hurt to be explicit.
   if (!aRootID) {
-    NS_ERROR("trying to hide entire document?");
-    return IPC_FAIL_NO_REASON(this);
+    return IPC_FAIL(this, "Trying to hide entire document?");
   }
 
   ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
   if (!rootEntry) {
     NS_ERROR("invalid root being removed!");
     return IPC_OK();
   }
 
@@ -266,29 +264,37 @@ DocAccessibleParent::RecvStateChangeEven
     new xpcAccStateChangeEvent(type, xpcAcc, doc, node, fromUser, state, extra,
                                aEnabled);
   nsCoreUtils::DispatchAccEvent(Move(event));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
+DocAccessibleParent::RecvCaretMoveEvent(const uint64_t& aID,
+#if defined(XP_WIN)
+                                        const LayoutDeviceIntRect& aCaretRect,
+#endif // defined (XP_WIN)
+                                        const int32_t& aOffset)
 {
   if (mShutdown) {
     return IPC_OK();
   }
 
   ProxyAccessible* proxy = GetAccessible(aID);
   if (!proxy) {
     NS_ERROR("unknown caret move event target!");
     return IPC_OK();
   }
 
+#if defined(XP_WIN)
+  ProxyCaretMoveEvent(proxy, aCaretRect);
+#else
   ProxyCaretMoveEvent(proxy, aOffset);
+#endif
 
   if (!nsCoreUtils::AccEventObserversExist()) {
     return IPC_OK();
   }
 
   xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
   xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
   nsIDOMNode* node = nullptr;
@@ -333,16 +339,31 @@ DocAccessibleParent::RecvTextChangeEvent
   RefPtr<xpcAccTextChangeEvent> event =
     new xpcAccTextChangeEvent(type, xpcAcc, doc, node, aFromUser, aStart, aLen,
                               aIsInsert, aStr);
   nsCoreUtils::DispatchAccEvent(Move(event));
 
   return IPC_OK();
 }
 
+#if defined(XP_WIN)
+
+mozilla::ipc::IPCResult
+DocAccessibleParent::RecvSyncTextChangeEvent(const uint64_t& aID,
+                                             const nsString& aStr,
+                                             const int32_t& aStart,
+                                             const uint32_t& aLen,
+                                             const bool& aIsInsert,
+                                             const bool& aFromUser)
+{
+  return RecvTextChangeEvent(aID, aStr, aStart, aLen, aIsInsert, aFromUser);
+}
+
+#endif // defined(XP_WIN)
+
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvSelectionEvent(const uint64_t& aID,
                                         const uint64_t& aWidgetID,
                                         const uint32_t& aType)
 {
   if (mShutdown) {
     return IPC_OK();
   }
@@ -369,19 +390,18 @@ DocAccessibleParent::RecvSelectionEvent(
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvRoleChangedEvent(const uint32_t& aRole)
 {
   if (mShutdown) {
     return IPC_OK();
   }
 
- if (aRole >= roles::LAST_ROLE) {
-   NS_ERROR("child sent bad role in RoleChangedEvent");
-   return IPC_FAIL_NO_REASON(this);
+ if (aRole > roles::LAST_ROLE) {
+   return IPC_FAIL(this, "Child sent bad role in RoleChangedEvent");
  }
 
  mRole = static_cast<a11y::role>(aRole);
  return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
@@ -431,16 +451,21 @@ DocAccessibleParent::AddChildDoc(DocAcce
   // OuterDocAccessibles are expected to only have a document as a child.
   // However for compatibility we tolerate replacing one document with another
   // here.
   if (outerDoc->ChildrenCount() > 1 ||
       (outerDoc->ChildrenCount() == 1 && !outerDoc->ChildAt(0)->IsDoc())) {
     return IPC_FAIL(this, "binding to proxy that can't be a outerDoc!");
   }
 
+  if (outerDoc->ChildrenCount() == 1) {
+    MOZ_ASSERT(outerDoc->ChildAt(0)->AsDoc());
+    outerDoc->ChildAt(0)->AsDoc()->Unbind();
+  }
+
   aChildDoc->SetParent(outerDoc);
   outerDoc->SetChildDoc(aChildDoc);
   mChildDocs.AppendElement(aChildDoc->mActorID);
   aChildDoc->mParentDoc = mActorID;
 
   if (aCreating) {
     ProxyCreated(aChildDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
   }
@@ -593,36 +618,41 @@ DocAccessibleParent::MaybeInitWindowEmul
     nsIntRect rootRect = rootDocument->Bounds();
     rect.x = rootRect.x - rect.x;
     rect.y -= rootRect.y;
 
     auto tab = static_cast<dom::TabParent*>(Manager());
     tab->GetDocShellIsActive(&isActive);
   }
 
-  IAccessibleHolder hWndAccHolder;
-  HWND parentWnd = reinterpret_cast<HWND>(rootDocument->GetNativeWindow());
-  HWND hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
-                                             parentWnd, rect.x, rect.y,
-                                             rect.width, rect.height,
-                                             isActive);
-  if (hWnd) {
-    // Attach accessible document to the emulated native window
-    ::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
-    SetEmulatedWindowHandle(hWnd);
+  nsWinUtils::NativeWindowCreateProc onCreate([this](HWND aHwnd) -> void {
+    IAccessibleHolder hWndAccHolder;
+
+    ::SetPropW(aHwnd, kPropNameDocAccParent, reinterpret_cast<HANDLE>(this));
+
+    SetEmulatedWindowHandle(aHwnd);
+
     IAccessible* rawHWNDAcc = nullptr;
-    if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
+    if (SUCCEEDED(::AccessibleObjectFromWindow(aHwnd, OBJID_WINDOW,
                                                IID_IAccessible,
                                                (void**)&rawHWNDAcc))) {
       hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
     }
-  }
+
+    Unused << SendEmulatedWindow(reinterpret_cast<uintptr_t>(mEmulatedWindowHandle),
+                                 hWndAccHolder);
+  });
 
-  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,
+                                                        isActive, &onCreate);
+  MOZ_ASSERT(hWnd);
 }
 
 /**
  * @param aCOMProxy COM Proxy to the document in the content process.
  */
 void
 DocAccessibleParent::SendParentCOMProxy()
 {
@@ -639,17 +669,23 @@ DocAccessibleParent::SendParentCOMProxy(
   }
 
   IAccessible* rawNative = nullptr;
   outerDoc->GetNativeInterface((void**) &rawNative);
   MOZ_ASSERT(rawNative);
 
   IAccessibleHolder::COMPtrType ptr(rawNative);
   IAccessibleHolder holder(Move(ptr));
-  Unused << PDocAccessibleParent::SendParentCOMProxy(holder);
+  if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
+    return;
+  }
+
+#if defined(MOZ_CONTENT_SANDBOX)
+  mParentProxyStream = Move(holder.GetPreservedStream());
+#endif // defined(MOZ_CONTENT_SANDBOX)
 }
 
 void
 DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle)
 {
   if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
     ::DestroyWindow(mEmulatedWindowHandle);
   }
@@ -683,12 +719,43 @@ DocAccessibleParent::RecvGetWindowedPlug
   aPluginCOMProxy->Set(IAccessibleHolder::COMPtrType(rawAccPlugin));
 
   return IPC_OK();
 #else
   return IPC_FAIL(this, "Message unsupported in this build configuration");
 #endif
 }
 
+mozilla::ipc::IPCResult
+DocAccessibleParent::RecvFocusEvent(const uint64_t& aID,
+                                    const LayoutDeviceIntRect& aCaretRect)
+{
+  if (mShutdown) {
+    return IPC_OK();
+  }
+
+  ProxyAccessible* proxy = GetAccessible(aID);
+  if (!proxy) {
+    NS_ERROR("no proxy for event!");
+    return IPC_OK();
+  }
+
+  ProxyFocusEvent(proxy, aCaretRect);
+
+  if (!nsCoreUtils::AccEventObserversExist()) {
+    return IPC_OK();
+  }
+
+  xpcAccessibleGeneric* xpcAcc = GetXPCAccessible(proxy);
+  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+  nsIDOMNode* node = nullptr;
+  bool fromUser = true; // XXX fix me
+  RefPtr<xpcAccEvent> event = new xpcAccEvent(nsIAccessibleEvent::EVENT_FOCUS,
+                                              xpcAcc, doc, node, fromUser);
+  nsCoreUtils::DispatchAccEvent(Move(event));
+
+  return IPC_OK();
+}
+
 #endif // defined(XP_WIN)
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -75,24 +75,37 @@ public:
   virtual mozilla::ipc::IPCResult RecvShowEvent(const ShowEventData& aData, const bool& aFromUser)
     override;
   virtual mozilla::ipc::IPCResult RecvHideEvent(const uint64_t& aRootID, const bool& aFromUser)
     override;
   virtual mozilla::ipc::IPCResult RecvStateChangeEvent(const uint64_t& aID,
                                                        const uint64_t& aState,
                                                        const bool& aEnabled) override final;
 
-  virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
-    override final;
+  virtual mozilla::ipc::IPCResult RecvCaretMoveEvent(const uint64_t& aID,
+#if defined(XP_WIN)
+                                                     const LayoutDeviceIntRect& aCaretRect,
+#endif
+                                                     const int32_t& aOffset) override final;
 
   virtual mozilla::ipc::IPCResult RecvTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                                                       const int32_t& aStart, const uint32_t& aLen,
                                                       const bool& aIsInsert,
                                                       const bool& aFromUser) override;
 
+#if defined(XP_WIN)
+  virtual mozilla::ipc::IPCResult RecvSyncTextChangeEvent(const uint64_t& aID, const nsString& aStr,
+                                                          const int32_t& aStart, const uint32_t& aLen,
+                                                          const bool& aIsInsert,
+                                                          const bool& aFromUser) override;
+
+  virtual mozilla::ipc::IPCResult RecvFocusEvent(const uint64_t& aID,
+                                                 const LayoutDeviceIntRect& aCaretRect) override;
+#endif // defined(XP_WIN)
+
   virtual mozilla::ipc::IPCResult RecvSelectionEvent(const uint64_t& aID,
                                                      const uint64_t& aWidgetID,
                                                      const uint32_t& aType) override;
 
   virtual mozilla::ipc::IPCResult RecvRoleChangedEvent(const uint32_t& aRole) override final;
 
   virtual mozilla::ipc::IPCResult RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
 
@@ -137,17 +150,16 @@ public:
     ProxyAccessible* parent = aChildDoc->Parent();
     MOZ_ASSERT(parent);
     if (parent) {
       aChildDoc->Parent()->ClearChildDoc(aChildDoc);
     }
     DebugOnly<bool> result = mChildDocs.RemoveElement(aChildDoc->mActorID);
     aChildDoc->mParentDoc = kNoParentDoc;
     MOZ_ASSERT(result);
-    MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_DIAGNOSTIC_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
     mAccessibles.RemoveEntry(aAccessible->ID());
   }
 
@@ -219,17 +231,21 @@ private:
   xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
 
   nsTArray<uint64_t> mChildDocs;
   uint64_t mParentDoc;
 
 #if defined(XP_WIN)
   // The handle associated with the emulated window that contains this document
   HWND mEmulatedWindowHandle;
-#endif
+
+#if defined(MOZ_CONTENT_SANDBOX)
+  mscom::PreservedStreamPtr mParentProxyStream;
+#endif // defined(MOZ_CONTENT_SANDBOX)
+#endif // defined(XP_WIN)
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
   nsTHashtable<ProxyEntry> mAccessibles;
   uint64_t mActorID;
   bool mTopLevel;
--- a/accessible/ipc/IPCTypes.h
+++ b/accessible/ipc/IPCTypes.h
@@ -20,30 +20,33 @@
 #endif // !defined(COM_NO_WINDOWS_H)
 
 // COM headers pull in MSXML which conflicts with our own XMLDocument class.
 // This define excludes those conflicting definitions.
 #if !defined(__XMLDocument_FWD_DEFINED__)
 #define __XMLDocument_FWD_DEFINED__
 #endif // !defined(__XMLDocument_FWD_DEFINED__)
 
+#include <combaseapi.h>
+
 #include "mozilla/a11y/COMPtrTypes.h"
 
 // This define in rpcndr.h messes up our code, so we must undefine it after
 // COMPtrTypes.h has been included.
 #if defined(small)
 #undef small
 #endif // defined(small)
 
 #else
 
 namespace mozilla {
 namespace a11y {
 
 typedef uint32_t IAccessibleHolder;
+typedef uint32_t IHandlerControlHolder;
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // defined(XP_WIN) && defined(ACCESSIBILITY)
 
 #endif // mozilla_a11y_IPCTypes_h
 
--- a/accessible/ipc/ProxyAccessibleBase.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -49,25 +49,19 @@ ProxyAccessibleBase<Derived>::Shutdown()
   ProxyDestroyed(static_cast<Derived*>(this));
   mDoc->RemoveAccessible(static_cast<Derived*>(this));
 }
 
 template <class Derived>
 void
 ProxyAccessibleBase<Derived>::SetChildDoc(DocAccessibleParent* aChildDoc)
 {
-  // DocAccessibleParent::AddChildDoc tolerates replacing one document with
-  // another. We must reflect that here.
   MOZ_ASSERT(aChildDoc);
-  MOZ_ASSERT(mChildren.Length() <= 1);
-  if (mChildren.IsEmpty()) {
-    mChildren.AppendElement(aChildDoc);
-  } else {
-    mChildren.ReplaceElementAt(0, aChildDoc);
-  }
+  MOZ_ASSERT(mChildren.Length() == 0);
+  mChildren.AppendElement(aChildDoc);
   mOuterDoc = true;
 }
 
 template <class Derived>
 void
 ProxyAccessibleBase<Derived>::ClearChildDoc(DocAccessibleParent* aChildDoc)
 {
   MOZ_ASSERT(aChildDoc);
--- a/accessible/ipc/other/DocAccessibleChild.cpp
+++ b/accessible/ipc/other/DocAccessibleChild.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "DocAccessibleChild.h"
 
+#include "nsAccessibilityService.h"
 #include "Accessible-inl.h"
 #include "ProxyAccessible.h"
 #include "Relation.h"
 #include "HyperTextAccessible-inl.h"
 #include "TextLeafAccessible.h"
 #include "ImageAccessible.h"
 #include "TableAccessible.h"
 #include "TableCellAccessible.h"
@@ -1998,10 +1999,17 @@ DocAccessibleChild::RecvDOMNodeID(const 
   nsIAtom* id = content->GetID();
   if (id) {
     id->ToString(*aDOMNodeID);
   }
 
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+DocAccessibleChild::RecvRestoreFocus()
+{
+  FocusMgr()->ForceFocusEvent();
+  return IPC_OK();
+}
+
 }
 }
--- a/accessible/ipc/other/DocAccessibleChild.h
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -21,27 +21,30 @@ class TableCellAccessible;
 
 /*
  * These objects handle content side communication for an accessible document,
  * and their lifetime is the same as the document they represent.
  */
 class DocAccessibleChild : public DocAccessibleChildBase
 {
 public:
-  explicit DocAccessibleChild(DocAccessible* aDoc)
+  DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
     : DocAccessibleChildBase(aDoc)
   {
     MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
+    SetManager(aManager);
   }
 
   ~DocAccessibleChild()
   {
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   }
 
+  virtual mozilla::ipc::IPCResult RecvRestoreFocus() override;
+
   /*
    * Return the state for the accessible with given ID.
    */
   virtual mozilla::ipc::IPCResult RecvState(const uint64_t& aID, uint64_t* aState) override;
 
   /*
    * Return the native state for the accessible with given ID.
    */
--- a/accessible/ipc/other/PDocAccessible.ipdl
+++ b/accessible/ipc/other/PDocAccessible.ipdl
@@ -68,16 +68,22 @@ parent:
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
 child:
   async __delete__();
 
+  /*
+   * Called as a result of focus shifting from chrome to content
+   * elements through keyboard navigation.
+   */
+  async RestoreFocus();
+
   // Accessible
   nested(inside_sync) sync State(uint64_t aID) returns(uint64_t states);
   nested(inside_sync) sync NativeState(uint64_t aID) returns(uint64_t states);
   nested(inside_sync) sync Name(uint64_t aID) returns(nsString name);
   nested(inside_sync) sync Value(uint64_t aID) returns(nsString value);
   nested(inside_sync) sync Help(uint64_t aID) returns(nsString help);
   nested(inside_sync) sync Description(uint64_t aID) returns(nsString desc);
   nested(inside_sync) sync Attributes(uint64_t aID) returns(Attribute[] attributes);
--- a/accessible/ipc/other/ProxyAccessible.cpp
+++ b/accessible/ipc/other/ProxyAccessible.cpp
@@ -834,17 +834,17 @@ ProxyAccessible::GetSelectedItem(uint32_
 
 bool
 ProxyAccessible::IsItemSelected(uint32_t aIndex)
 {
   bool selected = false;
   Unused << mDoc->SendIsItemSelected(mID, aIndex, &selected);
   return selected;
 }
- 
+
 bool
 ProxyAccessible::AddItemToSelection(uint32_t aIndex)
 {
   bool success = false;
   Unused << mDoc->SendAddItemToSelection(mID, aIndex, &success);
   return success;
 }
 
--- a/accessible/ipc/win/COMPtrTypes.cpp
+++ b/accessible/ipc/win/COMPtrTypes.cpp
@@ -1,50 +1,78 @@
 /* -*- 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/COMPtrTypes.h"
 
+#include "Accessible2_3.h"
 #include "MainThreadUtils.h"
 #include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/Platform.h"
+#include "mozilla/a11y/HandlerProvider.h"
 #include "mozilla/Move.h"
 #include "mozilla/mscom/MainThreadHandoff.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/RefPtr.h"
+#include "nsXULAppAPI.h"
 
 using mozilla::mscom::MainThreadHandoff;
+using mozilla::mscom::ProxyUniquePtr;
 using mozilla::mscom::STAUniquePtr;
 
 namespace mozilla {
 namespace a11y {
 
 IAccessibleHolder
 CreateHolderFromAccessible(Accessible* aAccToWrap)
 {
   MOZ_ASSERT(aAccToWrap && NS_IsMainThread());
   if (!aAccToWrap) {
     return nullptr;
   }
 
-  IAccessible* rawNative = nullptr;
-  aAccToWrap->GetNativeInterface((void**)&rawNative);
-  MOZ_ASSERT(rawNative);
-  if (!rawNative) {
+  STAUniquePtr<IAccessible> iaToProxy;
+  aAccToWrap->GetNativeInterface(mscom::getter_AddRefs(iaToProxy));
+  MOZ_ASSERT(iaToProxy);
+  if (!iaToProxy) {
     return nullptr;
   }
 
-  STAUniquePtr<IAccessible> iaToProxy(rawNative);
+  static const bool useHandler =
+    Preferences::GetBool("accessibility.handler.enabled", false) &&
+    IsHandlerRegistered();
 
-  IAccessible* rawIntercepted = nullptr;
-  HRESULT hr = MainThreadHandoff::WrapInterface(Move(iaToProxy), &rawIntercepted);
+  RefPtr<HandlerProvider> payload;
+  if (useHandler) {
+    payload = new HandlerProvider(IID_IAccessible,
+                                  mscom::ToInterceptorTargetPtr(iaToProxy));
+  }
+
+  ProxyUniquePtr<IAccessible> intercepted;
+  HRESULT hr = MainThreadHandoff::WrapInterface(Move(iaToProxy), payload,
+                                                (IAccessible**) mscom::getter_AddRefs(intercepted));
   MOZ_ASSERT(SUCCEEDED(hr));
   if (FAILED(hr)) {
     return nullptr;
   }
 
-  IAccessibleHolder::COMPtrType iaIntercepted(rawIntercepted);
-  return IAccessibleHolder(Move(iaIntercepted));
+  return IAccessibleHolder(Move(intercepted));
+}
+
+IHandlerControlHolder
+CreateHolderFromHandlerControl(mscom::ProxyUniquePtr<IHandlerControl> aHandlerControl)
+{
+  MOZ_ASSERT(aHandlerControl);
+  MOZ_ASSERT(XRE_IsContentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!aHandlerControl) {
+    return nullptr;
+  }
+
+  return IHandlerControlHolder(Move(aHandlerControl));
 }
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/ipc/win/COMPtrTypes.h
+++ b/accessible/ipc/win/COMPtrTypes.h
@@ -2,26 +2,32 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_COMPtrTypes_h
 #define mozilla_a11y_COMPtrTypes_h
 
+#include "mozilla/a11y/AccessibleHandler.h"
 #include "mozilla/mscom/COMPtrHolder.h"
 
 #include <oleacc.h>
 
 namespace mozilla {
 namespace a11y {
 
 typedef mozilla::mscom::COMPtrHolder<IAccessible, IID_IAccessible> IAccessibleHolder;
 
 class Accessible;
 
 IAccessibleHolder
 CreateHolderFromAccessible(Accessible* aAccToWrap);
 
+typedef mozilla::mscom::COMPtrHolder<IHandlerControl, IID_IHandlerControl> IHandlerControlHolder;
+
+IHandlerControlHolder
+CreateHolderFromHandlerControl(mscom::ProxyUniquePtr<IHandlerControl> aHandlerControl);
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_COMPtrTypes_h
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -1,36 +1,39 @@
 /* -*- 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 "DocAccessibleChild.h"
 
+#include "nsAccessibilityService.h"
 #include "Accessible-inl.h"
 #include "mozilla/a11y/PlatformChild.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "RootAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 static StaticAutoPtr<PlatformChild> sPlatformChild;
 
-DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc)
+DocAccessibleChild::DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager)
   : DocAccessibleChildBase(aDoc)
   , mEmulatedWindowHandle(nullptr)
   , mIsRemoteConstructed(false)
 {
   MOZ_COUNT_CTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
   if (!sPlatformChild) {
     sPlatformChild = new PlatformChild();
     ClearOnShutdown(&sPlatformChild, ShutdownPhase::Shutdown);
   }
+
+  SetManager(aManager);
 }
 
 DocAccessibleChild::~DocAccessibleChild()
 {
   MOZ_COUNT_DTOR_INHERITED(DocAccessibleChild, DocAccessibleChildBase);
 }
 
 void
@@ -70,41 +73,43 @@ DocAccessibleChild::RecvEmulatedWindow(c
     MOZ_ASSERT(!mEmulatedWindowProxy);
     mEmulatedWindowProxy.reset(
       const_cast<IAccessibleHolder&>(aEmulatedWindowCOMProxy).Release());
   }
 
   return IPC_OK();
 }
 
+HWND
+DocAccessibleChild::GetNativeWindowHandle() const
+{
+  if (mEmulatedWindowHandle) {
+    return mEmulatedWindowHandle;
+  }
+
+  auto tab = static_cast<dom::TabChild*>(Manager());
+  MOZ_ASSERT(tab);
+  return reinterpret_cast<HWND>(tab->GetNativeWindowHandle());
+}
+
 void
 DocAccessibleChild::PushDeferredEvent(UniquePtr<DeferredEvent> aEvent)
 {
   DocAccessibleChild* topLevelIPCDoc = nullptr;
 
   if (mDoc && mDoc->IsRoot()) {
     topLevelIPCDoc = this;
   } else {
     auto tabChild = static_cast<dom::TabChild*>(Manager());
     if (!tabChild) {
       return;
     }
 
-    nsTArray<PDocAccessibleChild*> ipcDocAccs;
-    tabChild->ManagedPDocAccessibleChild(ipcDocAccs);
-
-    // Look for the top-level DocAccessibleChild - there will only be one
-    // per TabChild.
-    for (uint32_t i = 0, l = ipcDocAccs.Length(); i < l; ++i) {
-      auto ipcDocAcc = static_cast<DocAccessibleChild*>(ipcDocAccs[i]);
-      if (ipcDocAcc->mDoc && ipcDocAcc->mDoc->IsRoot()) {
-        topLevelIPCDoc = ipcDocAcc;
-        break;
-      }
-    }
+    topLevelIPCDoc =
+      static_cast<DocAccessibleChild*>(tabChild->GetTopLevelDocAccessibleChild());
   }
 
   if (topLevelIPCDoc) {
     topLevelIPCDoc->mDeferredEvents.AppendElement(Move(aEvent));
   }
 }
 
 bool
@@ -150,37 +155,93 @@ DocAccessibleChild::SendStateChangeEvent
     return PDocAccessibleChild::SendStateChangeEvent(aID, aState, aEnabled);
   }
 
   PushDeferredEvent(MakeUnique<SerializedStateChange>(this, aID, aState,
                                                       aEnabled));
   return true;
 }
 
+LayoutDeviceIntRect
+DocAccessibleChild::GetCaretRectFor(const uint64_t& aID)
+{
+  Accessible* target;
+
+  if (aID) {
+    target = reinterpret_cast<Accessible*>(aID);
+  } else {
+    target = mDoc;
+  }
+
+  MOZ_ASSERT(target);
+
+  HyperTextAccessible* text = target->AsHyperText();
+  if (!text) {
+    return LayoutDeviceIntRect();
+  }
+
+  nsIWidget* widget = nullptr;
+  return text->GetCaretRect(&widget);
+}
+
+bool
+DocAccessibleChild::SendFocusEvent(const uint64_t& aID)
+{
+  return SendFocusEvent(aID, GetCaretRectFor(aID));
+}
+
+bool
+DocAccessibleChild::SendFocusEvent(const uint64_t& aID,
+                                   const LayoutDeviceIntRect& aCaretRect)
+{
+  if (IsConstructedInParentProcess()) {
+    return PDocAccessibleChild::SendFocusEvent(aID, aCaretRect);
+  }
+
+  PushDeferredEvent(MakeUnique<SerializedFocus>(this, aID, aCaretRect));
+  return true;
+}
+
 bool
 DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
                                        const int32_t& aOffset)
 {
+  return SendCaretMoveEvent(aID, GetCaretRectFor(aID), aOffset);
+}
+
+bool
+DocAccessibleChild::SendCaretMoveEvent(const uint64_t& aID,
+                                       const LayoutDeviceIntRect& aCaretRect,
+                                       const int32_t& aOffset)
+{
   if (IsConstructedInParentProcess()) {
-    return PDocAccessibleChild::SendCaretMoveEvent(aID, aOffset);
+    return PDocAccessibleChild::SendCaretMoveEvent(aID, aCaretRect, aOffset);
   }
 
-  PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aOffset));
+  PushDeferredEvent(MakeUnique<SerializedCaretMove>(this, aID, aCaretRect,
+                                                    aOffset));
   return true;
 }
 
 bool
 DocAccessibleChild::SendTextChangeEvent(const uint64_t& aID,
                                         const nsString& aStr,
                                         const int32_t& aStart,
                                         const uint32_t& aLen,
                                         const bool& aIsInsert,
                                         const bool& aFromUser)
 {
   if (IsConstructedInParentProcess()) {
+    if (aStr.Contains(L'\xfffc')) {
+      // The AT is going to need to reenter content while the event is being
+      // dispatched synchronously.
+      return PDocAccessibleChild::SendSyncTextChangeEvent(aID, aStr, aStart,
+                                                          aLen, aIsInsert,
+                                                          aFromUser);
+    }
     return PDocAccessibleChild::SendTextChangeEvent(aID, aStr, aStart,
                                                     aLen, aIsInsert, aFromUser);
   }
 
   PushDeferredEvent(MakeUnique<SerializedTextChange>(this, aID, aStr, aStart,
                                                      aLen, aIsInsert, aFromUser));
   return true;
 }
@@ -242,11 +303,18 @@ DocAccessibleChild::SendBindChildDoc(Doc
     return DocAccessibleChildBase::SendBindChildDoc(aChildDoc, aNewParentID);
   }
 
   PushDeferredEvent(MakeUnique<SerializedBindChildDoc>(this, aChildDoc,
                                                        aNewParentID));
   return true;
 }
 
+ipc::IPCResult
+DocAccessibleChild::RecvRestoreFocus()
+{
+  FocusMgr()->ForceFocusEvent();
+  return IPC_OK();
+}
+
 } // namespace a11y
 } // namespace mozilla
 
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -17,37 +17,45 @@ namespace a11y {
 
 /*
  * These objects handle content side communication for an accessible document,
  * and their lifetime is the same as the document they represent.
  */
 class DocAccessibleChild : public DocAccessibleChildBase
 {
 public:
-  explicit DocAccessibleChild(DocAccessible* aDoc);
+  DocAccessibleChild(DocAccessible* aDoc, IProtocol* aManager);
   ~DocAccessibleChild();
 
   virtual void Shutdown() override;
 
   virtual ipc::IPCResult
-  RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy) override;
+    RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy) override;
   virtual ipc::IPCResult
     RecvEmulatedWindow(const WindowsHandle& aEmulatedWindowHandle,
                        const IAccessibleHolder& aEmulatedWindowCOMProxy) override;
+  virtual ipc::IPCResult
+    RecvRestoreFocus() override;
 
-  HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
+  HWND GetNativeWindowHandle() const;
   IAccessible* GetEmulatedWindowIAccessible() const { return mEmulatedWindowProxy.get(); }
 
   IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
 
   bool SendEvent(const uint64_t& aID, const uint32_t& type);
   bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser);
   bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState,
                             const bool& aEnabled);
   bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset);
+  bool SendCaretMoveEvent(const uint64_t& aID,
+                          const LayoutDeviceIntRect& aCaretRect,
+                          const int32_t& aOffset);
+  bool SendFocusEvent(const uint64_t& aID);
+  bool SendFocusEvent(const uint64_t& aID,
+                      const LayoutDeviceIntRect& aCaretRect);
   bool SendTextChangeEvent(const uint64_t& aID, const nsString& aStr,
                            const int32_t& aStart, const uint32_t& aLen,
                            const bool& aIsInsert, const bool& aFromUser);
   bool SendSelectionEvent(const uint64_t& aID, const uint64_t& aWidgetID,
                           const uint32_t& aType);
   bool SendRoleChangedEvent(const uint32_t& aRole);
 
   bool ConstructChildDocInParentProcess(DocAccessibleChild* aNewChildDoc,
@@ -60,16 +68,18 @@ protected:
   virtual void MaybeSendShowEvent(ShowEventData& aData, bool aFromUser) override;
 
 private:
   void RemoveDeferredConstructor();
 
   bool IsConstructedInParentProcess() const { return mIsRemoteConstructed; }
   void SetConstructedInParentProcess() { mIsRemoteConstructed = true; }
 
+  LayoutDeviceIntRect GetCaretRectFor(const uint64_t& aID);
+
   /**
    * DocAccessibleChild should not fire events until it has asynchronously
    * received the COM proxy for its parent. OTOH, content a11y may still be
    * attempting to fire events during this window of time. If this object does
    * not yet have its parent proxy, instead of immediately sending the events to
    * our parent, we enqueue them to mDeferredEvents. As soon as
    * RecvParentCOMProxy is called, we play back mDeferredEvents.
    */
@@ -154,29 +164,49 @@ private:
     uint64_t  mID;
     uint64_t  mState;
     bool      mEnabled;
   };
 
   struct SerializedCaretMove final : public DeferredEvent
   {
     SerializedCaretMove(DocAccessibleChild* aTarget, uint64_t aID,
-                        int32_t aOffset)
+                        const LayoutDeviceIntRect& aCaretRect, int32_t aOffset)
       : DeferredEvent(aTarget)
       , mID(aID)
+      , mCaretRect(aCaretRect)
       , mOffset(aOffset)
     {}
 
     void Dispatch(DocAccessibleChild* aIPCDoc) override
     {
-      Unused << aIPCDoc->SendCaretMoveEvent(mID, mOffset);
+      Unused << aIPCDoc->SendCaretMoveEvent(mID, mCaretRect, mOffset);
     }
 
-    uint64_t  mID;
-    int32_t   mOffset;
+    uint64_t            mID;
+    LayoutDeviceIntRect mCaretRect;
+    int32_t             mOffset;
+  };
+
+  struct SerializedFocus final : public DeferredEvent
+  {
+    SerializedFocus(DocAccessibleChild* aTarget, uint64_t aID,
+                    const LayoutDeviceIntRect& aCaretRect)
+      : DeferredEvent(aTarget)
+      , mID(aID)
+      , mCaretRect(aCaretRect)
+    {}
+
+    void Dispatch(DocAccessibleChild* aIPCDoc) override
+    {
+      Unused << aIPCDoc->SendFocusEvent(mID, mCaretRect);
+    }
+
+    uint64_t            mID;
+    LayoutDeviceIntRect mCaretRect;
   };
 
   struct SerializedTextChange final : public DeferredEvent
   {
     SerializedTextChange(DocAccessibleChild* aTarget, uint64_t aID,
                          const nsString& aStr, int32_t aStart, uint32_t aLen,
                          bool aIsInsert, bool aFromUser)
       : DeferredEvent(aTarget)
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -0,0 +1,269 @@
+/* -*- 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/. */
+
+#define INITGUID
+
+#include "mozilla/a11y/HandlerProvider.h"
+
+#include "Accessible2_3.h"
+#include "HandlerData.h"
+#include "HandlerData_i.c"
+#include "mozilla/Assertions.h"
+#include "mozilla/a11y/AccessibleWrap.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Move.h"
+#include "mozilla/mscom/AgileReference.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 <memory.h>
+
+namespace mozilla {
+namespace a11y {
+
+HandlerProvider::HandlerProvider(REFIID aIid,
+                               mscom::InterceptorTargetPtr<IUnknown> aTarget)
+  : mRefCnt(0)
+  , mMutex("mozilla::a11y::HandlerProvider::mMutex")
+  , mTargetUnkIid(aIid)
+  , mTargetUnk(Move(aTarget))
+{
+}
+
+HRESULT
+HandlerProvider::QueryInterface(REFIID riid, void** ppv)
+{
+  if (!ppv) {
+    return E_INVALIDARG;
+  }
+
+  RefPtr<IUnknown> punk;
+
+  if (riid == IID_IUnknown || riid == IID_IGeckoBackChannel) {
+    punk = static_cast<IGeckoBackChannel*>(this);
+  }
+
+  if (!punk) {
+    return E_NOINTERFACE;
+  }
+
+  punk.forget(ppv);
+  return S_OK;
+}
+
+ULONG
+HandlerProvider::AddRef()
+{
+  return ++mRefCnt;
+}
+
+ULONG
+HandlerProvider::Release()
+{
+  ULONG result = --mRefCnt;
+  if (!result) {
+    delete this;
+  }
+  return result;
+}
+
+HRESULT
+HandlerProvider::GetHandler(NotNull<CLSID*> aHandlerClsid)
+{
+  if (!IsTargetInterfaceCacheable()) {
+    return E_NOINTERFACE;
+  }
+
+  *aHandlerClsid = CLSID_AccessibleHandler;
+  return S_OK;
+}
+
+void
+HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
+{
+  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+  if (mSerializer) {
+    return;
+  }
+
+  IA2Payload payload{};
+
+  if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
+                                 this, &HandlerProvider::BuildIA2Data,
+                                 &payload.mData) ||
+      !payload.mData.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);
+}
+
+HRESULT
+HandlerProvider::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
+{
+  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+  if (!IsTargetInterfaceCacheable()) {
+    *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
+    return S_OK;
+  }
+
+  MutexAutoLock lock(mMutex);
+
+  GetAndSerializePayload(lock);
+
+  if (!mSerializer || !(*mSerializer)) {
+    // Failed payload serialization is non-fatal
+    *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
+    return S_OK;
+  }
+
+  *aOutPayloadSize = mSerializer->GetSize();
+  return S_OK;
+}
+
+void
+HandlerProvider::BuildIA2Data(IA2Data* 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()));
+
+  // 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);
+  }
+}
+
+void
+HandlerProvider::ClearIA2Data(IA2Data& aData)
+{
+  ZeroMemory(&aData, sizeof(IA2Data));
+}
+
+bool
+HandlerProvider::IsTargetInterfaceCacheable()
+{
+  return MarshalAs(mTargetUnkIid) == NEWEST_IA2_IID;
+}
+
+HRESULT
+HandlerProvider::WriteHandlerPayload(NotNull<IStream*> aStream)
+{
+  MutexAutoLock lock(mMutex);
+
+  if (!mSerializer || !(*mSerializer)) {
+    // Failed payload serialization is non-fatal
+    mscom::StructToStream emptyStruct;
+    return emptyStruct.Write(aStream);
+  }
+
+  HRESULT hr = mSerializer->Write(aStream);
+
+  mSerializer.reset();
+
+  return hr;
+}
+
+REFIID
+HandlerProvider::MarshalAs(REFIID aIid)
+{
+  static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+                "You have modified NEWEST_IA2_IID. This code needs updating.");
+  if (aIid == IID_IDispatch || aIid == IID_IAccessible ||
+      aIid == IID_IAccessible2 || aIid == IID_IAccessible2_2 ||
+      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;
+}
+
+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;
+}
+
+void
+HandlerProvider::SetHandlerControlOnMainThread(DWORD aPid,
+                                              mscom::ProxyUniquePtr<IHandlerControl> aCtrl)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto content = dom::ContentChild::GetSingleton();
+  MOZ_ASSERT(content);
+
+  IHandlerControlHolder holder(CreateHolderFromHandlerControl(Move(aCtrl)));
+  Unused << content->SendA11yHandlerControl(aPid, holder);
+}
+
+HRESULT
+HandlerProvider::put_HandlerControl(long aPid, IHandlerControl* aCtrl)
+{
+  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+  if (!aCtrl) {
+    return E_INVALIDARG;
+  }
+
+  auto ptrProxy = mscom::ToProxyUniquePtr(aCtrl);
+
+  if (!mscom::InvokeOnMainThread("HandlerProvider::SetHandlerControlOnMainThread",
+                                 this,
+                                 &HandlerProvider::SetHandlerControlOnMainThread,
+                                 static_cast<DWORD>(aPid), Move(ptrProxy))) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+HRESULT
+HandlerProvider::Refresh(IA2Data* aOutData)
+{
+  MOZ_ASSERT(mscom::IsCurrentThreadMTA());
+
+  if (!mscom::InvokeOnMainThread("HandlerProvider::BuildIA2Data",
+                                 this, &HandlerProvider::BuildIA2Data,
+                                 aOutData)) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+} // namespace a11y
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -0,0 +1,75 @@
+/* -*- 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_HandlerProvider_h
+#define mozilla_a11y_HandlerProvider_h
+
+#include "handler/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"
+
+struct NEWEST_IA2_INTERFACE;
+
+namespace mozilla {
+
+namespace mscom {
+
+class StructToStream;
+
+} // namespace mscom
+
+namespace a11y {
+
+class HandlerProvider final : public IGeckoBackChannel
+                            , public mscom::IHandlerProvider
+{
+public:
+  HandlerProvider(REFIID aIid, mscom::InterceptorTargetPtr<IUnknown> aTarget);
+
+  // 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_(REFIID) MarshalAs(REFIID aIid) 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;
+
+private:
+  ~HandlerProvider() = default;
+
+  void SetHandlerControlOnMainThread(DWORD aPid,
+                                     mscom::ProxyUniquePtr<IHandlerControl> aCtrl);
+  void GetAndSerializePayload(const MutexAutoLock&);
+  void BuildIA2Data(IA2Data* aOutIA2Data);
+  static void ClearIA2Data(IA2Data& aData);
+  bool IsTargetInterfaceCacheable();
+
+  Atomic<uint32_t>                  mRefCnt;
+  Mutex                             mMutex; // Protects mSerializer
+  const IID                         mTargetUnkIid;
+  mscom::InterceptorTargetPtr<IUnknown> mTargetUnk; // Constant, main thread only
+  UniquePtr<mscom::StructToStream>  mSerializer;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_HandlerProvider_h
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/IAccessible32.manifest
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="Mozilla.Firefox.xul" version="1.0.0.0" />
+  <file name="xul.dll" />
+  <comInterfaceExternalProxyStub
+      iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+      proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
+      name="IAccessible"
+      tlbid="{1EA4DBF0-3C3B-11CF-810C-00AA00389B71}"
+  />
+</assembly>
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/IAccessible64.manifest
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <assemblyIdentity type="win32" name="Mozilla.Firefox.xul" version="1.0.0.0" />
+  <file name="xul.dll" />
+  <comInterfaceExternalProxyStub
+      iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+      proxyStubClsid32="{03022430-ABC4-11D0-BDE2-00AA001A1953}"
+      name="IAccessible"
+  />
+</assembly>
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -4,16 +4,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/. */
 
 include protocol PFileDescriptorSet;
 include protocol PBrowser;
 
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   int32_t MsaaID;
@@ -45,33 +46,42 @@ parent:
   /*
    * Notify the parent process the document in the child process is firing an
    * event.
    */
   async Event(uint64_t aID, uint32_t type);
   async ShowEvent(ShowEventData data, bool aFromUser);
   async HideEvent(uint64_t aRootID, bool aFromUser);
   async StateChangeEvent(uint64_t aID, uint64_t aState, bool aEnabled);
-  async CaretMoveEvent(uint64_t aID, int32_t aOffset);
+  async CaretMoveEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect,
+                       int32_t aOffset);
   async TextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart, uint32_t aLen,
                         bool aIsInsert, bool aFromUser);
+  sync SyncTextChangeEvent(uint64_t aID, nsString aStr, int32_t aStart,
+                           uint32_t aLen, bool aIsInsert, bool aFromUser);
   async SelectionEvent(uint64_t aID, uint64_t aWidgetID, uint32_t aType);
   async RoleChangedEvent(uint32_t aRole);
+  async FocusEvent(uint64_t aID, LayoutDeviceIntRect aCaretRect);
 
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   sync GetWindowedPluginIAccessible(WindowsHandle aHwnd)
     returns (IAccessibleHolder aPluginCOMProxy);
 
 child:
   async ParentCOMProxy(IAccessibleHolder aParentCOMProxy);
   async EmulatedWindow(WindowsHandle aEmulatedWindowHandle,
                        IAccessibleHolder aEmulatedWindowCOMProxy);
+  /*
+   * Called as a result of focus shifting from chrome to content
+   * elements through keyboard navigation.
+   */
+  async RestoreFocus();
 
   async __delete__();
 };
 
 }
 }
--- a/accessible/ipc/win/PlatformChild.cpp
+++ b/accessible/ipc/win/PlatformChild.cpp
@@ -1,14 +1,15 @@
 /* -*- 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/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 "AccessibleTableCell.h"
@@ -43,16 +44,24 @@ static const mozilla::mscom::ArrayData s
 // we intend to instantiate them. Therefore RegisterProxy() must be called
 // via EnsureMTA.
 PlatformChild::PlatformChild()
   : 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 {
+    tmpActCtxMTA.reset(new mozilla::mscom::ActivationContextRegion(actCtxResourceId));
+  });
+  mActCtxMTA = Move(tmpActCtxMTA);
+
   mozilla::mscom::InterceptorLog::Init();
   mozilla::mscom::RegisterArrayData(sPlatformChildArrayData);
 
 
   UniquePtr<mozilla::mscom::RegisteredProxy> customProxy;
   mozilla::mscom::EnsureMTA([&customProxy]() -> void {
     customProxy = Move(mozilla::mscom::RegisterProxy());
   });
--- a/accessible/ipc/win/PlatformChild.h
+++ b/accessible/ipc/win/PlatformChild.h
@@ -2,32 +2,35 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_PlatformChild_h
 #define mozilla_a11y_PlatformChild_h
 
+#include "mozilla/mscom/ActivationContext.h"
+#include "mozilla/mscom/Ptr.h"
 #include "mozilla/mscom/Registration.h"
 
 namespace mozilla {
 namespace a11y {
 
 class PlatformChild
 {
 public:
   PlatformChild();
 
   PlatformChild(PlatformChild&) = delete;
   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> mAccTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
   UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
 };
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.cpp
@@ -0,0 +1,1010 @@
+/* -*- 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)
+
+#define INITGUID
+
+#include "AccessibleHandler.h"
+#include "AccessibleHandlerControl.h"
+#include "AccessibleTextTearoff.h"
+
+#include "Factory.h"
+#include "HandlerData.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/UniquePtr.h"
+
+#include <objbase.h>
+#include <uiautomation.h>
+#include <winreg.h>
+
+#include "AccessibleHypertext.h"
+#include "Accessible2_i.c"
+#include "Accessible2_2_i.c"
+#include "Accessible2_3_i.c"
+
+namespace mozilla {
+namespace a11y {
+
+static mscom::Factory<AccessibleHandler> sHandlerFactory;
+
+HRESULT
+AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface)
+{
+  if (!aOutInterface || !aOuter || aIid != IID_IUnknown) {
+    return E_INVALIDARG;
+  }
+
+  *aOutInterface = nullptr;
+
+  HRESULT hr;
+  RefPtr<AccessibleHandler> handler(new AccessibleHandler(aOuter, &hr));
+  if (!handler) {
+    return E_OUTOFMEMORY;
+  }
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return handler->InternalQueryInterface(aIid, aOutInterface);
+}
+
+AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
+  : mscom::Handler(aOuter, aResult)
+  , mDispatch(nullptr)
+  , mIA2PassThru(nullptr)
+  , mServProvPassThru(nullptr)
+  , mCachedData()
+  , mCacheGen(0)
+{
+  RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+  MOZ_ASSERT(ctl);
+  if (!ctl) {
+    if (aResult) {
+      *aResult = E_UNEXPECTED;
+    }
+    return;
+  }
+
+  mCacheGen = ctl->GetCacheGen();
+}
+
+AccessibleHandler::~AccessibleHandler()
+{
+  if (mCachedData.mGeckoBackChannel) {
+    mCachedData.mGeckoBackChannel->Release();
+  }
+}
+
+HRESULT
+AccessibleHandler::ResolveIA2()
+{
+  if (mIA2PassThru) {
+    return S_OK;
+  }
+
+  RefPtr<IUnknown> proxy(GetProxy());
+  if (!proxy) {
+    return E_UNEXPECTED;
+  }
+
+  HRESULT hr = proxy->QueryInterface(NEWEST_IA2_IID,
+                                     reinterpret_cast<void**>(&mIA2PassThru));
+  if (SUCCEEDED(hr)) {
+    // mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
+    mIA2PassThru->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);
+}
+
+HRESULT
+AccessibleHandler::ResolveIDispatch()
+{
+  if (mDispatch) {
+    return S_OK;
+  }
+
+  HRESULT hr;
+
+  if (!mDispatchUnk) {
+    RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+    if (!ctl) {
+      return E_OUTOFMEMORY;
+    }
+
+    RefPtr<ITypeInfo> typeinfo;
+    hr = ctl->GetHandlerTypeInfo(getter_AddRefs(typeinfo));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ::CreateStdDispatch(GetOuter(), static_cast<NEWEST_IA2_INTERFACE*>(this),
+                             typeinfo, getter_AddRefs(mDispatchUnk));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = mDispatchUnk->QueryInterface(IID_IDispatch,
+                                    reinterpret_cast<void**>(&mDispatch));
+  if (SUCCEEDED(hr)) {
+    // mDispatch is weak (see comments in AccessibleHandler.h)
+    mDispatch->Release();
+  }
+
+  return hr;
+}
+
+HRESULT
+AccessibleHandler::QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
+                                         void** aOutInterface)
+{
+  MOZ_ASSERT(aProxyUnknown);
+
+  static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+                "You have modified NEWEST_IA2_IID. This code needs updating.");
+  if (aIid == IID_IDispatch || aIid == IID_IAccessible2_3 ||
+      aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2 ||
+      aIid == IID_IAccessible) {
+    RefPtr<NEWEST_IA2_INTERFACE> ia2(static_cast<NEWEST_IA2_INTERFACE*>(this));
+    ia2.forget(aOutInterface);
+    return S_OK;
+  }
+
+  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);
+    return S_OK;
+  }
+
+  if (aIid == IID_IProvideClassInfo) {
+    RefPtr<IProvideClassInfo> clsInfo(this);
+    clsInfo.forget(aOutInterface);
+    return S_OK;
+  }
+
+  return E_NOINTERFACE;
+}
+
+HRESULT
+AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid)
+{
+  if (!aStream) {
+    return E_INVALIDARG;
+  }
+
+  mscom::StructFromStream deserializer(aStream);
+  if (!deserializer) {
+    return E_FAIL;
+  }
+  if (deserializer.IsEmpty()) {
+    return S_FALSE;
+  }
+
+  if (!deserializer.Read(&mCachedData, &IA2Payload_Decode)) {
+    return E_FAIL;
+  }
+
+  if (!mCachedData.mGeckoBackChannel) {
+    return S_OK;
+  }
+
+  RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+  if (!ctl) {
+    return E_OUTOFMEMORY;
+  }
+
+  return ctl->Register(WrapNotNull(mCachedData.mGeckoBackChannel));
+}
+
+REFIID
+AccessibleHandler::MarshalAs(REFIID aIid)
+{
+  static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
+                "You have modified NEWEST_IA2_IID. This code needs updating.");
+  if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
+      aIid == IID_IAccessible2 || aIid == IID_IAccessible ||
+      aIid == IID_IDispatch) {
+    return NEWEST_IA2_IID;
+  }
+
+  return aIid;
+}
+
+HRESULT
+AccessibleHandler::GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize)
+{
+  if (!aOutPayloadSize) {
+    return E_INVALIDARG;
+  }
+
+  // If we're sending the payload to somebody else, we'd better make sure that
+  // it is up to date. If the cache update fails then we'll return a 0 payload
+  // size so that we don't transfer obsolete data.
+  if (FAILED(MaybeUpdateCachedData())) {
+    *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
+    return S_OK;
+  }
+
+  mSerializer = MakeUnique<mscom::StructToStream>(mCachedData, &IA2Payload_Encode);
+  if (!mSerializer) {
+    return E_FAIL;
+  }
+
+  *aOutPayloadSize = mSerializer->GetSize();
+  return S_OK;
+}
+
+HRESULT
+AccessibleHandler::WriteHandlerPayload(IStream* aStream, REFIID aIid)
+{
+  if (!aStream) {
+    return E_INVALIDARG;
+  }
+
+  if (!mSerializer) {
+    return E_UNEXPECTED;
+  }
+
+  HRESULT hr = mSerializer->Write(aStream);
+  mSerializer.reset();
+  return hr;
+}
+
+HRESULT
+AccessibleHandler::QueryInterface(REFIID riid, void** ppv)
+{
+  return Handler::QueryInterface(riid, ppv);
+}
+
+ULONG
+AccessibleHandler::AddRef()
+{
+  return Handler::AddRef();
+}
+
+ULONG
+AccessibleHandler::Release()
+{
+  return Handler::Release();
+}
+
+HRESULT
+AccessibleHandler::GetTypeInfoCount(UINT *pctinfo)
+{
+  HRESULT hr = ResolveIDispatch();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mDispatch->GetTypeInfoCount(pctinfo);
+}
+
+HRESULT
+AccessibleHandler::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+{
+  HRESULT hr = ResolveIDispatch();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
+}
+
+
+HRESULT
+AccessibleHandler::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
+                                 LCID lcid, DISPID *rgDispId)
+{
+  HRESULT hr = ResolveIDispatch();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
+}
+
+HRESULT
+AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
+                          WORD wFlags, DISPPARAMS *pDispParams,
+                          VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
+                          UINT *puArgErr)
+{
+  HRESULT hr = ResolveIDispatch();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
+                           pVarResult, pExcepInfo, puArgErr);
+}
+
+#define BEGIN_CACHE_ACCESS \
+  { \
+    HRESULT hr; \
+    if (FAILED(hr = MaybeUpdateCachedData())) { \
+      return hr; \
+    } \
+  }
+
+static BSTR
+CopyBSTR(BSTR aSrc)
+{
+  return SysAllocStringLen(aSrc, SysStringLen(aSrc));
+}
+
+HRESULT
+AccessibleHandler::get_accParent(IDispatch **ppdispParent)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accParent(ppdispParent);
+}
+
+HRESULT
+AccessibleHandler::get_accChildCount(long *pcountChildren)
+{
+  if (!pcountChildren) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accChildCount(pcountChildren);
+}
+
+HRESULT
+AccessibleHandler::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
+{
+  if (!ppdispChild) {
+    return E_INVALIDARG;
+  }
+  // Unlikely, but we might as well optimize for it
+  if (varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF) {
+    RefPtr<IDispatch> disp(this);
+    disp.forget(ppdispChild);
+    return S_OK;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accChild(varChild, ppdispChild);
+}
+
+HRESULT
+AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName)
+{
+  if (!pszName) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accName(varChild, pszName);
+}
+
+HRESULT
+AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue)
+{
+  if (!pszValue) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accValue(varChild, pszValue);
+}
+
+HRESULT
+AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription)
+{
+  if (!pszDescription) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accDescription(varChild, pszDescription);
+}
+
+
+HRESULT
+AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
+{
+  if (!pvarRole) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accRole(varChild, pvarRole);
+}
+
+
+HRESULT
+AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState)
+{
+  if (!pvarState) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accState(varChild, pvarState);
+}
+
+HRESULT
+AccessibleHandler::get_accHelp(VARIANT varChild, BSTR *pszHelp)
+{
+  // This matches what AccessibleWrap does
+  if (!pszHelp) {
+    return E_INVALIDARG;
+  }
+  *pszHelp = nullptr;
+  return S_FALSE;
+}
+
+HRESULT
+AccessibleHandler::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild,
+                                    long *pidTopic)
+{
+  // This matches what AccessibleWrap does
+  if (!pszHelpFile || !pidTopic) {
+    return E_INVALIDARG;
+  }
+  *pszHelpFile = nullptr;
+  *pidTopic = 0;
+  return S_FALSE;
+}
+
+HRESULT
+AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
+                                           BSTR *pszKeyboardShortcut)
+{
+  if (!pszKeyboardShortcut) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
+}
+
+HRESULT
+AccessibleHandler::get_accFocus(VARIANT *pvarChild)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accFocus(pvarChild);
+}
+
+HRESULT
+AccessibleHandler::get_accSelection(VARIANT *pvarChildren)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accSelection(pvarChildren);
+}
+
+HRESULT
+AccessibleHandler::get_accDefaultAction(VARIANT varChild,
+                                        BSTR *pszDefaultAction)
+{
+  if (!pszDefaultAction) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
+}
+
+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;
+  }
+  return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
+                                   varChild);
+}
+
+HRESULT
+AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
+                               VARIANT *pvarEndUpAt)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->accNavigate(navDir, varStart, pvarEndUpAt);
+}
+
+HRESULT
+AccessibleHandler::accHitTest(long xLeft, long yTop, VARIANT *pvarChild)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->accHitTest(xLeft, yTop, pvarChild);
+}
+
+HRESULT
+AccessibleHandler::accDoDefaultAction(VARIANT varChild)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->accDoDefaultAction(varChild);
+}
+
+HRESULT
+AccessibleHandler::put_accName(VARIANT varChild, BSTR szName)
+{
+  // This matches AccessibleWrap
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue)
+{
+  // This matches AccessibleWrap
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_nRelations(long* nRelations)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_nRelations(nRelations);
+}
+
+HRESULT
+AccessibleHandler::get_relation(long relationIndex,
+                                IAccessibleRelation** relation)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_relation(relationIndex, relation);
+}
+
+HRESULT
+AccessibleHandler::get_relations(long maxRelations,
+                                 IAccessibleRelation** relations,
+                                 long* nRelations)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_relations(maxRelations, relations, nRelations);
+}
+
+HRESULT
+AccessibleHandler::role(long* role)
+{
+  if (!role) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->role(role);
+}
+
+HRESULT
+AccessibleHandler::scrollTo(IA2ScrollType scrollType)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->scrollTo(scrollType);
+}
+
+HRESULT
+AccessibleHandler::scrollToPoint(IA2CoordinateType coordinateType, long x,
+                                 long y)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->scrollToPoint(coordinateType, x, y);
+}
+
+HRESULT
+AccessibleHandler::get_groupPosition(long* groupLevel, long* similarItemsInGroup,
+                                     long* positionInGroup)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_groupPosition(groupLevel, similarItemsInGroup,
+                                         positionInGroup);
+}
+
+HRESULT
+AccessibleHandler::get_states(AccessibleStates* states)
+{
+  if (!states) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_states(states);
+}
+
+HRESULT
+AccessibleHandler::get_extendedRole(BSTR* extendedRole)
+{
+  // This matches ia2Accessible
+  if (!extendedRole) {
+    return E_INVALIDARG;
+  }
+  *extendedRole = nullptr;
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_localizedExtendedRole(BSTR* localizedExtendedRole)
+{
+  // This matches ia2Accessible
+  if (!localizedExtendedRole) {
+    return E_INVALIDARG;
+  }
+  *localizedExtendedRole = nullptr;
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_nExtendedStates(long* nExtendedStates)
+{
+  // This matches ia2Accessible
+  if (!nExtendedStates) {
+    return E_INVALIDARG;
+  }
+  *nExtendedStates = 0;
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_extendedStates(long maxExtendedStates, BSTR** extendedStates,
+                                      long* nExtendedStates)
+{
+  // This matches ia2Accessible
+  if (!extendedStates || !nExtendedStates) {
+    return E_INVALIDARG;
+  }
+  *extendedStates = nullptr;
+  *nExtendedStates = 0;
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_localizedExtendedStates(long maxLocalizedExtendedStates,
+                                               BSTR** localizedExtendedStates,
+                                               long* nLocalizedExtendedStates)
+{
+  // This matches ia2Accessible
+  if (!localizedExtendedStates || !nLocalizedExtendedStates) {
+    return E_INVALIDARG;
+  }
+  *localizedExtendedStates = nullptr;
+  *nLocalizedExtendedStates = 0;
+  return E_NOTIMPL;
+}
+
+HRESULT
+AccessibleHandler::get_uniqueID(long* uniqueID)
+{
+  if (!uniqueID) {
+    return E_INVALIDARG;
+  }
+  if (!HasPayload()) {
+    HRESULT hr = ResolveIA2();
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return mIA2PassThru->get_uniqueID(uniqueID);
+  }
+  *uniqueID = mCachedData.mData.mUniqueId;
+  return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_windowHandle(HWND* windowHandle)
+{
+  if (!windowHandle) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_windowHandle(windowHandle);
+}
+
+HRESULT
+AccessibleHandler::get_indexInParent(long* indexInParent)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_indexInParent(indexInParent);
+}
+
+HRESULT
+AccessibleHandler::get_locale(IA2Locale* locale)
+{
+  if (!locale) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_locale(locale);
+  return S_OK;
+}
+
+HRESULT
+AccessibleHandler::get_attributes(BSTR* attributes)
+{
+  if (!attributes) {
+    return E_INVALIDARG;
+  }
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_attributes(attributes);
+}
+
+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);
+}
+
+HRESULT
+AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible,
+                                           long* caretOffset)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_accessibleWithCaret(accessible, caretOffset);
+}
+
+HRESULT
+AccessibleHandler::get_relationTargetsOfType(BSTR type, long maxTargets,
+                                             IUnknown*** targets,
+                                             long* nTargets)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
+                                                 nTargets);
+}
+
+HRESULT
+AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges)
+{
+  HRESULT hr = ResolveIA2();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return mIA2PassThru->get_selectionRanges(ranges, nRanges);
+}
+
+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 }}
+};
+
+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;
+  }
+
+  for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) {
+    if (aServiceId == kUnsupportedServices[i]) {
+      return E_NOINTERFACE;
+    }
+  }
+
+  if (!mServProvPassThru) {
+    RefPtr<IUnknown> proxy(GetProxy());
+    if (!proxy) {
+      return E_UNEXPECTED;
+    }
+
+    HRESULT hr = proxy->QueryInterface(IID_IServiceProvider,
+                                       reinterpret_cast<void**>(&mServProvPassThru));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    // mServProvPassThru is a weak reference (see comments in
+    // AccessibleHandler.h)
+    mServProvPassThru->Release();
+  }
+
+  return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
+}
+
+HRESULT
+AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo)
+{
+  RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
+  if (!ctl) {
+    return E_OUTOFMEMORY;
+  }
+
+  return ctl->GetHandlerTypeInfo(aOutTypeInfo);
+}
+
+} // namespace a11y
+} // namespace mozilla
+
+extern "C" HRESULT __stdcall
+ProxyDllCanUnloadNow();
+
+extern "C" HRESULT __stdcall
+DllCanUnloadNow()
+{
+  return mozilla::mscom::Module::CanUnload() && ProxyDllCanUnloadNow();
+}
+
+extern "C" HRESULT __stdcall
+ProxyDllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface);
+
+extern "C" HRESULT __stdcall
+DllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface)
+{
+  if (aClsid == CLSID_AccessibleHandler) {
+    return mozilla::a11y::sHandlerFactory.QueryInterface(aIid, aOutInterface);
+  }
+  return ProxyDllGetClassObject(aClsid, aIid, aOutInterface);
+}
+
+extern "C" BOOL WINAPI
+ProxyDllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved);
+
+BOOL WINAPI
+DllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved)
+{
+  if (aReason == DLL_PROCESS_ATTACH) {
+    DisableThreadLibraryCalls((HMODULE)aInstDll);
+  }
+  // This is required for ProxyDllRegisterServer to work correctly
+  return ProxyDllMain(aInstDll, aReason, aReserved);
+}
+
+extern "C" HRESULT __stdcall
+ProxyDllRegisterServer();
+
+extern "C" HRESULT __stdcall
+DllRegisterServer()
+{
+  HRESULT hr = mozilla::mscom::Handler::Register(CLSID_AccessibleHandler);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ProxyDllRegisterServer();
+}
+
+extern "C" HRESULT __stdcall
+ProxyDllUnregisterServer();
+
+extern "C" HRESULT __stdcall
+DllUnregisterServer()
+{
+  HRESULT hr = mozilla::mscom::Handler::Unregister(CLSID_AccessibleHandler);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ProxyDllUnregisterServer();
+}
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.def
@@ -0,0 +1,10 @@
+;+# 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/.
+
+LIBRARY        AccessibleHandler.dll
+
+EXPORTS        DllGetClassObject      PRIVATE
+               DllCanUnloadNow        PRIVATE
+               DllRegisterServer      PRIVATE
+               DllUnregisterServer    PRIVATE
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.h
@@ -0,0 +1,195 @@
+/* -*- 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_AccessibleHandler_h
+#define mozilla_a11y_AccessibleHandler_h
+
+#define NEWEST_IA2_BASENAME Accessible2_3
+
+#define __QUOTE(idl) #idl
+#define __GENIDL(base) __QUOTE(base##.idl)
+#define IDLFOR(base) __GENIDL(base)
+#define NEWEST_IA2_IDL IDLFOR(NEWEST_IA2_BASENAME)
+
+#define __GENIFACE(base) I##base
+#define INTERFACEFOR(base) __GENIFACE(base)
+#define NEWEST_IA2_INTERFACE INTERFACEFOR(NEWEST_IA2_BASENAME)
+
+#define __GENIID(iface) IID_##iface
+#define IIDFOR(iface) __GENIID(iface)
+#define NEWEST_IA2_IID IIDFOR(NEWEST_IA2_INTERFACE)
+
+#if defined(__midl)
+
+import NEWEST_IA2_IDL;
+
+#else
+
+#include "HandlerData.h"
+
+#include <windows.h>
+
+#if !defined(MOZILLA_INTERNAL_API)
+
+#include "Accessible2_3.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:
+  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;
+
+  REFIID MarshalAs(REFIID aRequestedIid) override;
+  HRESULT GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) override;
+  HRESULT WriteHandlerPayload(IStream* aStream, REFIID aIId) override;
+
+  // IUnknown
+  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+  STDMETHODIMP_(ULONG) AddRef() override;
+  STDMETHODIMP_(ULONG) Release() override;
+
+  // IDispatch
+  STDMETHODIMP GetTypeInfoCount(UINT *pctinfo) override;
+  STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override;
+  STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
+                             LCID lcid, DISPID *rgDispId) override;
+  STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
+                      DISPPARAMS *pDispParams, VARIANT *pVarResult,
+                      EXCEPINFO *pExcepInfo, UINT *puArgErr) override;
+
+  // IAccessible
+  STDMETHODIMP get_accParent(IDispatch **ppdispParent) override;
+  STDMETHODIMP get_accChildCount(long *pcountChildren) override;
+  STDMETHODIMP get_accChild(VARIANT varChild, IDispatch **ppdispChild) override;
+  STDMETHODIMP get_accName(VARIANT varChild, BSTR *pszName) override;
+  STDMETHODIMP get_accValue(VARIANT varChild, BSTR *pszValue) override;
+  STDMETHODIMP get_accDescription(VARIANT varChild, BSTR *pszDescription) override;
+  STDMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole) override;
+  STDMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState) override;
+  STDMETHODIMP get_accHelp(VARIANT varChild, BSTR *pszHelp) override;
+  STDMETHODIMP get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild,
+                                long *pidTopic) override;
+  STDMETHODIMP get_accKeyboardShortcut(VARIANT varChild,
+                                       BSTR *pszKeyboardShortcut) override;
+  STDMETHODIMP get_accFocus(VARIANT *pvarChild) override;
+  STDMETHODIMP get_accSelection(VARIANT *pvarChildren) override;
+  STDMETHODIMP get_accDefaultAction(VARIANT varChild,
+                                    BSTR *pszDefaultAction) override;
+  STDMETHODIMP accSelect(long flagsSelect, VARIANT varChild) override;
+  STDMETHODIMP accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
+                           long *pcyHeight, VARIANT varChild) override;
+  STDMETHODIMP accNavigate(long navDir, VARIANT varStart,
+                           VARIANT *pvarEndUpAt) override;
+  STDMETHODIMP accHitTest( long xLeft, long yTop, VARIANT *pvarChild) override;
+  STDMETHODIMP accDoDefaultAction(VARIANT varChild) override;
+  STDMETHODIMP put_accName(VARIANT varChild, BSTR szName) override;
+  STDMETHODIMP put_accValue(VARIANT varChild, BSTR szValue) override;
+
+  // IAccessible2
+  STDMETHODIMP get_nRelations(long* nRelations) override;
+  STDMETHODIMP get_relation(long relationIndex,
+                            IAccessibleRelation** relation) override;
+  STDMETHODIMP get_relations(long maxRelations, IAccessibleRelation** relations,
+                             long* nRelations) override;
+  STDMETHODIMP role(long* role) override;
+  STDMETHODIMP scrollTo(IA2ScrollType scrollType) override;
+  STDMETHODIMP scrollToPoint(IA2CoordinateType coordinateType, long x,
+                             long y) override;
+  STDMETHODIMP get_groupPosition(long* groupLevel, long* similarItemsInGroup,
+                                 long* positionInGroup) override;
+  STDMETHODIMP get_states(AccessibleStates* states) override;
+  STDMETHODIMP get_extendedRole(BSTR* extendedRole) override;
+  STDMETHODIMP get_localizedExtendedRole(BSTR* localizedExtendedRole) override;
+  STDMETHODIMP get_nExtendedStates(long* nExtendedStates) override;
+  STDMETHODIMP get_extendedStates(long maxExtendedStates, BSTR** extendedStates,
+                                  long* nExtendedStates) override;
+  STDMETHODIMP get_localizedExtendedStates(long maxLocalizedExtendedStates,
+                                           BSTR** localizedExtendedStates,
+                                           long* nLocalizedExtendedStates) override;
+  STDMETHODIMP get_uniqueID(long* uniqueID) override;
+  STDMETHODIMP get_windowHandle(HWND* windowHandle) override;
+  STDMETHODIMP get_indexInParent(long* indexInParent) override;
+  STDMETHODIMP get_locale(IA2Locale* locale) override;
+  STDMETHODIMP get_attributes(BSTR* attributes) override;
+
+  // IAccessible2_2
+  STDMETHODIMP get_attribute(BSTR name, VARIANT* attribute) override;
+  STDMETHODIMP get_accessibleWithCaret(IUnknown** accessible,
+                                       long* caretOffset) override;
+  STDMETHODIMP get_relationTargetsOfType(BSTR type, long maxTargets,
+                                         IUnknown*** targets,
+                                         long* nTargets) override;
+
+  // IAccessible2_3
+  STDMETHODIMP get_selectionRanges(IA2Range** ranges, long* nRanges) override;
+
+  // IServiceProvider
+  STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aIid,
+                            void** aOutInterface) override;
+
+  // IProvideClassInfo
+  STDMETHODIMP GetClassInfo(ITypeInfo** aOutTypeInfo) override;
+
+private:
+  AccessibleHandler(IUnknown* aOuter, HRESULT* aResult);
+  virtual ~AccessibleHandler();
+
+  HRESULT ResolveIA2();
+  HRESULT ResolveIDispatch();
+  HRESULT MaybeUpdateCachedData();
+
+  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
+   * from those aggregated objects have delegated unknowns.
+   *
+   * AddRef'ing an interface with a delegated unknown ends up incrementing the
+   * refcount of the *aggregator*. Since we are the aggregator of mDispatchUnk
+   * and of the wrapped proxy, holding a strong reference to any interfaces
+   * QI'd off of those objects would create a reference cycle.
+   *
+   * We may hold onto pointers to those references, but when we query them we
+   * 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
+  IA2Payload                        mCachedData;
+  UniquePtr<mscom::StructToStream>  mSerializer;
+  uint32_t                          mCacheGen;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // !defined(MOZILLA_INTERNAL_API)
+
+#endif // defined(__midl)
+
+#endif // mozilla_a11y_AccessibleHandler_h
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandler.rc
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+1 typelib HandlerData.tlb
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.cpp
@@ -0,0 +1,208 @@
+/* -*- 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 "AccessibleHandlerControl.h"
+
+#include "AccessibleHandler.h"
+
+#include "AccessibleEventId.h"
+
+#include "mozilla/Move.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace a11y {
+
+mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory;
+
+namespace detail {
+
+TextChange::TextChange()
+  : mIA2UniqueId(0)
+  , mIsInsert(false)
+  , mText()
+{
+}
+
+TextChange::TextChange(long aIA2UniqueId, bool aIsInsert,
+                       NotNull<IA2TextSegment*> aText)
+  : mIA2UniqueId(aIA2UniqueId)
+  , mIsInsert(aIsInsert)
+  , mText{BSTRCopy(aText->text), aText->start, aText->end}
+{
+}
+
+TextChange::TextChange(TextChange&& aOther)
+  : mText()
+{
+  *this = Move(aOther);
+}
+
+TextChange::TextChange(const TextChange& aOther)
+  : mText()
+{
+  *this = aOther;
+}
+
+TextChange&
+TextChange::operator=(TextChange&& aOther)
+{
+  mIA2UniqueId = aOther.mIA2UniqueId;
+  mIsInsert = aOther.mIsInsert;
+  aOther.mIA2UniqueId = 0;
+  ::SysFreeString(mText.text);
+  mText = aOther.mText;
+  aOther.mText.text = nullptr;
+  return *this;
+}
+
+TextChange&
+TextChange::operator=(const TextChange& aOther)
+{
+  mIA2UniqueId = aOther.mIA2UniqueId;
+  mIsInsert = aOther.mIsInsert;
+  ::SysFreeString(mText.text);
+  mText = {BSTRCopy(aOther.mText.text), aOther.mText.start, aOther.mText.end};
+  return *this;
+}
+
+TextChange::~TextChange()
+{
+  ::SysFreeString(mText.text);
+}
+
+HRESULT
+TextChange::GetOld(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldSegment)
+{
+  if (mIsInsert || aIA2UniqueId != mIA2UniqueId) {
+    return S_OK;
+  }
+
+  return SegCopy(*aOutOldSegment, mText);
+}
+
+HRESULT
+TextChange::GetNew(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewSegment)
+{
+  if (!mIsInsert || aIA2UniqueId != mIA2UniqueId) {
+    return S_OK;
+  }
+
+  return SegCopy(*aOutNewSegment, mText);
+}
+
+/* static */ BSTR
+TextChange::BSTRCopy(const BSTR& aIn)
+{
+  return ::SysAllocStringLen(aIn, ::SysStringLen(aIn));
+}
+
+/* static */ HRESULT
+TextChange::SegCopy(IA2TextSegment& aDest, const IA2TextSegment& aSrc)
+{
+  aDest = {BSTRCopy(aSrc.text), aSrc.start, aSrc.end};
+  if (aSrc.text && !aDest.text) {
+    return E_OUTOFMEMORY;
+  }
+  if (!::SysStringLen(aDest.text)) {
+    return S_FALSE;
+  }
+  return S_OK;
+}
+
+} // namespace detail
+
+HRESULT
+AccessibleHandlerControl::Create(AccessibleHandlerControl** aOutObject)
+{
+  if (!aOutObject) {
+    return E_INVALIDARG;
+  }
+
+  RefPtr<AccessibleHandlerControl> ctl(new AccessibleHandlerControl());
+  ctl.forget(aOutObject);
+  return S_OK;
+}
+
+AccessibleHandlerControl::AccessibleHandlerControl()
+  : mIsRegistered(false)
+  , mCacheGen(0)
+  , mIA2Proxy(mscom::RegisterProxy(L"ia2marshal.dll"))
+  , mHandlerProxy(mscom::RegisterProxy())
+{
+  MOZ_ASSERT(mIA2Proxy);
+}
+
+IMPL_IUNKNOWN1(AccessibleHandlerControl, IHandlerControl)
+
+HRESULT
+AccessibleHandlerControl::Invalidate()
+{
+  ++mCacheGen;
+  return S_OK;
+}
+
+HRESULT
+AccessibleHandlerControl::OnTextChange(long aHwnd, long aIA2UniqueId,
+                                       VARIANT_BOOL aIsInsert,
+                                       IA2TextSegment* aText)
+{
+  if (!aText) {
+    return E_INVALIDARG;
+  }
+
+  mTextChange = detail::TextChange(aIA2UniqueId, aIsInsert, WrapNotNull(aText));
+  NotifyWinEvent(aIsInsert ? IA2_EVENT_TEXT_INSERTED : IA2_EVENT_TEXT_REMOVED,
+                 reinterpret_cast<HWND>(static_cast<uintptr_t>(aHwnd)),
+                 OBJID_CLIENT, aIA2UniqueId);
+  return S_OK;
+}
+
+HRESULT
+AccessibleHandlerControl::GetNewText(long aIA2UniqueId,
+                                     NotNull<IA2TextSegment*> aOutNewText)
+{
+  return mTextChange.GetNew(aIA2UniqueId, aOutNewText);
+}
+
+HRESULT
+AccessibleHandlerControl::GetOldText(long aIA2UniqueId,
+                                     NotNull<IA2TextSegment*> aOutOldText)
+{
+  return mTextChange.GetOld(aIA2UniqueId, aOutOldText);
+}
+
+HRESULT
+AccessibleHandlerControl::GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo)
+{
+  if (!mHandlerProxy) {
+    return E_UNEXPECTED;
+  }
+
+  return mHandlerProxy->GetTypeInfoForGuid(CLSID_AccessibleHandler,
+                                           aOutTypeInfo);
+}
+
+HRESULT
+AccessibleHandlerControl::Register(NotNull<IGeckoBackChannel*> aGecko)
+{
+  if (mIsRegistered) {
+    return S_OK;
+  }
+
+  long pid = static_cast<long>(::GetCurrentProcessId());
+  HRESULT hr = aGecko->put_HandlerControl(pid, this);
+  mIsRegistered = SUCCEEDED(hr);
+  MOZ_ASSERT(mIsRegistered);
+  return hr;
+}
+
+} // namespace a11y
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleHandlerControl.h
@@ -0,0 +1,92 @@
+/* -*- 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_AccessibleHandlerControl_h
+#define mozilla_a11y_AccessibleHandlerControl_h
+
+#include "Factory.h"
+#include "HandlerData.h"
+#include "IUnknownImpl.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/NotNull.h"
+
+namespace mozilla {
+namespace a11y {
+
+namespace detail {
+
+class TextChange final
+{
+public:
+  TextChange();
+  TextChange(long aIA2UniqueId, bool aIsInsert, NotNull<IA2TextSegment*> aText);
+  TextChange(TextChange&& aOther);
+  TextChange(const TextChange& aOther);
+
+  TextChange& operator=(TextChange&& aOther);
+  TextChange& operator=(const TextChange& aOther);
+
+  ~TextChange();
+
+  HRESULT GetOld(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldSegment);
+  HRESULT GetNew(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewSegment);
+
+private:
+  static BSTR BSTRCopy(const BSTR& aIn);
+  static HRESULT SegCopy(IA2TextSegment& aDest, const IA2TextSegment& aSrc);
+
+  long mIA2UniqueId;
+  bool mIsInsert;
+  IA2TextSegment mText;
+};
+
+} // namespace detail
+
+class AccessibleHandlerControl final : public IHandlerControl
+{
+public:
+  static HRESULT Create(AccessibleHandlerControl** aOutObject);
+
+  DECL_IUNKNOWN
+
+  // IHandlerControl
+  STDMETHODIMP Invalidate() override;
+  STDMETHODIMP OnTextChange(long aHwnd, long aIA2UniqueId,
+                            VARIANT_BOOL aIsInsert, IA2TextSegment* aText) override;
+
+  uint32_t GetCacheGen() const
+  {
+    return mCacheGen;
+  }
+
+  HRESULT GetNewText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutNewText);
+  HRESULT GetOldText(long aIA2UniqueId, NotNull<IA2TextSegment*> aOutOldText);
+
+  HRESULT GetHandlerTypeInfo(ITypeInfo** aOutTypeInfo);
+
+  HRESULT Register(NotNull<IGeckoBackChannel*> aGecko);
+
+private:
+  AccessibleHandlerControl();
+  ~AccessibleHandlerControl() = default;
+
+  bool mIsRegistered;
+  uint32_t mCacheGen;
+  detail::TextChange mTextChange;
+  UniquePtr<mscom::RegisteredProxy> mIA2Proxy;
+  UniquePtr<mscom::RegisteredProxy> mHandlerProxy;
+};
+
+extern mscom::SingletonFactory<AccessibleHandlerControl> gControlFactory;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif // mozilla_a11y_AccessibleHandlerControl_h
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleTextTearoff.cpp
@@ -0,0 +1,359 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/AccessibleTextTearoff.h
@@ -0,0 +1,88 @@
+/* -*- 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
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerData.acf
@@ -0,0 +1,11 @@
+/* -*- 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/. */
+
+[explicit_handle]
+interface HandlerData
+{
+  typedef [encode,decode] IA2Payload;
+}
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/HandlerData.idl
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla-config.h"
+#include "AccessibleHandler.h"
+
+import "ocidl.idl";
+import "ServProv.idl";
+
+import "AccessibleText.idl";
+
+typedef struct _IA2Data
+{
+  long mUniqueId;
+} IA2Data;
+
+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)
+#if defined(USE_LOCAL_UUID)
+
+# if defined(DEBUG)
+
+// Local debug builds
+#  define HANDLER_CLSID 398ffd8d-5382-48f7-9e3b-19012762d39a
+#  define IHANDLERCONTROL_IID a218497e-8b10-460b-b668-a92b7ee39ff2
+#  define ASYNCIHANDLERCONTROL_IID ca18b9ab-04b6-41be-87f7-d99913d6a2e8
+#  define IGECKOBACKCHANNEL_IID 231c4946-4479-4c8e-aadc-8a0e48fc4c51
+
+# else
+
+// Local non-debug builds
+#  define HANDLER_CLSID ce573faf-7815-4fc2-a031-b092268ace9e
+#  define IHANDLERCONTROL_IID 2b715cce-1790-4fe1-aef5-48bb5acdf3a1
+#  define ASYNCIHANDLERCONTROL_IID 8e089670-4f57-41a7-89c0-37f17482fa6f
+#  define IGECKOBACKCHANNEL_IID 18e2488d-310f-400f-8339-0e50b513e801
+
+# endif
+
+#elif defined(NIGHTLY_BUILD)
+
+// Nightly Builds
+# define IHANDLERCONTROL_IID c57343fc-e011-40c2-b748-da82eabf0f1f
+# define ASYNCIHANDLERCONTROL_IID 648c92a1-ea35-46da-a806-6b55c6247373
+# define HANDLER_CLSID 4629216b-8753-41bf-9527-5bff51401671
+# define IGECKOBACKCHANNEL_IID e61e038d-40dd-464a-9aba-66b206b6911b
+
+#elif defined(USE_BETA_UUID)
+
+// Beta Builds
+# define IHANDLERCONTROL_IID 119149fa-d212-4f22-9517-082eecc1a084
+# define ASYNCIHANDLERCONTROL_IID 4e253d9b-59cf-4b32-a973-38bc85495d61
+# define HANDLER_CLSID 21e9f98d-a6c9-4cb5-b288-ae2fd2a96c58
+# define IGECKOBACKCHANNEL_IID 77b75c7d-d1c2-4469-864d-31aaebb67cc6
+
+#elif defined(RELEASE_OR_BETA)
+
+// Release Builds
+# define IHANDLERCONTROL_IID ce30f77e-8847-44f0-a648-a9656bd89c0d
+# define ASYNCIHANDLERCONTROL_IID dca8d857-1a63-4045-8f36-8809eb093d04
+# define HANDLER_CLSID 1baa303d-b4b9-45e5-9ccb-e3fca3e274b6
+# define IGECKOBACKCHANNEL_IID b32983ff-ef84-4945-8f86-fb7491b4f57b
+
+#else
+
+// Catch-all
+# define IHANDLERCONTROL_IID 3316ce35-f892-4832-97c5-06c52c03cdba
+# define ASYNCIHANDLERCONTROL_IID 15b48b76-ad38-4ad3-bd1a-d3c48a5a9947
+# define HANDLER_CLSID 4a195748-dca2-45fb-9295-0a139e76a9e7
+# define IGECKOBACKCHANNEL_IID dd2e4a89-999e-4d65-8b65-440c923ddb61
+
+#endif
+
+[uuid(2b0e83b3-fd1a-443f-9ed6-c00d39055b58)]
+interface HandlerData
+{
+  typedef struct _IA2Payload
+  {
+    IA2Data mData;
+    IGeckoBackChannel* mGeckoBackChannel;
+  } IA2Payload;
+}
+
+[object,
+ uuid(IHANDLERCONTROL_IID),
+ async_uuid(ASYNCIHANDLERCONTROL_IID),
+ pointer_default(unique)]
+interface IHandlerControl : IUnknown
+{
+  HRESULT Invalidate();
+  HRESULT OnTextChange([in] long aHwnd, [in] long aIA2UniqueId,
+                       [in] VARIANT_BOOL aIsInsert,
+                       [in] IA2TextSegment* aText);
+}
+
+[object,
+ uuid(IGECKOBACKCHANNEL_IID),
+ pointer_default(unique)]
+interface IGeckoBackChannel : IUnknown
+{
+  [propput] HRESULT HandlerControl([in] long aPid, [in] IHandlerControl* aCtrl);
+  HRESULT Refresh([out] IA2Data* aOutData);
+}
+
+[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
+   * FFIs to discover which interfaces may be controlled via IDispatch.
+   * (In particular, the python FFI used by NVDA needs this).
+   *
+   * In reality, the only a11y interface that is Automation compliant is
+   * IAccessible; our remaining interfaces are not.
+   *
+   * Once the FFI knows that IAccessible is supported, the FFI queries for
+   * IAccessible and is then able to resolve non-automation interfaces from
+   * there.
+   */
+  [uuid(HANDLER_CLSID)]
+  coclass AccessibleHandler
+  {
+    [default] interface IAccessible;
+  };
+};
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/Makefile.in
@@ -0,0 +1,38 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+IA2DIR = $(topsrcdir)/other-licenses/ia2
+MSAADIR = $(topsrcdir)/accessible/interfaces/msaa
+GARBAGE += $(MIDL_GENERATED_FILES) midl_done
+
+MIDL_GENERATED_FILES = \
+  dlldata.c \
+  HandlerData.h \
+  HandlerData_c.c \
+  HandlerData_i.c \
+  HandlerData_p.c \
+  HandlerData.tlb \
+  $(NULL)
+
+export:: $(MIDL_GENERATED_FILES)
+
+$(MIDL_GENERATED_FILES): midl_done
+
+midl_done: HandlerData.acf HandlerData.idl
+	$(MIDL) $(MIDL_FLAGS) $(DEFINES) -I $(topobjdir) -I $(DIST)/include -I $(IA2DIR) -I $(MSAADIR) -Oicf -acf $(srcdir)/HandlerData.acf $(srcdir)/HandlerData.idl
+	touch $@
+
+INSTALL_TARGETS += midl
+midl_FILES := HandlerData.h \
+              HandlerData_i.c \
+              $(NULL)
+midl_DEST := $(DIST)/include
+midl_TARGET := midl
+
+export:: midl
+
+register::
+	regsvr32 -s $(DIST)/bin/$(SHARED_LIBRARY)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/ipc/win/handler/moz.build
@@ -0,0 +1,71 @@
+# -*- 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']
+
+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',
+    'HandlerData_p.c',
+]
+
+DEFFILE = SRCDIR + '/AccessibleHandler.def'
+
+USE_LIBS += [
+    'mscom_oop',
+]
+
+OS_LIBS += [
+    'rpcrt4',
+]
+
+RCINCLUDE = 'AccessibleHandler.rc'
+
+# Since we are defining our own COM entry points (DllRegisterServer et al),
+# but we still want to be able to delegate some work to the generated code,
+# we add the prefix "Proxy" to all of the generated counterparts.
+DEFINES['ENTRY_PREFIX'] = 'Proxy'
+DEFINES['REGISTER_PROXY_DLL'] = True
+LIBRARY_DEFINES['MOZ_MSCOM_REMARSHAL_NO_HANDLER'] = True
+
+# We want to generate distinct UUIDs on a per-channel basis, so we need
+# finer granularity than the standard preprocessor definitions offer.
+# These defines allow us to separate local builds from automated builds,
+# as well as separate beta from release.
+if CONFIG['MOZ_UPDATE_CHANNEL'] == 'default':
+  DEFINES['USE_LOCAL_UUID'] = True
+elif CONFIG['MOZ_UPDATE_CHANNEL'] == 'beta':
+  DEFINES['USE_BETA_UUID'] = True
+
+# This DLL may be loaded into other processes, so we need static libs for
+# Windows 7 and Windows 8.
+USE_STATIC_LIBS = True
+
+LIBRARY_DEFINES['UNICODE'] = True
+LIBRARY_DEFINES['_UNICODE'] = True
+LIBRARY_DEFINES['MOZ_NO_MOZALLOC'] = True
+DISABLE_STL_WRAPPING = True
+
--- a/accessible/ipc/win/moz.build
+++ b/accessible/ipc/win/moz.build
@@ -1,32 +1,50 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
-if CONFIG['COMPILE_ENVIRONMENT']:
-    DIRS += ['typelib']
+if CONFIG['COMPILE_ENVIRONMENT'] and CONFIG['ACCESSIBILITY']:
+    DIRS += [
+        'handler',
+        'typelib',
+    ]
 
 # With --disable-accessibility, we need to compile PDocAccessible.ipdl (which
 # also depends on COMPtrTypes.h), but not the C++.
 IPDL_SOURCES += ['PDocAccessible.ipdl']
-EXPORTS.mozilla.a11y += ['COMPtrTypes.h']
+
+EXPORTS.mozilla.a11y += [
+    'COMPtrTypes.h',
+]
 
 if CONFIG['ACCESSIBILITY']:
+    if not CONFIG['HAVE_64BIT_BUILD']:
+        EXPORTS += [
+            'IAccessible32.manifest',
+        ]
+
+    EXPORTS += [
+        'IAccessible64.manifest',
+    ]
+
     EXPORTS.mozilla.a11y += [
         'DocAccessibleChild.h',
+        'HandlerProvider.h',
         'PlatformChild.h',
         'ProxyAccessible.h'
     ]
 
     SOURCES += [
+        '!./handler/HandlerData_c.c',
         'COMPtrTypes.cpp',
         'DocAccessibleChild.cpp',
+        'HandlerProvider.cpp',
         'PlatformChild.cpp',
         'ProxyAccessible.cpp',
     ]
 
     LOCAL_INCLUDES += [
         '/accessible/base',
         '/accessible/generic',
         '/accessible/windows/ia2',
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -1,67 +1,65 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* global AccessFu, Components, Utils, PrefCache, Logger, Services,
-          PointerAdapter, dump, Presentation, Rect */
 /* exported AccessFu */
 
-'use strict';
+"use strict";
 
 const {utils: Cu, interfaces: Ci} = Components;
 
-this.EXPORTED_SYMBOLS = ['AccessFu']; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ["AccessFu"]; // jshint ignore:line
 
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/accessibility/Utils.jsm");
 
-if (Utils.MozBuildApp === 'mobile/android') {
-  Cu.import('resource://gre/modules/Messaging.jsm');
+if (Utils.MozBuildApp === "mobile/android") {
+  Cu.import("resource://gre/modules/Messaging.jsm");
 }
 
 const ACCESSFU_DISABLE = 0; // jshint ignore:line
 const ACCESSFU_ENABLE = 1;
 const ACCESSFU_AUTO = 2;
 
-const SCREENREADER_SETTING = 'accessibility.screenreader';
-const QUICKNAV_MODES_PREF = 'accessibility.accessfu.quicknav_modes';
-const QUICKNAV_INDEX_PREF = 'accessibility.accessfu.quicknav_index';
+const SCREENREADER_SETTING = "accessibility.screenreader";
+const QUICKNAV_MODES_PREF = "accessibility.accessfu.quicknav_modes";
+const QUICKNAV_INDEX_PREF = "accessibility.accessfu.quicknav_index";
 
 this.AccessFu = { // jshint ignore:line
   /**
    * Initialize chrome-layer accessibility functionality.
    * If accessibility is enabled on the platform, then a special accessibility
    * mode is started.
    */
   attach: function attach(aWindow) {
     Utils.init(aWindow);
 
-    if (Utils.MozBuildApp === 'mobile/android') {
-      EventDispatcher.instance.dispatch('Accessibility:Ready');
-      EventDispatcher.instance.registerListener(this, 'Accessibility:Settings');
+    if (Utils.MozBuildApp === "mobile/android") {
+      EventDispatcher.instance.dispatch("Accessibility:Ready");
+      EventDispatcher.instance.registerListener(this, "Accessibility:Settings");
     }
 
     this._activatePref = new PrefCache(
-      'accessibility.accessfu.activate', this._enableOrDisable.bind(this));
+      "accessibility.accessfu.activate", this._enableOrDisable.bind(this));
 
     this._enableOrDisable();
   },
 
   /**
    * Shut down chrome-layer accessibility functionality from the outside.
    */
   detach: function detach() {
     // Avoid disabling twice.
     if (this._enabled) {
       this._disable();
     }
-    if (Utils.MozBuildApp === 'mobile/android') {
-      EventDispatcher.instance.unregisterListener(this, 'Accessibility:Settings');
+    if (Utils.MozBuildApp === "mobile/android") {
+      EventDispatcher.instance.unregisterListener(this, "Accessibility:Settings");
     }
     delete this._activatePref;
     Utils.uninit();
   },
 
   /**
    * A lazy getter for event handler that binds the scope to AccessFu object.
    */
@@ -76,29 +74,29 @@ this.AccessFu = { // jshint ignore:line
    * with arrow keys.
    */
   _enable: function _enable() {
     if (this._enabled) {
       return;
     }
     this._enabled = true;
 
-    Cu.import('resource://gre/modules/accessibility/Utils.jsm');
-    Cu.import('resource://gre/modules/accessibility/PointerAdapter.jsm');
-    Cu.import('resource://gre/modules/accessibility/Presentation.jsm');
+    Cu.import("resource://gre/modules/accessibility/Utils.jsm");
+    Cu.import("resource://gre/modules/accessibility/PointerAdapter.jsm");
+    Cu.import("resource://gre/modules/accessibility/Presentation.jsm");
 
     for (let mm of Utils.AllMessageManagers) {
       this._addMessageListeners(mm);
       this._loadFrameScript(mm);
     }
 
     // Add stylesheet
-    let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
+    let stylesheetURL = "chrome://global/content/accessibility/AccessFu.css";
     let stylesheet = Utils.win.document.createProcessingInstruction(
-      'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
+      "xml-stylesheet", `href="${stylesheetURL}" type="text/css"`);
     Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
     this.stylesheet = Cu.getWeakReference(stylesheet);
 
 
     // Populate quicknav modes
     this._quicknavModesPref =
       new PrefCache(QUICKNAV_MODES_PREF, (aName, aValue, aFirstRun) => {
         this.Input.quickNavMode.updateModes(aValue);
@@ -110,139 +108,139 @@ this.AccessFu = { // jshint ignore:line
 
     this._quicknavCurrentModePref =
       new PrefCache(QUICKNAV_INDEX_PREF, (aName, aValue) => {
         this.Input.quickNavMode.updateCurrentMode(Number(aValue));
       }, true);
 
     // Check for output notification
     this._notifyOutputPref =
-      new PrefCache('accessibility.accessfu.notify_output');
+      new PrefCache("accessibility.accessfu.notify_output");
 
 
     this.Input.start();
     Output.start();
     PointerAdapter.start();
 
-    if (Utils.MozBuildApp === 'mobile/android') {
+    if (Utils.MozBuildApp === "mobile/android") {
       EventDispatcher.instance.registerListener(this, [
-        'Accessibility:ActivateObject',
-        'Accessibility:Focus',
-        'Accessibility:LongPress',
-        'Accessibility:MoveByGranularity',
-        'Accessibility:NextObject',
-        'Accessibility:PreviousObject',
-        'Accessibility:ScrollBackward',
-        'Accessibility:ScrollForward',
+        "Accessibility:ActivateObject",
+        "Accessibility:Focus",
+        "Accessibility:LongPress",
+        "Accessibility:MoveByGranularity",
+        "Accessibility:NextObject",
+        "Accessibility:PreviousObject",
+        "Accessibility:ScrollBackward",
+        "Accessibility:ScrollForward",
       ]);
     }
 
-    Services.obs.addObserver(this, 'remote-browser-shown', false);
-    Services.obs.addObserver(this, 'inprocess-browser-shown', false);
-    Utils.win.addEventListener('TabOpen', this);
-    Utils.win.addEventListener('TabClose', this);
-    Utils.win.addEventListener('TabSelect', this);
+    Services.obs.addObserver(this, "remote-browser-shown");
+    Services.obs.addObserver(this, "inprocess-browser-shown");
+    Utils.win.addEventListener("TabOpen", this);
+    Utils.win.addEventListener("TabClose", this);
+    Utils.win.addEventListener("TabSelect", this);
 
     if (this.readyCallback) {
       this.readyCallback();
       delete this.readyCallback;
     }
 
-    Logger.info('AccessFu:Enabled');
+    Logger.info("AccessFu:Enabled");
   },
 
   /**
    * Disable AccessFu and return to default interaction mode.
    */
   _disable: function _disable() {
     if (!this._enabled) {
       return;
     }
 
     this._enabled = false;
 
     Utils.win.document.removeChild(this.stylesheet.get());
 
     for (let mm of Utils.AllMessageManagers) {
-      mm.sendAsyncMessage('AccessFu:Stop');
+      mm.sendAsyncMessage("AccessFu:Stop");
       this._removeMessageListeners(mm);
     }
 
     this.Input.stop();
     Output.stop();
     PointerAdapter.stop();
 
-    Utils.win.removeEventListener('TabOpen', this);
-    Utils.win.removeEventListener('TabClose', this);
-    Utils.win.removeEventListener('TabSelect', this);
+    Utils.win.removeEventListener("TabOpen", this);
+    Utils.win.removeEventListener("TabClose", this);
+    Utils.win.removeEventListener("TabSelect", this);
 
-    Services.obs.removeObserver(this, 'remote-browser-shown');
-    Services.obs.removeObserver(this, 'inprocess-browser-shown');
+    Services.obs.removeObserver(this, "remote-browser-shown");
+    Services.obs.removeObserver(this, "inprocess-browser-shown");
 
-    if (Utils.MozBuildApp === 'mobile/android') {
+    if (Utils.MozBuildApp === "mobile/android") {
       EventDispatcher.instance.unregisterListener(this, [
-        'Accessibility:ActivateObject',
-        'Accessibility:Focus',
-        'Accessibility:LongPress',
-        'Accessibility:MoveByGranularity',
-        'Accessibility:NextObject',
-        'Accessibility:PreviousObject',
-        'Accessibility:ScrollBackward',
-        'Accessibility:ScrollForward',
+        "Accessibility:ActivateObject",
+        "Accessibility:Focus",
+        "Accessibility:LongPress",
+        "Accessibility:MoveByGranularity",
+        "Accessibility:NextObject",
+        "Accessibility:PreviousObject",
+        "Accessibility:ScrollBackward",
+        "Accessibility:ScrollForward",
       ]);
     }
 
     delete this._quicknavModesPref;
     delete this._notifyOutputPref;
 
     if (this.doneCallback) {
       this.doneCallback();
       delete this.doneCallback;
     }
 
-    Logger.info('AccessFu:Disabled');
+    Logger.info("AccessFu:Disabled");
   },
 
   _enableOrDisable: function _enableOrDisable() {
     try {
       if (!this._activatePref) {
         return;
       }
       let activatePref = this._activatePref.value;
       if (activatePref == ACCESSFU_ENABLE ||
           this._systemPref && activatePref == ACCESSFU_AUTO) {
         this._enable();
       } else {
         this._disable();
       }
     } catch (x) {
-      dump('Error ' + x.message + ' ' + x.fileName + ':' + x.lineNumber);
+      dump("Error " + x.message + " " + x.fileName + ":" + x.lineNumber);
     }
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     Logger.debug(() => {
-      return ['Recieved', aMessage.name, JSON.stringify(aMessage.json)];
+      return ["Recieved", aMessage.name, JSON.stringify(aMessage.json)];
     });
 
     switch (aMessage.name) {
-      case 'AccessFu:Ready':
+      case "AccessFu:Ready":
         let mm = Utils.getMessageManager(aMessage.target);
         if (this._enabled) {
-          mm.sendAsyncMessage('AccessFu:Start',
-                              {method: 'start', buildApp: Utils.MozBuildApp});
+          mm.sendAsyncMessage("AccessFu:Start",
+                              {method: "start", buildApp: Utils.MozBuildApp});
         }
         break;
-      case 'AccessFu:Present':
+      case "AccessFu:Present":
         this._output(aMessage.json, aMessage.target);
         break;
-      case 'AccessFu:Input':
+      case "AccessFu:Input":
         this.Input.setEditState(aMessage.json);
         break;
-      case 'AccessFu:DoScroll':
+      case "AccessFu:DoScroll":
         this.Input.doScroll(aMessage.json);
         break;
     }
   },
 
   _output: function _output(aPresentationData, aBrowser) {
     if (!Utils.isAliveAndVisible(
       Utils.AccService.getAccessibleFor(aBrowser))) {
@@ -256,139 +254,139 @@ this.AccessFu = { // jshint ignore:line
       try {
         Output[presenter.type](presenter.details, aBrowser);
       } catch (x) {
         Logger.logException(x);
       }
     }
 
     if (this._notifyOutputPref.value) {
-      Services.obs.notifyObservers(null, 'accessibility-output',
+      Services.obs.notifyObservers(null, "accessibility-output",
                                    JSON.stringify(aPresentationData));
     }
   },
 
   _loadFrameScript: function _loadFrameScript(aMessageManager) {
     if (this._processedMessageManagers.indexOf(aMessageManager) < 0) {
       aMessageManager.loadFrameScript(
-        'chrome://global/content/accessibility/content-script.js', true);
+        "chrome://global/content/accessibility/content-script.js", true);
       this._processedMessageManagers.push(aMessageManager);
     } else if (this._enabled) {
       // If the content-script is already loaded and AccessFu is enabled,
       // send an AccessFu:Start message.
-      aMessageManager.sendAsyncMessage('AccessFu:Start',
-        {method: 'start', buildApp: Utils.MozBuildApp});
+      aMessageManager.sendAsyncMessage("AccessFu:Start",
+        {method: "start", buildApp: Utils.MozBuildApp});
     }
   },
 
   _addMessageListeners: function _addMessageListeners(aMessageManager) {
-    aMessageManager.addMessageListener('AccessFu:Present', this);
-    aMessageManager.addMessageListener('AccessFu:Input', this);
-    aMessageManager.addMessageListener('AccessFu:Ready', this);
-    aMessageManager.addMessageListener('AccessFu:DoScroll', this);
+    aMessageManager.addMessageListener("AccessFu:Present", this);
+    aMessageManager.addMessageListener("AccessFu:Input", this);
+    aMessageManager.addMessageListener("AccessFu:Ready", this);
+    aMessageManager.addMessageListener("AccessFu:DoScroll", this);
   },
 
   _removeMessageListeners: function _removeMessageListeners(aMessageManager) {
-    aMessageManager.removeMessageListener('AccessFu:Present', this);
-    aMessageManager.removeMessageListener('AccessFu:Input', this);
-    aMessageManager.removeMessageListener('AccessFu:Ready', this);
-    aMessageManager.removeMessageListener('AccessFu:DoScroll', this);
+    aMessageManager.removeMessageListener("AccessFu:Present", this);
+    aMessageManager.removeMessageListener("AccessFu:Input", this);
+    aMessageManager.removeMessageListener("AccessFu:Ready", this);
+    aMessageManager.removeMessageListener("AccessFu:DoScroll", this);
   },
 
   _handleMessageManager: function _handleMessageManager(aMessageManager) {
     if (this._enabled) {
       this._addMessageListeners(aMessageManager);
     }
     this._loadFrameScript(aMessageManager);
   },
 
-  onEvent: function (event, data, callback) {
+  onEvent: function(event, data, callback) {
     switch (event) {
-      case 'Accessibility:Settings':
+      case "Accessibility:Settings":
         this._systemPref = data.enabled;
         this._enableOrDisable();
         break;
-      case 'Accessibility:NextObject':
-      case 'Accessibility:PreviousObject': {
+      case "Accessibility:NextObject":
+      case "Accessibility:PreviousObject": {
         let rule = data ?
           data.rule.substr(0, 1).toUpperCase() + data.rule.substr(1).toLowerCase() :
-          'Simple';
-        let method = event.replace(/Accessibility:(\w+)Object/, 'move$1');
-        this.Input.moveCursor(method, rule, 'gesture');
+          "Simple";
+        let method = event.replace(/Accessibility:(\w+)Object/, "move$1");
+        this.Input.moveCursor(method, rule, "gesture");
         break;
       }
-      case 'Accessibility:ActivateObject':
+      case "Accessibility:ActivateObject":
         this.Input.activateCurrent(data);
         break;
-      case 'Accessibility:LongPress':
+      case "Accessibility:LongPress":
         this.Input.sendContextMenuMessage();
         break;
-      case 'Accessibility:ScrollForward':
-        this.Input.androidScroll('forward');
+      case "Accessibility:ScrollForward":
+        this.Input.androidScroll("forward");
         break;
-      case 'Accessibility:ScrollBackward':
-        this.Input.androidScroll('backward');
+      case "Accessibility:ScrollBackward":
+        this.Input.androidScroll("backward");
         break;
-      case 'Accessibility:Focus':
+      case "Accessibility:Focus":
         this._focused = data.gainFocus;
         if (this._focused) {
           this.autoMove({ forcePresent: true, noOpIfOnScreen: true });
         }
         break;
-      case 'Accessibility:MoveByGranularity':
+      case "Accessibility:MoveByGranularity":
         this.Input.moveByGranularity(data);
         break;
     }
   },
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case 'remote-browser-shown':
-      case 'inprocess-browser-shown':
+      case "remote-browser-shown":
+      case "inprocess-browser-shown":
       {
         // Ignore notifications that aren't from a Browser
         let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
         if (!frameLoader.ownerIsMozBrowserFrame) {
           return;
         }
         this._handleMessageManager(frameLoader.messageManager);
         break;
       }
     }
   },
 
   _handleEvent: function _handleEvent(aEvent) {
     switch (aEvent.type) {
-      case 'TabOpen':
+      case "TabOpen":
       {
         let mm = Utils.getMessageManager(aEvent.target);
         this._handleMessageManager(mm);
         break;
       }
-      case 'TabClose':
+      case "TabClose":
       {
         let mm = Utils.getMessageManager(aEvent.target);
         let mmIndex = this._processedMessageManagers.indexOf(mm);
         if (mmIndex > -1) {
           this._removeMessageListeners(mm);
           this._processedMessageManagers.splice(mmIndex, 1);
         }
         break;
       }
-      case 'TabSelect':
+      case "TabSelect":
       {
         if (this._focused) {
           // We delay this for half a second so the awesomebar could close,
           // and we could use the current coordinates for the content item.
           // XXX TODO figure out how to avoid magic wait here.
-	  this.autoMove({
-	    delay: 500,
-	    forcePresent: true,
-	    noOpIfOnScreen: true,
-	    moveMethod: 'moveFirst' });
+          this.autoMove({
+            delay: 500,
+            forcePresent: true,
+            noOpIfOnScreen: true,
+            moveMethod: "moveFirst" });
         }
         break;
       }
       default:
       {
         // A settings change, it does not have an event type
         if (aEvent.settingName == SCREENREADER_SETTING) {
           this._systemPref = aEvent.settingValue;
@@ -396,17 +394,17 @@ this.AccessFu = { // jshint ignore:line
         }
         break;
       }
     }
   },
 
   autoMove: function autoMove(aOptions) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:AutoMove', aOptions);
+    mm.sendAsyncMessage("AccessFu:AutoMove", aOptions);
   },
 
   announce: function announce(aAnnouncement) {
     this._output(Presentation.announce(aAnnouncement), Utils.CurrentBrowser);
   },
 
   // So we don't enable/disable twice
   _enabled: false,
@@ -447,61 +445,61 @@ this.AccessFu = { // jshint ignore:line
       return bounds.expandToIntegers();
     }
 };
 
 var Output = {
   brailleState: {
     startOffset: 0,
     endOffset: 0,
-    text: '',
+    text: "",
     selectionStart: 0,
     selectionEnd: 0,
 
     init: function init(aOutput) {
-      if (aOutput && 'output' in aOutput) {
+      if (aOutput && "output" in aOutput) {
         this.startOffset = aOutput.startOffset;
         this.endOffset = aOutput.endOffset;
         // We need to append a space at the end so that the routing key
         // corresponding to the end of the output (i.e. the space) can be hit to
         // move the caret there.
-        this.text = aOutput.output + ' ';
-        this.selectionStart = typeof aOutput.selectionStart === 'number' ?
+        this.text = aOutput.output + " ";
+        this.selectionStart = typeof aOutput.selectionStart === "number" ?
                               aOutput.selectionStart : this.selectionStart;
-        this.selectionEnd = typeof aOutput.selectionEnd === 'number' ?
+        this.selectionEnd = typeof aOutput.selectionEnd === "number" ?
                             aOutput.selectionEnd : this.selectionEnd;
 
         return { text: this.text,
                  selectionStart: this.selectionStart,
                  selectionEnd: this.selectionEnd };
       }
 
       return null;
     },
 
     adjustText: function adjustText(aText) {
       let newBraille = [];
       let braille = {};
 
       let prefix = this.text.substring(0, this.startOffset).trim();
       if (prefix) {
-        prefix += ' ';
+        prefix += " ";
         newBraille.push(prefix);
       }
 
       newBraille.push(aText);
 
       let suffix = this.text.substring(this.endOffset).trim();
       if (suffix) {
-        suffix = ' ' + suffix;
+        suffix = " " + suffix;
         newBraille.push(suffix);
       }
 
       this.startOffset = braille.startOffset = prefix.length;
-      this.text = braille.text = newBraille.join('') + ' ';
+      this.text = braille.text = newBraille.join("") + " ";
       this.endOffset = braille.endOffset = braille.text.length - suffix.length;
       braille.selectionStart = this.selectionStart;
       braille.selectionEnd = this.selectionEnd;
 
       return braille;
     },
 
     adjustSelection: function adjustSelection(aSelection) {
@@ -515,93 +513,93 @@ var Output = {
       this.selectionEnd = braille.selectionEnd =
         aSelection.selectionEnd + this.startOffset;
 
       return braille;
     }
   },
 
   start: function start() {
-    Cu.import('resource://gre/modules/Geometry.jsm');
+    Cu.import("resource://gre/modules/Geometry.jsm");
   },
 
   stop: function stop() {
     if (this.highlightBox) {
       let highlightBox = this.highlightBox.get();
       if (highlightBox) {
         highlightBox.remove();
       }
       delete this.highlightBox;
     }
   },
 
   B2G: function B2G(aDetails) {
-    Utils.dispatchChromeEvent('accessibility-output', aDetails);
+    Utils.dispatchChromeEvent("accessibility-output", aDetails);
   },
 
   Visual: function Visual(aDetail, aBrowser) {
     switch (aDetail.eventType) {
-      case 'viewport-change':
-      case 'vc-change':
+      case "viewport-change":
+      case "vc-change":
       {
         let highlightBox = null;
         if (!this.highlightBox) {
           let doc = Utils.win.document;
           // Add highlight box
           highlightBox = Utils.win.document.
-            createElementNS('http://www.w3.org/1999/xhtml', 'div');
+            createElementNS("http://www.w3.org/1999/xhtml", "div");
           let parent = doc.body || doc.documentElement;
           parent.appendChild(highlightBox);
-          highlightBox.id = 'virtual-cursor-box';
+          highlightBox.id = "virtual-cursor-box";
 
           // Add highlight inset for inner shadow
           highlightBox.appendChild(
-            doc.createElementNS('http://www.w3.org/1999/xhtml', 'div'));
+            doc.createElementNS("http://www.w3.org/1999/xhtml", "div"));
 
           this.highlightBox = Cu.getWeakReference(highlightBox);
         } else {
           highlightBox = this.highlightBox.get();
         }
 
         let padding = aDetail.padding;
         let r = AccessFu.adjustContentBounds(aDetail.bounds, aBrowser, true);
 
         // First hide it to avoid flickering when changing the style.
-        highlightBox.classList.remove('show');
-        highlightBox.style.top = (r.top - padding) + 'px';
-        highlightBox.style.left = (r.left - padding) + 'px';
-        highlightBox.style.width = (r.width + padding*2) + 'px';
-        highlightBox.style.height = (r.height + padding*2) + 'px';
-        highlightBox.classList.add('show');
+        highlightBox.classList.remove("show");
+        highlightBox.style.top = (r.top - padding) + "px";
+        highlightBox.style.left = (r.left - padding) + "px";
+        highlightBox.style.width = (r.width + padding * 2) + "px";
+        highlightBox.style.height = (r.height + padding * 2) + "px";
+        highlightBox.classList.add("show");
 
         break;
       }
-      case 'tabstate-change':
+      case "tabstate-change":
       {
         let highlightBox = this.highlightBox ? this.highlightBox.get() : null;
         if (highlightBox) {
-          highlightBox.classList.remove('show');
+          highlightBox.classList.remove("show");
         }
         break;
       }
     }
   },
 
   Android: function Android(aDetails, aBrowser) {
     const ANDROID_VIEW_TEXT_CHANGED = 0x10;
     const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
 
     for (let androidEvent of aDetails) {
-      androidEvent.type = 'Accessibility:Event';
+      androidEvent.type = "Accessibility:Event";
       if (androidEvent.bounds) {
         androidEvent.bounds = AccessFu.adjustContentBounds(
           androidEvent.bounds, aBrowser);
       }
 
-      switch(androidEvent.eventType) {
+      switch (androidEvent.eventType) {
         case ANDROID_VIEW_TEXT_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustText(
             androidEvent.text);
           break;
         case ANDROID_VIEW_TEXT_SELECTION_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustSelection(
             androidEvent.brailleOutput);
           break;
@@ -611,140 +609,140 @@ var Output = {
           break;
       }
 
       Utils.win.WindowEventDispatcher.sendRequest(androidEvent);
     }
   },
 
   Braille: function Braille(aDetails) {
-    Logger.debug('Braille output: ' + aDetails.output);
+    Logger.debug("Braille output: " + aDetails.output);
   }
 };
 
 var Input = {
   editState: {},
 
   start: function start() {
     // XXX: This is too disruptive on desktop for now.
     // Might need to add special modifiers.
-    if (Utils.MozBuildApp != 'browser') {
-      Utils.win.document.addEventListener('keypress', this, true);
+    if (Utils.MozBuildApp != "browser") {
+      Utils.win.document.addEventListener("keypress", this, true);
     }
-    Utils.win.addEventListener('mozAccessFuGesture', this, true);
+    Utils.win.addEventListener("mozAccessFuGesture", this, true);
   },
 
   stop: function stop() {
-    if (Utils.MozBuildApp != 'browser') {
-      Utils.win.document.removeEventListener('keypress', this, true);
+    if (Utils.MozBuildApp != "browser") {
+      Utils.win.document.removeEventListener("keypress", this, true);
     }
-    Utils.win.removeEventListener('mozAccessFuGesture', this, true);
+    Utils.win.removeEventListener("mozAccessFuGesture", this, true);
   },
 
   handleEvent: function Input_handleEvent(aEvent) {
     try {
       switch (aEvent.type) {
-      case 'keypress':
+      case "keypress":
         this._handleKeypress(aEvent);
         break;
-      case 'mozAccessFuGesture':
+      case "mozAccessFuGesture":
         this._handleGesture(aEvent.detail);
         break;
       }
     } catch (x) {
       Logger.logException(x);
     }
   },
 
   _handleGesture: function _handleGesture(aGesture) {
     let gestureName = aGesture.type + aGesture.touches.length;
-    Logger.debug('Gesture', aGesture.type,
-                 '(fingers: ' + aGesture.touches.length + ')');
+    Logger.debug("Gesture", aGesture.type,
+                 "(fingers: " + aGesture.touches.length + ")");
 
     switch (gestureName) {
-      case 'dwell1':
-      case 'explore1':
-        this.moveToPoint('Simple', aGesture.touches[0].x,
+      case "dwell1":
+      case "explore1":
+        this.moveToPoint("Simple", aGesture.touches[0].x,
           aGesture.touches[0].y);
         break;
-      case 'doubletap1':
+      case "doubletap1":
         this.activateCurrent();
         break;
-      case 'doubletaphold1':
-        Utils.dispatchChromeEvent('accessibility-control', 'quicknav-menu');
+      case "doubletaphold1":
+        Utils.dispatchChromeEvent("accessibility-control", "quicknav-menu");
         break;
-      case 'swiperight1':
-        this.moveCursor('moveNext', 'Simple', 'gestures');
+      case "swiperight1":
+        this.moveCursor("moveNext", "Simple", "gestures");
         break;
-      case 'swipeleft1':
-        this.moveCursor('movePrevious', 'Simple', 'gesture');
+      case "swipeleft1":
+        this.moveCursor("movePrevious", "Simple", "gesture");
         break;
-      case 'swipeup1':
+      case "swipeup1":
         this.moveCursor(
-          'movePrevious', this.quickNavMode.current, 'gesture', true);
+          "movePrevious", this.quickNavMode.current, "gesture", true);
         break;
-      case 'swipedown1':
-        this.moveCursor('moveNext', this.quickNavMode.current, 'gesture', true);
+      case "swipedown1":
+        this.moveCursor("moveNext", this.quickNavMode.current, "gesture", true);
         break;
-      case 'exploreend1':
-      case 'dwellend1':
+      case "exploreend1":
+      case "dwellend1":
         this.activateCurrent(null, true);
         break;
-      case 'swiperight2':
+      case "swiperight2":
         if (aGesture.edge) {
-          Utils.dispatchChromeEvent('accessibility-control',
-            'edge-swipe-right');
+          Utils.dispatchChromeEvent("accessibility-control",
+            "edge-swipe-right");
           break;
         }
         this.sendScrollMessage(-1, true);
         break;
-      case 'swipedown2':
+      case "swipedown2":
         if (aGesture.edge) {
-          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-down');
+          Utils.dispatchChromeEvent("accessibility-control", "edge-swipe-down");
           break;
         }
         this.sendScrollMessage(-1);
         break;
-      case 'swipeleft2':
+      case "swipeleft2":
         if (aGesture.edge) {
-          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-left');
+          Utils.dispatchChromeEvent("accessibility-control", "edge-swipe-left");
           break;
         }
         this.sendScrollMessage(1, true);
         break;
-      case 'swipeup2':
+      case "swipeup2":
         if (aGesture.edge) {
-          Utils.dispatchChromeEvent('accessibility-control', 'edge-swipe-up');
+          Utils.dispatchChromeEvent("accessibility-control", "edge-swipe-up");
           break;
         }
         this.sendScrollMessage(1);
         break;
-      case 'explore2':
+      case "explore2":
         Utils.CurrentBrowser.contentWindow.scrollBy(
           -aGesture.deltaX, -aGesture.deltaY);
         break;
-      case 'swiperight3':
-        this.moveCursor('moveNext', this.quickNavMode.current, 'gesture');
+      case "swiperight3":
+        this.moveCursor("moveNext", this.quickNavMode.current, "gesture");
         break;
-      case 'swipeleft3':
-        this.moveCursor('movePrevious', this.quickNavMode.current, 'gesture');
+      case "swipeleft3":
+        this.moveCursor("movePrevious", this.quickNavMode.current, "gesture");
         break;
-      case 'swipedown3':
+      case "swipedown3":
         this.quickNavMode.next();
-        AccessFu.announce('quicknav_' + this.quickNavMode.current);
+        AccessFu.announce("quicknav_" + this.quickNavMode.current);
         break;
-      case 'swipeup3':
+      case "swipeup3":
         this.quickNavMode.previous();
-        AccessFu.announce('quicknav_' + this.quickNavMode.current);
+        AccessFu.announce("quicknav_" + this.quickNavMode.current);
         break;
-      case 'tripletap3':
-        Utils.dispatchChromeEvent('accessibility-control', 'toggle-shade');
+      case "tripletap3":
+        Utils.dispatchChromeEvent("accessibility-control", "toggle-shade");
         break;
-      case 'tap2':
-        Utils.dispatchChromeEvent('accessibility-control', 'toggle-pause');
+      case "tap2":
+        Utils.dispatchChromeEvent("accessibility-control", "toggle-pause");
         break;
     }
   },
 
   _handleKeypress: function _handleKeypress(aEvent) {
     let target = aEvent.target;
 
     // Ignore keys with modifiers so the content could take advantage of them.
@@ -759,60 +757,60 @@ var Input = {
         // If it was pressed with meta, pass the key on without the meta.
         if (this.editState.editing) {
           return;
         }
 
         let key = String.fromCharCode(aEvent.charCode);
         try {
           let [methodName, rule] = this.keyMap[key];
-          this.moveCursor(methodName, rule, 'keyboard');
+          this.moveCursor(methodName, rule, "keyboard");
         } catch (x) {
           return;
         }
         break;
       case aEvent.DOM_VK_RIGHT:
         if (this.editState.editing) {
           if (!this.editState.atEnd) {
             // Don't move forward if caret is not at end of entry.
             // XXX: Fix for rtl
             return;
-          } else {
-            target.blur();
           }
+          target.blur();
+
         }
         this.moveCursor(aEvent.shiftKey ?
-          'moveLast' : 'moveNext', 'Simple', 'keyboard');
+          "moveLast" : "moveNext", "Simple", "keyboard");
         break;
       case aEvent.DOM_VK_LEFT:
         if (this.editState.editing) {
           if (!this.editState.atStart) {
             // Don't move backward if caret is not at start of entry.
             // XXX: Fix for rtl
             return;
-          } else {
-            target.blur();
           }
+          target.blur();
+
         }
         this.moveCursor(aEvent.shiftKey ?
-          'moveFirst' : 'movePrevious', 'Simple', 'keyboard');
+          "moveFirst" : "movePrevious", "Simple", "keyboard");
         break;
       case aEvent.DOM_VK_UP:
         if (this.editState.multiline) {
           if (!this.editState.atStart) {
             // Don't blur content if caret is not at start of text area.
             return;
-          } else {
-            target.blur();
           }
+          target.blur();
+
         }
 
-        if (Utils.MozBuildApp == 'mobile/android') {
+        if (Utils.MozBuildApp == "mobile/android") {
           // Return focus to native Android browser chrome.
-          Utils.win.WindowEventDispatcher.dispatch('ToggleChrome:Focus');
+          Utils.win.WindowEventDispatcher.dispatch("ToggleChrome:Focus");
         }
         break;
       case aEvent.DOM_VK_RETURN:
         if (this.editState.editing) {
           return;
         }
         this.activateCurrent();
         break;
@@ -823,137 +821,137 @@ var Input = {
     aEvent.preventDefault();
     aEvent.stopPropagation();
   },
 
   moveToPoint: function moveToPoint(aRule, aX, aY) {
     // XXX: Bug 1013408 - There is no alignment between the chrome window's
     // viewport size and the content viewport size in Android. This makes
     // sending mouse events beyond its bounds impossible.
-    if (Utils.MozBuildApp === 'mobile/android') {
+    if (Utils.MozBuildApp === "mobile/android") {
       let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-      mm.sendAsyncMessage('AccessFu:MoveToPoint',
-        {rule: aRule, x: aX, y: aY, origin: 'top'});
+      mm.sendAsyncMessage("AccessFu:MoveToPoint",
+        {rule: aRule, x: aX, y: aY, origin: "top"});
     } else {
       let win = Utils.win;
-      Utils.winUtils.sendMouseEvent('mousemove',
+      Utils.winUtils.sendMouseEvent("mousemove",
         aX - win.mozInnerScreenX, aY - win.mozInnerScreenY, 0, 0, 0);
     }
   },
 
   moveCursor: function moveCursor(aAction, aRule, aInputType, aAdjustRange) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:MoveCursor',
+    mm.sendAsyncMessage("AccessFu:MoveCursor",
                         { action: aAction, rule: aRule,
-                          origin: 'top', inputType: aInputType,
+                          origin: "top", inputType: aInputType,
                           adjustRange: aAdjustRange });
   },
 
   androidScroll: function androidScroll(aDirection) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:AndroidScroll',
-                        { direction: aDirection, origin: 'top' });
+    mm.sendAsyncMessage("AccessFu:AndroidScroll",
+                        { direction: aDirection, origin: "top" });
   },
 
   moveByGranularity: function moveByGranularity(aDetails) {
     const GRANULARITY_PARAGRAPH = 8;
     const GRANULARITY_LINE = 4;
 
     if (!this.editState.editing) {
       if (aDetails.granularity & (GRANULARITY_PARAGRAPH | GRANULARITY_LINE)) {
-        this.moveCursor('move' + aDetails.direction, 'Simple', 'gesture');
+        this.moveCursor("move" + aDetails.direction, "Simple", "gesture");
         return;
       }
     } else {
       aDetails.atStart = this.editState.atStart;
       aDetails.atEnd = this.editState.atEnd;
     }
 
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    let type = this.editState.editing ? 'AccessFu:MoveCaret' :
-                                        'AccessFu:MoveByGranularity';
+    let type = this.editState.editing ? "AccessFu:MoveCaret" :
+                                        "AccessFu:MoveByGranularity";
     mm.sendAsyncMessage(type, aDetails);
   },
 
   activateCurrent: function activateCurrent(aData, aActivateIfKey = false) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    let offset = aData && typeof aData.keyIndex === 'number' ?
+    let offset = aData && typeof aData.keyIndex === "number" ?
                  aData.keyIndex - Output.brailleState.startOffset : -1;
 
-    mm.sendAsyncMessage('AccessFu:Activate',
+    mm.sendAsyncMessage("AccessFu:Activate",
                         {offset: offset, activateIfKey: aActivateIfKey});
   },
 
   sendContextMenuMessage: function sendContextMenuMessage() {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:ContextMenu', {});
+    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);
   },
 
   sendScrollMessage: function sendScrollMessage(aPage, aHorizontal) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
-    mm.sendAsyncMessage('AccessFu:Scroll',
-      {page: aPage, horizontal: aHorizontal, origin: 'top'});
+    mm.sendAsyncMessage("AccessFu:Scroll",
+      {page: aPage, horizontal: aHorizontal, origin: "top"});
   },
 
   doScroll: function doScroll(aDetails) {
     let horizontal = aDetails.horizontal;
     let page = aDetails.page;
     let p = AccessFu.adjustContentBounds(
       aDetails.bounds, Utils.CurrentBrowser, true).center();
     Utils.winUtils.sendWheelEvent(p.x, p.y,
       horizontal ? page : 0, horizontal ? 0 : page, 0,
       Utils.win.WheelEvent.DOM_DELTA_PAGE, 0, 0, 0, 0);
   },
 
   get keyMap() {
     delete this.keyMap;
     this.keyMap = {
-      a: ['moveNext', 'Anchor'],
-      A: ['movePrevious', 'Anchor'],
-      b: ['moveNext', 'Button'],
-      B: ['movePrevious', 'Button'],
-      c: ['moveNext', 'Combobox'],
-      C: ['movePrevious', 'Combobox'],
-      d: ['moveNext', 'Landmark'],
-      D: ['movePrevious', 'Landmark'],
-      e: ['moveNext', 'Entry'],
-      E: ['movePrevious', 'Entry'],
-      f: ['moveNext', 'FormElement'],
-      F: ['movePrevious', 'FormElement'],
-      g: ['moveNext', 'Graphic'],
-      G: ['movePrevious', 'Graphic'],
-      h: ['moveNext', 'Heading'],
-      H: ['movePrevious', 'Heading'],
-      i: ['moveNext', 'ListItem'],
-      I: ['movePrevious', 'ListItem'],
-      k: ['moveNext', 'Link'],
-      K: ['movePrevious', 'Link'],
-      l: ['moveNext', 'List'],
-      L: ['movePrevious', 'List'],
-      p: ['moveNext', 'PageTab'],
-      P: ['movePrevious', 'PageTab'],
-      r: ['moveNext', 'RadioButton'],
-      R: ['movePrevious', 'RadioButton'],
-      s: ['moveNext', 'Separator'],
-      S: ['movePrevious', 'Separator'],
-      t: ['moveNext', 'Table'],
-      T: ['movePrevious', 'Table'],
-      x: ['moveNext', 'Checkbox'],
-      X: ['movePrevious', 'Checkbox']
+      a: ["moveNext", "Anchor"],
+      A: ["movePrevious", "Anchor"],
+      b: ["moveNext", "Button"],
+      B: ["movePrevious", "Button"],
+      c: ["moveNext", "Combobox"],
+      C: ["movePrevious", "Combobox"],
+      d: ["moveNext", "Landmark"],
+      D: ["movePrevious", "Landmark"],
+      e: ["moveNext", "Entry"],
+      E: ["movePrevious", "Entry"],
+      f: ["moveNext", "FormElement"],
+      F: ["movePrevious", "FormElement"],
+      g: ["moveNext", "Graphic"],
+      G: ["movePrevious", "Graphic"],
+      h: ["moveNext", "Heading"],
+      H: ["movePrevious", "Heading"],
+      i: ["moveNext", "ListItem"],
+      I: ["movePrevious", "ListItem"],
+      k: ["moveNext", "Link"],
+      K: ["movePrevious", "Link"],
+      l: ["moveNext", "List"],
+      L: ["movePrevious", "List"],
+      p: ["moveNext", "PageTab"],
+      P: ["movePrevious", "PageTab"],
+      r: ["moveNext", "RadioButton"],
+      R: ["movePrevious", "RadioButton"],
+      s: ["moveNext", "Separator"],
+      S: ["movePrevious", "Separator"],
+      t: ["moveNext", "Table"],
+      T: ["movePrevious", "Table"],
+      x: ["moveNext", "Checkbox"],
+      X: ["movePrevious", "Checkbox"]
     };
 
     return this.keyMap;
   },
 
   quickNavMode: {
     get current() {
       return this.modes[this._currentIndex];
@@ -968,21 +966,21 @@ var Input = {
     next: function quickNavMode_next() {
       Services.prefs.setIntPref(QUICKNAV_INDEX_PREF,
         this._currentIndex + 1 >= this.modes.length ?