summaryrefslogtreecommitdiffstats
path: root/accessible/tests/browser
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/browser')
-rw-r--r--accessible/tests/browser/.eslintrc.js218
-rw-r--r--accessible/tests/browser/browser.ini17
-rw-r--r--accessible/tests/browser/browser_shutdown_multi_reference.js48
-rw-r--r--accessible/tests/browser/browser_shutdown_parent_own_reference.js72
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_no_reference.js48
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_only.js40
-rw-r--r--accessible/tests/browser/browser_shutdown_remote_own_reference.js75
-rw-r--r--accessible/tests/browser/browser_shutdown_scope_lifecycle.js21
-rw-r--r--accessible/tests/browser/browser_shutdown_start_restart.js41
-rw-r--r--accessible/tests/browser/e10s/browser.ini51
-rw-r--r--accessible/tests/browser/e10s/browser_caching_attributes.js117
-rw-r--r--accessible/tests/browser/e10s/browser_caching_description.js164
-rw-r--r--accessible/tests/browser/e10s/browser_caching_name.js434
-rw-r--r--accessible/tests/browser/e10s/browser_caching_relations.js86
-rw-r--r--accessible/tests/browser/e10s/browser_caching_states.js120
-rw-r--r--accessible/tests/browser/e10s/browser_caching_value.js155
-rw-r--r--accessible/tests/browser/e10s/browser_events_caretmove.js21
-rw-r--r--accessible/tests/browser/e10s/browser_events_hide.js35
-rw-r--r--accessible/tests/browser/e10s/browser_events_show.js17
-rw-r--r--accessible/tests/browser/e10s/browser_events_statechange.js62
-rw-r--r--accessible/tests/browser/e10s/browser_events_textchange.js74
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js43
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js318
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_canvas.js25
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js64
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_doc.js312
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_gencontent.js78
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_hidden.js30
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_imagemap.js176
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_list.js43
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js39
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_listener.js29
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_optgroup.js91
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_removal.js39
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_table.js51
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_textleaf.js35
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_visibility.js196
-rw-r--r--accessible/tests/browser/e10s/browser_treeupdate_whitespace.js71
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html23
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html44
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_imagemap.html21
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml11
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_visibility.html78
-rw-r--r--accessible/tests/browser/e10s/doc_treeupdate_whitespace.html10
-rw-r--r--accessible/tests/browser/e10s/events.js127
-rw-r--r--accessible/tests/browser/e10s/head.js84
-rw-r--r--accessible/tests/browser/head.js116
-rw-r--r--accessible/tests/browser/shared-head.js229
48 files changed, 4299 insertions, 0 deletions
diff --git a/accessible/tests/browser/.eslintrc.js b/accessible/tests/browser/.eslintrc.js
new file mode 100644
index 000000000..53425abc4
--- /dev/null
+++ b/accessible/tests/browser/.eslintrc.js
@@ -0,0 +1,218 @@
+"use strict";
+
+module.exports = { // eslint-disable-line no-undef
+ "extends": [
+ "../../../testing/mochitest/browser.eslintrc.js"
+ ],
+ // All globals made available in the test environment.
+ "globals": {
+ // Content scripts have global 'content' object
+ "content": true,
+
+ "add_task": true,
+
+ // Defined in accessible/tests/mochitest/ common.js, name.js, states.js
+ "prettyName": true,
+ "statesToString": true,
+ "eventTypeToString": true,
+ "testAttrs": true,
+ "testAbsentAttrs": true,
+ "testName": true,
+ "testDescr": true,
+ "testStates": true,
+ "testRelation": true,
+ "testValue": true,
+ "testAccessibleTree": true,
+ "isAccessible": true,
+ "getAccessibleDOMNodeID": true,
+
+ // Defined for all top level accessibility browser tests.
+ "setE10sPrefs": true,
+ "unsetE10sPrefs": true,
+ "initPromise": true,
+ "shutdownPromise": true,
+ "forceGC": true,
+
+ // Defined for all e10s accessibility browser tests.
+ "addAccessibleTask": true,
+ "BrowserTestUtils": true,
+ "ContentTask": true,
+ "gBrowser": true,
+ "isDefunct": true,
+ "loadScripts": true,
+ "loadFrameScripts": true,
+ "Logger": true,
+ "MOCHITESTS_DIR": true,
+ "waitForEvent": true,
+ "waitForMultipleEvents": true,
+ "invokeSetAttribute": true,
+ "invokeSetStyle": true,
+ "invokeFocus": true,
+ "findAccessibleChildByID": true
+ },
+ "rules": {
+ "mozilla/no-aArgs": "warn",
+ "mozilla/no-cpows-in-tests": "warn",
+ "mozilla/reject-importGlobalProperties": "warn",
+ "mozilla/var-only-at-top-level": "warn",
+
+ "block-scoped-var": "error",
+ "brace-style": ["error", "1tbs"],
+ "camelcase": "error",
+ "comma-dangle": ["error", "never"],
+ "comma-spacing": "error",
+ "comma-style": ["error", "last"],
+ "complexity": ["error", 35],
+ "consistent-this": "off",
+ "curly": ["error", "multi-line"],
+ "default-case": "off",
+ "dot-location": ["error", "property"],
+ "dot-notation": "error",
+ "eol-last": "error",
+ "eqeqeq": "off",
+ "func-names": "off",
+ "func-style": "off",
+ "generator-star": "off",
+ "global-strict": "off",
+ "handle-callback-err": ["error", "er"],
+ "indent": ["error", 2, {"SwitchCase": 1}],
+ "key-spacing": ["error", {"beforeColon": false, "afterColon": true}],
+ "linebreak-style": "off",
+ "max-depth": "off",
+ "max-nested-callbacks": ["error", 4],
+ "max-params": "off",
+ "max-statements": "off",
+ "new-cap": ["error", {"capIsNew": false}],
+ "new-parens": "error",
+ "no-array-constructor": "error",
+ "no-bitwise": "off",
+ "no-caller": "error",
+ "no-catch-shadow": "error",
+ "no-comma-dangle": "off",
+ "no-cond-assign": "error",
+ "no-console": "off",
+ "no-constant-condition": "off",
+ "no-continue": "off",
+ "no-control-regex": "error",
+ "no-debugger": "error",
+ "no-delete-var": "error",
+ "no-div-regex": "off",
+ "no-dupe-args": "error",
+ "no-dupe-keys": "error",
+ "no-duplicate-case": "error",
+ "no-else-return": "error",
+ "no-empty": "error",
+ "no-empty-character-class": "error",
+ "no-eval": "error",
+ "no-ex-assign": "error",
+ "no-extend-native": "error",
+ "no-extra-bind": "error",
+ "no-extra-boolean-cast": "error",
+ "no-extra-parens": "off",
+ "no-extra-semi": "error",
+ "no-extra-strict": "off",
+ "no-fallthrough": "error",
+ "no-floating-decimal": "off",
+ "no-inline-comments": "off",
+ "no-lonely-if": "error",
+ "no-mixed-requires": "off",
+ "no-mixed-spaces-and-tabs": "error",
+ "no-multi-spaces": "error",
+ "no-multi-str": "error",
+ "no-multiple-empty-lines": ["error", {"max": 1}],
+ "no-native-reassign": "error",
+ "no-nested-ternary": "error",
+ "no-new-require": "off",
+ "no-octal": "error",
+ "no-param-reassign": "off",
+ "no-path-concat": "off",
+ "no-plusplus": "off",
+ "no-process-env": "off",
+ "no-process-exit": "off",
+ "no-proto": "error",
+ "no-redeclare": "error",
+ "no-regex-spaces": "error",
+ "no-reserved-keys": "off",
+ "no-restricted-modules": "off",
+ "no-return-assign": "error",
+ "no-script-url": "off",
+ "no-self-compare": "error",
+ "no-sequences": "error",
+ "no-shadow": "error",
+ "no-shadow-restricted-names": "error",
+ "no-space-before-semi": "off",
+ "no-spaced-func": "error",
+ "no-sparse-arrays": "error",
+ "no-sync": "off",
+ "no-ternary": "off",
+ "no-throw-literal": "error",
+ "no-trailing-spaces": "error",
+ "no-undef": "error",
+ "no-underscore-dangle": "off",
+ "no-undefined": "off",
+ "no-unneeded-ternary": "error",
+ "no-unreachable": "error",
+ "no-unused-vars": ["error", {"vars": "all", "args": "none"}],
+ "no-use-before-define": "off",
+ "no-var": "off",
+ "no-warning-comments": "off",
+ "no-with": "error",
+ "object-shorthand": "off",
+ "one-var": ["error", "never"],
+ "padded-blocks": ["error", "never"],
+ "quote-props": "off",
+ "radix": "error",
+ "semi": ["error", "always"],
+ "semi-spacing": ["error", {"before": false, "after": true}],
+ "sort-vars": "off",
+ "space-after-function-name": "off",
+ "keyword-spacing": "error",
+ "space-before-blocks": "error",
+ "space-before-function-parentheses": "off",
+ "space-before-function-paren": ["error", "never"],
+ "space-in-brackets": "off",
+ "space-in-parens": ["error", "never"],
+ "space-infix-ops": ["error", {"int32Hint": true}],
+ "space-unary-ops": ["error", { "words": true, "nonwords": false }],
+ "space-unary-word-ops": "off",
+ "spaced-comment": ["error", "always"],
+ "strict": ["error", "global"],
+ "use-isnan": "error",
+ "valid-jsdoc": "off",
+ "valid-typeof": "error",
+ "vars-on-top": "off",
+ "wrap-iife": "off",
+ "wrap-regex": "off",
+ "yoda": "error",
+
+ "guard-for-in": "off",
+ "newline-after-var": "off",
+ "no-alert": "off",
+ "no-eq-null": "off",
+ "no-func-assign": "off",
+ "no-implied-eval": "off",
+ "no-inner-declarations": "off",
+ "no-invalid-regexp": "off",
+ "no-irregular-whitespace": "off",
+ "no-iterator": "off",
+ "no-label-var": "off",
+ "no-labels": "error",
+ "no-lone-blocks": "off",
+ "no-loop-func": "off",
+ "no-negated-in-lhs": "off",
+ "no-new": "off",
+ "no-new-func": "off",
+ "no-new-object": "off",
+ "no-new-wrappers": "off",
+ "no-obj-calls": "off",
+ "no-octal-escape": "off",
+ "no-undef-init": "error",
+ "no-unexpected-multiline": "error",
+ "object-curly-spacing": "off",
+ "no-unused-expressions": "off",
+ "no-void": "off",
+ "no-wrap-func": "off",
+ "operator-assignment": "off",
+ "operator-linebreak": ["error", "after"]
+ }
+};
diff --git a/accessible/tests/browser/browser.ini b/accessible/tests/browser/browser.ini
new file mode 100644
index 000000000..402deda24
--- /dev/null
+++ b/accessible/tests/browser/browser.ini
@@ -0,0 +1,17 @@
+[DEFAULT]
+
+support-files =
+ head.js
+ shared-head.js
+
+[browser_shutdown_multi_reference.js]
+[browser_shutdown_parent_own_reference.js]
+skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_remote_no_reference.js]
+skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_remote_only.js]
+skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_remote_own_reference.js]
+skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+[browser_shutdown_scope_lifecycle.js]
+[browser_shutdown_start_restart.js]
diff --git a/accessible/tests/browser/browser_shutdown_multi_reference.js b/accessible/tests/browser/browser_shutdown_multi_reference.js
new file mode 100644
index 000000000..e0ba3ce6b
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_multi_reference.js
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ info('Creating a service');
+ // Create a11y service.
+ let a11yInit = initPromise();
+ let accService1 = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield a11yInit;
+ ok(accService1, 'Service initialized');
+
+ // Add another reference to a11y service. This will not trigger
+ // 'a11y-init-or-shutdown' event
+ let accService2 = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService2, 'Service initialized');
+
+ info('Removing all service references');
+ let canShutdown = false;
+ // This promise will resolve only if canShutdonw flag is set to true. If
+ // 'a11y-init-or-shutdown' event with '0' flag comes before it can be shut
+ // down, the promise will reject.
+ let a11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise().then(flag => canShutdown ?
+ resolve() : reject('Accessible service was shut down incorrectly')));
+ // Remove first a11y service reference.
+ accService1 = null;
+ ok(!accService1, 'Service is removed');
+ // Force garbage collection that should not trigger shutdown because there is
+ // another reference.
+ forceGC();
+
+ // Have some breathing room when removing a11y service references.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ // Now allow a11y service to shutdown.
+ canShutdown = true;
+ // Remove last a11y service reference.
+ accService2 = null;
+ ok(!accService2, 'Service is removed');
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yShutdown;
+});
diff --git a/accessible/tests/browser/browser_shutdown_parent_own_reference.js b/accessible/tests/browser/browser_shutdown_parent_own_reference.js
new file mode 100644
index 000000000..9895bd2c7
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_parent_own_reference.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield Promise.all([parentA11yInit, contentA11yInit]);
+
+ info('Adding additional reference to accessibility service in content ' +
+ 'process');
+ // Add a new reference to the a11y service inside the content process.
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+
+ info('Trying to shut down a service in content and making sure it stays ' +
+ 'alive as it was started by parent');
+ let contentCanShutdown = false;
+ // This promise will resolve only if contentCanShutdown flag is set to true.
+ // If 'a11y-init-or-shutdown' event with '0' flag (in content) comes before
+ // it can be shut down, the promise will reject.
+ let contentA11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise(browser).then(flag => contentCanShutdown ?
+ resolve() : reject('Accessible service was shut down incorrectly')));
+ // Remove a11y service reference in content and force garbage collection.
+ // This should not trigger shutdown since a11y was originally initialized by
+ // the main process.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+
+ // Have some breathing room between a11y service shutdowns.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ info('Removing a service in parent');
+ // Now allow a11y service to shutdown in content.
+ contentCanShutdown = true;
+ // Remove the a11y service reference in the main process.
+ let parentA11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed in parent');
+ // Force garbage collection that should trigger shutdown in both parent and
+ // content.
+ forceGC();
+ yield Promise.all([parentA11yShutdown, contentA11yShutdown]);
+
+ // Unsetting e10s related preferences.
+ yield unsetE10sPrefs();
+ });
+});
diff --git a/accessible/tests/browser/browser_shutdown_remote_no_reference.js b/accessible/tests/browser/browser_shutdown_remote_no_reference.js
new file mode 100644
index 000000000..b066c2592
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_no_reference.js
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield Promise.all([parentA11yInit, contentA11yInit]);
+
+ info('Removing a service in parent and waiting for service to be shut ' +
+ 'down in content');
+ // Remove a11y service reference in the main process.
+ let parentA11yShutdown = shutdownPromise();
+ let contentA11yShutdown = shutdownPromise(browser);
+ accService = null;
+ ok(!accService, 'Service is removed in parent');
+ // Force garbage collection that should trigger shutdown in both main and
+ // content process.
+ forceGC();
+ yield Promise.all([parentA11yShutdown, contentA11yShutdown]);
+ });
+
+ // Unsetting e10s related preferences.
+ yield unsetE10sPrefs();
+});
diff --git a/accessible/tests/browser/browser_shutdown_remote_only.js b/accessible/tests/browser/browser_shutdown_remote_only.js
new file mode 100644
index 000000000..aab497678
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_only.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in content');
+ // Create a11y service in the content process.
+ let a11yInit = initPromise(browser);
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+ yield a11yInit;
+
+ info('Removing a service in content');
+ // Remove a11y service reference from the content process.
+ let a11yShutdown = shutdownPromise(browser);
+ // Force garbage collection that should trigger shutdown.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+ yield a11yShutdown;
+
+ // Unsetting e10s related preferences.
+ yield unsetE10sPrefs();
+ });
+});
diff --git a/accessible/tests/browser/browser_shutdown_remote_own_reference.js b/accessible/tests/browser/browser_shutdown_remote_own_reference.js
new file mode 100644
index 000000000..429737a81
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_remote_own_reference.js
@@ -0,0 +1,75 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Making sure that the e10s is enabled on Windows for testing.
+ yield setE10sPrefs();
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body></body>
+ </html>`
+ }, function*(browser) {
+ info('Creating a service in parent and waiting for service to be created ' +
+ 'in content');
+ // Create a11y service in the main process. This will trigger creating of
+ // the a11y service in parent as well.
+ let parentA11yInit = initPromise();
+ let contentA11yInit = initPromise(browser);
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized in parent');
+ yield Promise.all([parentA11yInit, contentA11yInit]);
+
+ info('Adding additional reference to accessibility service in content ' +
+ 'process');
+ // Add a new reference to the a11y service inside the content process.
+ loadFrameScripts(browser, `let accService = Components.classes[
+ '@mozilla.org/accessibilityService;1'].getService(
+ Components.interfaces.nsIAccessibilityService);`);
+
+ info('Shutting down a service in parent and making sure the one in ' +
+ 'content stays alive');
+ let contentCanShutdown = false;
+ let parentA11yShutdown = shutdownPromise();
+ // This promise will resolve only if contentCanShutdown flag is set to true.
+ // If 'a11y-init-or-shutdown' event with '0' flag (in content) comes before
+ // it can be shut down, the promise will reject.
+ let contentA11yShutdown = new Promise((resolve, reject) =>
+ shutdownPromise(browser).then(flag => contentCanShutdown ?
+ resolve() : reject('Accessible service was shut down incorrectly')));
+ // Remove a11y service reference in the main process and force garbage
+ // collection. This should not trigger shutdown in content since a11y
+ // service is used by XPCOM.
+ accService = null;
+ ok(!accService, 'Service is removed in parent');
+ // Force garbage collection that should not trigger shutdown because there
+ // is a reference in a content process.
+ forceGC();
+ loadFrameScripts(browser, `Components.utils.forceGC();`);
+ yield parentA11yShutdown;
+
+ // Have some breathing room between a11y service shutdowns.
+ yield new Promise(resolve => executeSoon(resolve));
+
+ info('Removing a service in content');
+ // Now allow a11y service to shutdown in content.
+ contentCanShutdown = true;
+ // Remove last reference to a11y service in content and force garbage
+ // collection that should trigger shutdown.
+ loadFrameScripts(browser, `accService = null; Components.utils.forceGC();`);
+ yield contentA11yShutdown;
+
+ // Unsetting e10s related preferences.
+ yield unsetE10sPrefs();
+ });
+});
diff --git a/accessible/tests/browser/browser_shutdown_scope_lifecycle.js b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js
new file mode 100644
index 000000000..be9c63d46
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_scope_lifecycle.js
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ // Create a11y service inside of the function scope. Its reference should be
+ // released once the anonimous function is called.
+ let a11yInitThenShutdown = initPromise().then(shutdownPromise);
+
+ (function() {
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ ok(accService, 'Service initialized');
+ })();
+
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yInitThenShutdown;
+});
diff --git a/accessible/tests/browser/browser_shutdown_start_restart.js b/accessible/tests/browser/browser_shutdown_start_restart.js
new file mode 100644
index 000000000..53bd4daee
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown_start_restart.js
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+add_task(function* () {
+ info('Creating a service');
+ // Create a11y service.
+ let a11yInit = initPromise();
+ let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield a11yInit;
+ ok(accService, 'Service initialized');
+
+ info('Removing a service');
+ // Remove the only reference to an a11y service.
+ let a11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed');
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yShutdown;
+
+ info('Recreating a service');
+ // Re-create a11y service.
+ a11yInit = initPromise();
+ accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+ Ci.nsIAccessibilityService);
+ yield a11yInit;
+ ok(accService, 'Service initialized again');
+
+ info('Removing a service again');
+ // Remove the only reference to an a11y service again.
+ a11yShutdown = shutdownPromise();
+ accService = null;
+ ok(!accService, 'Service is removed again');
+ // Force garbage collection that should trigger shutdown.
+ forceGC();
+ yield a11yShutdown;
+});
diff --git a/accessible/tests/browser/e10s/browser.ini b/accessible/tests/browser/e10s/browser.ini
new file mode 100644
index 000000000..e0ca44dde
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -0,0 +1,51 @@
+[DEFAULT]
+skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
+support-files =
+ events.js
+ head.js
+ doc_treeupdate_ariadialog.html
+ doc_treeupdate_ariaowns.html
+ doc_treeupdate_imagemap.html
+ doc_treeupdate_removal.xhtml
+ doc_treeupdate_visibility.html
+ doc_treeupdate_whitespace.html
+ !/accessible/tests/browser/shared-head.js
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+ !/accessible/tests/mochitest/moz.png
+
+# Caching tests
+[browser_caching_attributes.js]
+[browser_caching_description.js]
+[browser_caching_name.js]
+[browser_caching_relations.js]
+[browser_caching_states.js]
+[browser_caching_value.js]
+
+# Events tests
+[browser_events_caretmove.js]
+[browser_events_hide.js]
+[browser_events_show.js]
+[browser_events_statechange.js]
+[browser_events_textchange.js]
+
+# Tree update tests
+[browser_treeupdate_ariadialog.js]
+[browser_treeupdate_ariaowns.js]
+[browser_treeupdate_canvas.js]
+[browser_treeupdate_cssoverflow.js]
+[browser_treeupdate_doc.js]
+[browser_treeupdate_gencontent.js]
+[browser_treeupdate_hidden.js]
+[browser_treeupdate_imagemap.js]
+skip-if = e10s # Bug 1318569
+[browser_treeupdate_list.js]
+[browser_treeupdate_list_editabledoc.js]
+[browser_treeupdate_listener.js]
+[browser_treeupdate_optgroup.js]
+[browser_treeupdate_removal.js]
+[browser_treeupdate_table.js]
+[browser_treeupdate_textleaf.js]
+[browser_treeupdate_visibility.js]
+[browser_treeupdate_whitespace.js]
+skip-if = true # Failing due to incorrect index of test container children on document load.
diff --git a/accessible/tests/browser/e10s/browser_caching_attributes.js b/accessible/tests/browser/e10s/browser_caching_attributes.js
new file mode 100644
index 000000000..449ca9d91
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_attributes.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_FOCUS */
+
+loadScripts({ name: 'attributes.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Default textbox accessible attributes.
+ */
+const defaultAttributes = {
+ 'margin-top': '0px',
+ 'margin-right': '0px',
+ 'margin-bottom': '0px',
+ 'margin-left': '0px',
+ 'text-align': 'start',
+ 'text-indent': '0px',
+ 'id': 'textbox',
+ 'tag': 'input',
+ 'display': 'inline'
+};
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * expected {Object} expected attributes for given accessibles
+ * unexpected {Object} unexpected attributes for given accessibles
+ *
+ * action {?Function*} an optional action that yields a change in
+ * attributes
+ * attrs {?Array} an optional list of attributes to update
+ * waitFor {?Number} an optional event to wait for
+ * }
+ */
+const attributesTests = [{
+ desc: 'Initiall accessible attributes',
+ expected: defaultAttributes,
+ unexpected: {
+ 'line-number': '1',
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }
+}, {
+ desc: '@line-number attribute is present when textbox is focused',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'textbox');
+ },
+ waitFor: EVENT_FOCUS,
+ expected: Object.assign({}, defaultAttributes, { 'line-number': '1' }),
+ unexpected: {
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }
+}, {
+ desc: '@aria-live sets container-live and live attributes',
+ attrs: [{
+ attr: 'aria-live',
+ value: 'polite'
+ }],
+ expected: Object.assign({}, defaultAttributes, {
+ 'line-number': '1',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }),
+ unexpected: {
+ 'explicit-name': 'true'
+ }
+}, {
+ desc: '@title attribute sets explicit-name attribute to true',
+ attrs: [{
+ attr: 'title',
+ value: 'textbox'
+ }],
+ expected: Object.assign({}, defaultAttributes, {
+ 'line-number': '1',
+ 'explicit-name': 'true',
+ 'container-live': 'polite',
+ 'live': 'polite'
+ }),
+ unexpected: {}
+}];
+
+/**
+ * Test caching of accessible object attributes
+ */
+addAccessibleTask(`
+ <input id="textbox" value="hello">`,
+ function* (browser, accDoc) {
+ let textbox = findAccessibleChildByID(accDoc, 'textbox');
+ for (let { desc, action, attrs, expected, waitFor, unexpected } of attributesTests) {
+ info(desc);
+ let onUpdate;
+
+ if (waitFor) {
+ onUpdate = waitForEvent(waitFor, 'textbox');
+ }
+
+ if (action) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, 'textbox', attr, value);
+ }
+ }
+
+ yield onUpdate;
+ testAttrs(textbox, expected);
+ testAbsentAttrs(textbox, unexpected);
+ }
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_caching_description.js b/accessible/tests/browser/e10s/browser_caching_description.js
new file mode 100644
index 000000000..18ee58bd0
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_description.js
@@ -0,0 +1,164 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_REORDER */
+
+loadScripts({ name: 'name.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * expected {String} expected description value for a given accessible
+ * attrs {?Array} an optional list of attributes to update
+ * waitFor {?Array} an optional list of accessible events to wait for when
+ * attributes are updated
+ * }
+ */
+const tests = [{
+ desc: 'No description when there are no @alt, @title and @aria-describedby',
+ expected: ''
+}, {
+ desc: 'Description from @aria-describedby attribute',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: 'aria description'
+}, {
+ desc: 'No description from @aria-describedby since it is the same as the ' +
+ '@alt attribute which is used as the name',
+ attrs: [{
+ attr: 'alt',
+ value: 'aria description'
+ }],
+ waitFor: [{ eventType: EVENT_REORDER, id: 'body' }],
+ expected: ''
+}, {
+ desc: 'Description from @aria-describedby attribute when @alt and ' +
+ '@aria-describedby are not the same',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description2'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: 'another description'
+}, {
+ desc: 'Description from @aria-describedby attribute when @title (used for ' +
+ 'name) and @aria-describedby are not the same',
+ attrs: [{
+ attr: 'alt'
+ }, {
+ attr: 'title',
+ value: 'title'
+ }],
+ waitFor: [{ eventType: EVENT_REORDER, id: 'body' }],
+ expected: 'another description'
+}, {
+ desc: 'No description from @aria-describedby since it is the same as the ' +
+ '@title attribute which is used as the name',
+ attrs: [{
+ attr: 'title',
+ value: 'another description'
+ }],
+ waitFor: [{ eventType: EVENT_NAME_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'No description with only @title attribute which is used as the name',
+ attrs: [{
+ attr: 'aria-describedby'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'Description from @title attribute when @alt and @atitle are not the ' +
+ 'same',
+ attrs: [{
+ attr: 'alt',
+ value: 'aria description'
+ }],
+ waitFor: [{ eventType: EVENT_REORDER, id: 'body' }],
+ expected: 'another description'
+}, {
+ desc: 'No description from @title since it is the same as the @alt ' +
+ 'attribute which is used as the name',
+ attrs: [{
+ attr: 'alt',
+ value: 'another description'
+ }],
+ waitFor: [{ eventType: EVENT_NAME_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'No description from @aria-describedby since it is the same as the ' +
+ '@alt (used for name) and @title attributes',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description2'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'Description from @aria-describedby attribute when it is different ' +
+ 'from @alt (used for name) and @title attributes',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: 'aria description'
+}, {
+ desc: 'No description from @aria-describedby since it is the same as the ' +
+ '@alt attribute (used for name) but different from title',
+ attrs: [{
+ attr: 'alt',
+ value: 'aria description'
+ }],
+ waitFor: [{ eventType: EVENT_NAME_CHANGE, id: 'image' }],
+ expected: ''
+}, {
+ desc: 'Description from @aria-describedby attribute when @alt (used for ' +
+ 'name) and @aria-describedby are not the same but @title and ' +
+ 'aria-describedby are',
+ attrs: [{
+ attr: 'aria-describedby',
+ value: 'description2'
+ }],
+ waitFor: [{ eventType: EVENT_DESCRIPTION_CHANGE, id: 'image' }],
+ expected: 'another description'
+}];
+
+/**
+ * Test caching of accessible object description
+ */
+addAccessibleTask(`
+ <p id="description">aria description</p>
+ <p id="description2">another description</p>
+ <img id="image" />`,
+ function*(browser, accDoc) {
+ let imgAcc = findAccessibleChildByID(accDoc, 'image');
+
+ for (let { desc, waitFor, attrs, expected } of tests) {
+ info(desc);
+ let onUpdate;
+ if (waitFor) {
+ onUpdate = waitForMultipleEvents(waitFor);
+ }
+ if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, 'image', attr, value);
+ }
+ }
+ yield onUpdate;
+ // When attribute change (alt) triggers reorder event, accessible will
+ // become defunct.
+ if (isDefunct(imgAcc)) {
+ imgAcc = findAccessibleChildByID(accDoc, 'image');
+ }
+ testDescr(imgAcc, expected);
+ }
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_caching_name.js b/accessible/tests/browser/e10s/browser_caching_name.js
new file mode 100644
index 000000000..08e635014
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_name.js
@@ -0,0 +1,434 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER, EVENT_TEXT_INSERTED */
+
+loadScripts({ name: 'name.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Rules for name tests that are inspired by
+ * accessible/tests/mochitest/name/markuprules.xul
+ *
+ * Each element in the list of rules represents a name calculation rule for a
+ * particular test case.
+ *
+ * The rules have the following format:
+ * { attr } - calculated from attribute
+ * { elm } - calculated from another element
+ * { fromsubtree } - calculated from element's subtree
+ *
+ *
+ * Options include:
+ * * recreated - subrtee is recreated and the test should only continue
+ * after a reorder event
+ * * textchanged - text is inserted into a subtree and the test should only
+ * continue after a text inserted event
+ */
+const ARIARule = [{ attr: 'aria-labelledby' }, { attr: 'aria-label' }];
+const HTMLControlHeadRule = [...ARIARule, { elm: 'label', isSibling: true }];
+const rules = {
+ CSSContent: [{ elm: 'style', isSibling: true }, { fromsubtree: true }],
+ HTMLARIAGridCell: [...ARIARule, { fromsubtree: true }, { attr: 'title' }],
+ HTMLControl: [...HTMLControlHeadRule, { fromsubtree: true },
+ { attr: 'title' }],
+ HTMLElm: [...ARIARule, { attr: 'title' }],
+ HTMLImg: [...ARIARule, { attr: 'alt', recreated: true }, { attr: 'title' }],
+ HTMLImgEmptyAlt: [...ARIARule, { attr: 'title' }, { attr: 'alt' }],
+ HTMLInputButton: [...HTMLControlHeadRule, { attr: 'value' },
+ { attr: 'title' }],
+ HTMLInputImage: [...HTMLControlHeadRule, { attr: 'alt', recreated: true },
+ { attr: 'value', recreated: true }, { attr: 'title' }],
+ HTMLInputImageNoValidSrc: [...HTMLControlHeadRule,
+ { attr: 'alt', recreated: true }, { attr: 'value', recreated: true }],
+ HTMLInputReset: [...HTMLControlHeadRule,
+ { attr: 'value', textchanged: true }],
+ HTMLInputSubmit: [...HTMLControlHeadRule,
+ { attr: 'value', textchanged: true }],
+ HTMLLink: [...ARIARule, { fromsubtree: true }, { attr: 'title' }],
+ HTMLLinkImage: [...ARIARule, { elm: 'img' }, { attr: 'title' }],
+ HTMLOption: [...ARIARule, { attr: 'label' }, { fromsubtree: true },
+ { attr: 'title' }],
+ HTMLTable: [...ARIARule, { elm: 'caption' }, { attr: 'summary' },
+ { attr: 'title' }]
+};
+
+const markupTests = [{
+ id: 'btn',
+ ruleset: 'HTMLControl',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn">test4</label>
+ <button id="btn"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5">press me</button>`,
+ expected: ['test2 test3', 'test1', 'test4', 'press me', 'test5']
+}, {
+ id: 'btn',
+ ruleset: 'HTMLInputButton',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn">test4</label>
+ <input id="btn"
+ type="button"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from al"
+ src="no name from src"
+ data="no name from data"
+ title="name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value',
+ 'name from title']
+}, {
+ id: 'btn-submit',
+ ruleset: 'HTMLInputSubmit',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-submit">test4</label>
+ <input id="btn-submit"
+ type="submit"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from atl"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value']
+}, {
+ id: 'btn-reset',
+ ruleset: 'HTMLInputReset',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-reset">test4</label>
+ <input id="btn-reset"
+ type="reset"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ value="name from value"
+ alt="no name from alt"
+ src="no name from src"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from value']
+}, {
+ id: 'btn-image',
+ ruleset: 'HTMLInputImage',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-image">test4</label>
+ <input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"
+ data="no name from data"
+ title="name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from alt',
+ 'name from value', 'name from title']
+}, {
+ id: 'btn-image',
+ ruleset: 'HTMLInputImageNoValidSrc',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="btn-image">test4</label>
+ <input id="btn-image"
+ type="image"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ alt="name from alt"
+ value="name from value"
+ data="no name from data"
+ title="no name from title"/>`,
+ expected: ['test2 test3', 'test1', 'test4', 'name from alt',
+ 'name from value']
+}, {
+ id: 'opt',
+ ruleset: 'HTMLOption',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <select>
+ <option id="opt"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ label="test4"
+ title="test5">option1</option>
+ <option>option2</option>
+ </select>`,
+ expected: ['test2 test3', 'test1', 'test4', 'option1', 'test5']
+}, {
+ id: 'img',
+ ruleset: 'HTMLImg',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <img id="img"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ alt="Mozilla logo"
+ title="This is a logo"
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
+ expected: ['test2 test3', 'Logo of Mozilla', 'Mozilla logo', 'This is a logo']
+}, {
+ id: 'imgemptyalt',
+ ruleset: 'HTMLImgEmptyAlt',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <img id="imgemptyalt"
+ aria-label="Logo of Mozilla"
+ aria-labelledby="l1 l2"
+ title="This is a logo"
+ alt=""
+ src="http://example.com/a11y/accessible/tests/mochitest/moz.png"/>`,
+ expected: ['test2 test3', 'Logo of Mozilla', 'This is a logo', '']
+}, {
+ id: 'tc',
+ ruleset: 'HTMLElm',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="tc">test4</label>
+ <table>
+ <tr>
+ <td id="tc"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test5">
+ <p>This is a paragraph</p>
+ <a href="#">This is a link</a>
+ <ul>
+ <li>This is a list</li>
+ </ul>
+ </td>
+ </tr>
+ </table>`,
+ expected: ['test2 test3', 'test1', 'test5']
+}, {
+ id: 'gc',
+ ruleset: 'HTMLARIAGridCell',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <label for="gc">test4</label>
+ <table>
+ <tr>
+ <td id="gc"
+ role="gridcell"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="This is a paragraph This is a link This is a list">
+ <p>This is a paragraph</p>
+ <a href="#">This is a link</a>
+ <ul>
+ <li>Listitem1</li>
+ <li>Listitem2</li>
+ </ul>
+ </td>
+ </tr>
+ </table>`,
+ expected: ['test2 test3', 'test1', 'This is a paragraph',
+ 'This is a paragraph This is a link This is a list']
+}, {
+ id: 't',
+ ruleset: 'HTMLTable',
+ markup: `
+ <span id="l1">lby_tst6_1</span>
+ <span id="l2">lby_tst6_2</span>
+ <label for="t">label_tst6</label>
+ <table id="t"
+ aria-label="arialabel_tst6"
+ aria-labelledby="l1 l2"
+ summary="summary_tst6"
+ title="title_tst6">
+ <caption>caption_tst6</caption>
+ <tr>
+ <td>cell1</td>
+ <td>cell2</td>
+ </tr>
+ </table>`,
+ expected: ['lby_tst6_1 lby_tst6_2', 'arialabel_tst6', 'caption_tst6',
+ 'summary_tst6', 'title_tst6']
+}, {
+ id: 'btn',
+ ruleset: 'CSSContent',
+ markup: `
+ <style>
+ button::before {
+ content: "do not ";
+ }
+ </style>
+ <button id="btn">press me</button>`,
+ expected: ['do not press me', 'press me']
+}, {
+ // TODO: uncomment when Bug-1256382 is resoved.
+ // id: 'li',
+ // ruleset: 'CSSContent',
+ // markup: `
+ // <style>
+ // ul {
+ // list-style-type: decimal;
+ // }
+ // </style>
+ // <ul id="ul">
+ // <li id="li">Listitem</li>
+ // </ul>`,
+ // expected: ['1. Listitem', `${String.fromCharCode(0x2022)} Listitem`]
+// }, {
+ id: 'a',
+ ruleset: 'HTMLLink',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <a id="a"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test4">test5</a>`,
+ expected: ['test2 test3', 'test1', 'test5', 'test4']
+}, {
+ id: 'a-img',
+ ruleset: 'HTMLLinkImage',
+ markup: `
+ <span id="l1">test2</span>
+ <span id="l2">test3</span>
+ <a id="a-img"
+ aria-label="test1"
+ aria-labelledby="l1 l2"
+ title="test4"><img alt="test5"/></a>`,
+ expected: ['test2 test3', 'test1', 'test5', 'test4']
+}];
+
+/**
+ * Wait for an accessible event to happen and, in case given accessible is
+ * defunct, update it to one that is attached to the accessible event.
+ * @param {Promise} onEvent accessible event promise
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ */
+function* updateAccessibleIfNeeded(onEvent, target) {
+ let event = yield onEvent;
+ if (isDefunct(target.acc)) {
+ target.acc = findAccessibleChildByID(event.accessible, target.id);
+ }
+}
+
+/**
+ * Test accessible name that is calculated from an attribute, remove the
+ * attribute before proceeding to the next name test. If attribute removal
+ * results in a reorder or text inserted event - wait for it. If accessible
+ * becomes defunct, update its reference using the one that is attached to one
+ * of the above events.
+ * @param {Object} browser current "tabbrowser" element
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ * @param {Object} rule current attr rule for name calculation
+ * @param {[type]} expected expected name value
+ */
+function* testAttrRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent;
+ if (rule.recreated) {
+ onEvent = waitForEvent(EVENT_REORDER, target.parent);
+ } else if (rule.textchanged) {
+ onEvent = waitForEvent(EVENT_TEXT_INSERTED, target.id);
+ }
+ yield invokeSetAttribute(browser, target.id, rule.attr);
+ if (onEvent) {
+ yield updateAccessibleIfNeeded(onEvent, target);
+ }
+}
+
+/**
+ * Test accessible name that is calculated from an element name, remove the
+ * element before proceeding to the next name test. If element removal results
+ * in a reorder event - wait for it. If accessible becomes defunct, update its
+ * reference using the one that is attached to a possible reorder event.
+ * @param {Object} browser current "tabbrowser" element
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ * @param {Object} rule current elm rule for name calculation
+ * @param {[type]} expected expected name value
+ */
+function* testElmRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent = waitForEvent(EVENT_REORDER, rule.isSibling ?
+ target.parent : target.id);
+ yield ContentTask.spawn(browser, rule.elm, elm =>
+ content.document.querySelector(`${elm}`).remove());
+ yield updateAccessibleIfNeeded(onEvent, target);
+}
+
+/**
+ * Test accessible name that is calculated from its subtree, remove the subtree
+ * and wait for a reorder event before proceeding to the next name test. If
+ * accessible becomes defunct, update its reference using the one that is
+ * attached to a reorder event.
+ * @param {Object} browser current "tabbrowser" element
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ * @param {Object} rule current subtree rule for name calculation
+ * @param {[type]} expected expected name value
+ */
+function* testSubtreeRule(browser, target, rule, expected) {
+ testName(target.acc, expected);
+ let onEvent = waitForEvent(EVENT_REORDER, target.id);
+ yield ContentTask.spawn(browser, target.id, id => {
+ let elm = content.document.getElementById(id);
+ while (elm.firstChild) {
+ elm.removeChild(elm.firstChild);
+ }
+ });
+ yield updateAccessibleIfNeeded(onEvent, target);
+}
+
+/**
+ * Iterate over a list of rules and test accessible names for each one of the
+ * rules.
+ * @param {Object} browser current "tabbrowser" element
+ * @param {Object} target { acc, parent, id } structure that contains an
+ * accessible, its parent and its content element
+ * id.
+ * @param {Array} ruleset A list of rules to test a target with
+ * @param {Array} expected A list of expected name value for each rule
+ */
+function* testNameRule(browser, target, ruleset, expected) {
+ for (let i = 0; i < ruleset.length; ++i) {
+ let rule = ruleset[i];
+ let testFn;
+ if (rule.attr) {
+ testFn = testAttrRule;
+ } else if (rule.elm) {
+ testFn = testElmRule;
+ } else if (rule.fromsubtree) {
+ testFn = testSubtreeRule;
+ }
+ yield testFn(browser, target, rule, expected[i]);
+ }
+}
+
+markupTests.forEach(({ id, ruleset, markup, expected }) =>
+ addAccessibleTask(markup, function*(browser, accDoc) {
+ // Find a target accessible from an accessible subtree.
+ let acc = findAccessibleChildByID(accDoc, id);
+ // Find target's parent accessible from an accessible subtree.
+ let parent = getAccessibleDOMNodeID(acc.parent);
+ let target = { id, parent, acc };
+ yield testNameRule(browser, target, rules[ruleset], expected);
+ }));
diff --git a/accessible/tests/browser/e10s/browser_caching_relations.js b/accessible/tests/browser/e10s/browser_caching_relations.js
new file mode 100644
index 000000000..772aee96a
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_relations.js
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global RELATION_LABELLED_BY, RELATION_LABEL_FOR, RELATION_DESCRIBED_BY,
+ RELATION_DESCRIPTION_FOR, RELATION_CONTROLLER_FOR,
+ RELATION_CONTROLLED_BY, RELATION_FLOWS_TO, RELATION_FLOWS_FROM */
+
+loadScripts({ name: 'relations.js', dir: MOCHITESTS_DIR });
+
+/**
+ * A test specification that has the following format:
+ * [
+ * attr relevant aria attribute
+ * hostRelation corresponding host relation type
+ * dependantRelation corresponding dependant relation type
+ * ]
+ */
+const attrRelationsSpec = [
+ ['aria-labelledby', RELATION_LABELLED_BY, RELATION_LABEL_FOR],
+ ['aria-describedby', RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR],
+ ['aria-controls', RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY],
+ ['aria-flowto', RELATION_FLOWS_TO, RELATION_FLOWS_FROM]
+];
+
+function* testRelated(browser, accDoc, attr, hostRelation, dependantRelation) {
+ let host = findAccessibleChildByID(accDoc, 'host');
+ let dependant1 = findAccessibleChildByID(accDoc, 'dependant1');
+ let dependant2 = findAccessibleChildByID(accDoc, 'dependant2');
+
+ /**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * attrs {?Array} an optional list of attributes to update
+ * expected {Array} expected relation values for dependant1, dependant2
+ * and host respectively.
+ * }
+ */
+ const tests = [{
+ desc: 'No attribute',
+ expected: [ null, null, null ]
+ }, {
+ desc: 'Set attribute',
+ attrs: [{ key: attr, value: 'dependant1' }],
+ expected: [ host, null, dependant1 ]
+ }, {
+ desc: 'Change attribute',
+ attrs: [{ key: attr, value: 'dependant2' }],
+ expected: [ null, host, dependant2 ]
+ }, {
+ desc: 'Remove attribute',
+ attrs: [{ key: attr }],
+ expected: [ null, null, null ]
+ }];
+
+ for (let { desc, attrs, expected } of tests) {
+ info(desc);
+
+ if (attrs) {
+ for (let { key, value } of attrs) {
+ yield invokeSetAttribute(browser, 'host', key, value);
+ }
+ }
+
+ testRelation(dependant1, dependantRelation, expected[0]);
+ testRelation(dependant2, dependantRelation, expected[1]);
+ testRelation(host, hostRelation, expected[2]);
+ }
+}
+
+/**
+ * Test caching of relations between accessible objects.
+ */
+addAccessibleTask(`
+ <div id="dependant1">label</div>
+ <div id="dependant2">label2</div>
+ <div role="checkbox" id="host"></div>`,
+ function* (browser, accDoc) {
+ for (let spec of attrRelationsSpec) {
+ yield testRelated(browser, accDoc, ...spec);
+ }
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_caching_states.js b/accessible/tests/browser/e10s/browser_caching_states.js
new file mode 100644
index 000000000..69e4931ea
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_states.js
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_STATE_CHANGE, STATE_CHECKED, STATE_BUSY, STATE_REQUIRED,
+ STATE_INVALID, EXT_STATE_ENABLED */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR },
+ { name: 'states.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * expected {Array} expected states for a given accessible that have the
+ * following format:
+ * [
+ * expected state,
+ * expected extra state,
+ * absent state,
+ * absent extra state
+ * ]
+ * attrs {?Array} an optional list of attributes to update
+ * }
+ */
+
+// State caching tests for attribute changes
+const attributeTests = [{
+ desc: 'Checkbox with @checked attribute set to true should have checked ' +
+ 'state',
+ attrs: [{
+ attr: 'checked',
+ value: 'true'
+ }],
+ expected: [STATE_CHECKED, 0]
+}, {
+ desc: 'Checkbox with no @checked attribute should not have checked state',
+ attrs: [{
+ attr: 'checked'
+ }],
+ expected: [0, 0, STATE_CHECKED]
+}];
+
+// State caching tests for ARIA changes
+const ariaTests = [{
+ desc: 'File input has busy state when @aria-busy attribute is set to true',
+ attrs: [{
+ attr: 'aria-busy',
+ value: 'true'
+ }],
+ expected: [STATE_BUSY, 0, STATE_REQUIRED | STATE_INVALID]
+}, {
+ desc: 'File input has required state when @aria-required attribute is set ' +
+ 'to true',
+ attrs: [{
+ attr: 'aria-required',
+ value: 'true'
+ }],
+ expected: [STATE_REQUIRED, 0, STATE_INVALID]
+}, {
+ desc: 'File input has invalid state when @aria-invalid attribute is set to ' +
+ 'true',
+ attrs: [{
+ attr: 'aria-invalid',
+ value: 'true'
+ }],
+ expected: [STATE_INVALID, 0]
+}];
+
+// Extra state caching tests
+const extraStateTests = [{
+ desc: 'Input has no extra enabled state when aria and native disabled ' +
+ 'attributes are set at once',
+ attrs: [{
+ attr: 'aria-disabled',
+ value: 'true'
+ }, {
+ attr: 'disabled',
+ value: 'true'
+ }],
+ expected: [0, 0, 0, EXT_STATE_ENABLED]
+}, {
+ desc: 'Input has an extra enabled state when aria and native disabled ' +
+ 'attributes are unset at once',
+ attrs: [{
+ attr: 'aria-disabled'
+ }, {
+ attr: 'disabled'
+ }],
+ expected: [0, EXT_STATE_ENABLED]
+}];
+
+function* runStateTests(browser, accDoc, id, tests) {
+ let acc = findAccessibleChildByID(accDoc, id);
+ for (let { desc, attrs, expected } of tests) {
+ info(desc);
+ let onUpdate = waitForEvent(EVENT_STATE_CHANGE, id);
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, id, attr, value);
+ }
+ yield onUpdate;
+ testStates(acc, ...expected);
+ }
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask(`
+ <input id="checkbox" type="checkbox">
+ <input id="file" type="file">
+ <input id="text">`,
+ function* (browser, accDoc) {
+ yield runStateTests(browser, accDoc, 'checkbox', attributeTests);
+ yield runStateTests(browser, accDoc, 'file', ariaTests);
+ yield runStateTests(browser, accDoc, 'text', extraStateTests);
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_caching_value.js b/accessible/tests/browser/e10s/browser_caching_value.js
new file mode 100644
index 000000000..2669cbfab
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_caching_value.js
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global nsIAccessibleValue, EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE */
+
+loadScripts({ name: 'value.js', dir: MOCHITESTS_DIR });
+
+/**
+ * Test data has the format of:
+ * {
+ * desc {String} description for better logging
+ * id {String} given accessible DOMNode ID
+ * expected {String} expected value for a given accessible
+ * action {?Function*} an optional action that yields a value change
+ * attrs {?Array} an optional list of attributes to update
+ * waitFor {?Number} an optional value change event to wait for
+ * }
+ */
+const valueTests = [{
+ desc: 'Initially value is set to 1st element of select',
+ id: 'select',
+ expected: '1st'
+}, {
+ desc: 'Value should update to 3rd when 3 is pressed',
+ id: 'select',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'select');
+ yield BrowserTestUtils.synthesizeKey('3', {}, browser);
+ },
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: '3rd'
+}, {
+ desc: 'Initially value is set to @aria-valuenow for slider',
+ id: 'slider',
+ expected: ['5', 5, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuenow is updated',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuenow',
+ value: '6'
+ }],
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: ['6', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuetext is set',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuetext',
+ value: 'plain'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: ['plain', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change when @aria-valuetext is updated',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuetext',
+ value: 'hey!'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: ['hey!', 6, 0, 7, 0]
+}, {
+ desc: 'Value should change to @aria-valuetext when @aria-valuenow is removed',
+ id: 'slider',
+ attrs: [{
+ attr: 'aria-valuenow'
+ }],
+ expected: ['hey!', 0, 0, 7, 0]
+}, {
+ desc: 'Initially value is not set for combobox',
+ id: 'combobox',
+ expected: ''
+}, {
+ desc: 'Value should change when @value attribute is updated',
+ id: 'combobox',
+ attrs: [{
+ attr: 'value',
+ value: 'hello'
+ }],
+ waitFor: EVENT_TEXT_VALUE_CHANGE,
+ expected: 'hello'
+}, {
+ desc: 'Initially value corresponds to @value attribute for progress',
+ id: 'progress',
+ expected: '22%'
+}, {
+ desc: 'Value should change when @value attribute is updated',
+ id: 'progress',
+ attrs: [{
+ attr: 'value',
+ value: '50'
+ }],
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: '50%'
+}, {
+ desc: 'Initially value corresponds to @value attribute for range',
+ id: 'range',
+ expected: '6'
+}, {
+ desc: 'Value should change when slider is moved',
+ id: 'range',
+ action: function*(browser) {
+ yield invokeFocus(browser, 'range');
+ yield BrowserTestUtils.synthesizeKey('VK_LEFT', {}, browser);
+ },
+ waitFor: EVENT_VALUE_CHANGE,
+ expected: '5'
+}];
+
+/**
+ * Test caching of accessible object values
+ */
+addAccessibleTask(`
+ <div id="slider" role="slider" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="7">slider</div>
+ <select id="select">
+ <option>1st</option>
+ <option>2nd</option>
+ <option>3rd</option>
+ </select>
+ <input id="combobox" role="combobox" aria-autocomplete="inline">
+ <progress id="progress" value="22" max="100"></progress>
+ <input type="range" id="range" min="0" max="10" value="6">`,
+ function* (browser, accDoc) {
+ for (let { desc, id, action, attrs, expected, waitFor } of valueTests) {
+ info(desc);
+ let acc = findAccessibleChildByID(accDoc, id);
+ let onUpdate;
+
+ if (waitFor) {
+ onUpdate = waitForEvent(waitFor, id);
+ }
+
+ if (action) {
+ yield action(browser);
+ } else if (attrs) {
+ for (let { attr, value } of attrs) {
+ yield invokeSetAttribute(browser, id, attr, value);
+ }
+ }
+
+ yield onUpdate;
+ if (Array.isArray(expected)) {
+ acc.QueryInterface(nsIAccessibleValue);
+ testValue(acc, ...expected);
+ } else {
+ is(acc.value, expected, `Correct value for ${prettyName(acc)}`);
+ }
+ }
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_events_caretmove.js b/accessible/tests/browser/e10s/browser_events_caretmove.js
new file mode 100644
index 000000000..506945f30
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_caretmove.js
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global EVENT_TEXT_CARET_MOVED, nsIAccessibleCaretMoveEvent */
+
+'use strict';
+
+/**
+ * Test caret move event and its interface:
+ * - caretOffset
+ */
+addAccessibleTask('<input id="textbox" value="hello"/>', function*(browser) {
+ let onCaretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, 'textbox');
+ yield invokeFocus(browser, 'textbox');
+ let event = yield onCaretMoved;
+
+ let caretMovedEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent);
+ is(caretMovedEvent.caretOffset, 5,
+ 'Correct caret offset.');
+});
diff --git a/accessible/tests/browser/e10s/browser_events_hide.js b/accessible/tests/browser/e10s/browser_events_hide.js
new file mode 100644
index 000000000..bb9ee3961
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_hide.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global EVENT_HIDE */
+
+'use strict';
+
+/**
+ * Test hide event and its interface:
+ * - targetParent
+ * - targetNextSibling
+ * - targetPrevSibling
+ */
+addAccessibleTask(`
+ <div id="parent">
+ <div id="previous"></div>
+ <div id="to-hide"></div>
+ <div id="next"></div>
+ </div>`,
+ function*(browser, accDoc) {
+ let acc = findAccessibleChildByID(accDoc, 'to-hide');
+ let onHide = waitForEvent(EVENT_HIDE, acc);
+ yield invokeSetStyle(browser, 'to-hide', 'visibility', 'hidden');
+ let event = yield onHide;
+ let hideEvent = event.QueryInterface(Ci.nsIAccessibleHideEvent);
+
+ is(getAccessibleDOMNodeID(hideEvent.targetParent), 'parent',
+ 'Correct target parent.');
+ is(getAccessibleDOMNodeID(hideEvent.targetNextSibling), 'next',
+ 'Correct target next sibling.');
+ is(getAccessibleDOMNodeID(hideEvent.targetPrevSibling), 'previous',
+ 'Correct target previous sibling.');
+ }
+);
diff --git a/accessible/tests/browser/e10s/browser_events_show.js b/accessible/tests/browser/e10s/browser_events_show.js
new file mode 100644
index 000000000..5003c14f7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_show.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global EVENT_SHOW */
+
+'use strict';
+
+/**
+ * Test show event
+ */
+addAccessibleTask('<div id="div" style="visibility: hidden;"></div>',
+ function*(browser) {
+ let onShow = waitForEvent(EVENT_SHOW, 'div');
+ yield invokeSetStyle(browser, 'div', 'visibility', 'visible');
+ yield onShow;
+ });
diff --git a/accessible/tests/browser/e10s/browser_events_statechange.js b/accessible/tests/browser/e10s/browser_events_statechange.js
new file mode 100644
index 000000000..7f353efa7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_statechange.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global STATE_CHECKED, EXT_STATE_EDITABLE, nsIAccessibleStateChangeEvent,
+ EVENT_STATE_CHANGE */
+
+'use strict';
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR },
+ { name: 'states.js', dir: MOCHITESTS_DIR });
+
+function checkStateChangeEvent(event, state, isExtraState, isEnabled) {
+ let scEvent = event.QueryInterface(nsIAccessibleStateChangeEvent);
+ is(scEvent.state, state, 'Correct state of the statechange event.');
+ is(scEvent.isExtraState, isExtraState,
+ 'Correct extra state bit of the statechange event.');
+ is(scEvent.isEnabled, isEnabled, 'Correct state of statechange event state');
+}
+
+// Insert mock source into the iframe to be able to verify the right document
+// body id.
+let iframeSrc = `data:text/html,
+ <html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Inner Iframe</title>
+ </head>
+ <body id='iframe'></body>
+ </html>`;
+
+/**
+ * Test state change event and its interface:
+ * - state
+ * - isExtraState
+ * - isEnabled
+ */
+addAccessibleTask(`
+ <iframe id="iframe" src="${iframeSrc}"></iframe>
+ <input id="checkbox" type="checkbox" />`, function*(browser) {
+ // Test state change
+ let onStateChange = waitForEvent(EVENT_STATE_CHANGE, 'checkbox');
+ // Set checked for a checkbox.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('checkbox').checked = true;
+ });
+ let event = yield onStateChange;
+
+ checkStateChangeEvent(event, STATE_CHECKED, false, true);
+ testStates(event.accessible, STATE_CHECKED, 0);
+
+ // Test extra state
+ onStateChange = waitForEvent(EVENT_STATE_CHANGE, 'iframe');
+ // Set design mode on.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('iframe').contentDocument.designMode = 'on';
+ });
+ event = yield onStateChange;
+
+ checkStateChangeEvent(event, EXT_STATE_EDITABLE, true, true);
+ testStates(event.accessible, 0, EXT_STATE_EDITABLE);
+});
diff --git a/accessible/tests/browser/e10s/browser_events_textchange.js b/accessible/tests/browser/e10s/browser_events_textchange.js
new file mode 100644
index 000000000..a1aed52d1
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_events_textchange.js
@@ -0,0 +1,74 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* global EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
+ nsIAccessibleTextChangeEvent */
+
+'use strict';
+
+function checkTextChangeEvent(event, id, text, start, end, isInserted, isFromUserInput) {
+ let tcEvent = event.QueryInterface(nsIAccessibleTextChangeEvent);
+ is(tcEvent.start, start, `Correct start offset for ${prettyName(id)}`);
+ is(tcEvent.length, end - start, `Correct length for ${prettyName(id)}`);
+ is(tcEvent.isInserted, isInserted,
+ `Correct isInserted flag for ${prettyName(id)}`);
+ is(tcEvent.modifiedText, text, `Correct text for ${prettyName(id)}`);
+ is(tcEvent.isFromUserInput, isFromUserInput,
+ `Correct value of isFromUserInput for ${prettyName(id)}`);
+}
+
+function* changeText(browser, id, value, events) {
+ let onEvents = waitForMultipleEvents(events.map(({ isInserted }) => {
+ let eventType = isInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED;
+ return { id, eventType };
+ }));
+ // Change text in the subtree.
+ yield ContentTask.spawn(browser, [id, value], ([contentId, contentValue]) => {
+ content.document.getElementById(contentId).firstChild.textContent =
+ contentValue;
+ });
+ let resolvedEvents = yield onEvents;
+
+ events.forEach(({ isInserted, str, offset }, idx) =>
+ checkTextChangeEvent(resolvedEvents[idx],
+ id, str, offset, offset + str.length, isInserted, false));
+}
+
+function* removeTextFromInput(browser, id, value, start, end) {
+ let onTextRemoved = waitForEvent(EVENT_TEXT_REMOVED, id);
+ // Select text and delete it.
+ yield ContentTask.spawn(browser, [id, start, end], ([contentId, contentStart, contentEnd]) => {
+ let el = content.document.getElementById(contentId);
+ el.focus();
+ el.setSelectionRange(contentStart, contentEnd);
+ });
+ yield BrowserTestUtils.sendChar('VK_DELETE', browser);
+
+ let event = yield onTextRemoved;
+ checkTextChangeEvent(event, id, value, start, end, false, true);
+}
+
+/**
+ * Test text change event and its interface:
+ * - start
+ * - length
+ * - isInserted
+ * - modifiedText
+ * - isFromUserInput
+ */
+addAccessibleTask(`
+ <p id="p">abc</p>
+ <input id="input" value="input" />`, function*(browser) {
+ let events = [
+ { isInserted: false, str: 'abc', offset: 0 },
+ { isInserted: true, str: 'def', offset: 0 }
+ ];
+ yield changeText(browser, 'p', 'def', events);
+
+ events = [{ isInserted: true, str: 'DEF', offset: 2 }];
+ yield changeText(browser, 'p', 'deDEFf', events);
+
+ // Test isFromUserInput property.
+ yield removeTextFromInput(browser, 'input', 'n', 1, 2);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
new file mode 100644
index 000000000..a9544bc5c
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_SHOW, ROLE_DIALOG, ROLE_PUSHBUTTON, ROLE_TEXT_LEAF, ROLE_ENTRY,
+ ROLE_DOCUMENT */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+// Test ARIA Dialog
+addAccessibleTask('doc_treeupdate_ariadialog.html', function*(browser, accDoc) {
+ testAccessibleTree(accDoc, {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ });
+
+ // Make dialog visible and update its inner content.
+ let onShow = waitForEvent(EVENT_SHOW, 'dialog');
+ yield ContentTask.spawn(browser, {}, () => {
+ content.document.getElementById('dialog').style.display = 'block';
+ });
+ yield onShow;
+
+ testAccessibleTree(accDoc, {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_DIALOG,
+ children: [
+ {
+ role: ROLE_PUSHBUTTON,
+ children: [ { role: ROLE_TEXT_LEAF } ]
+ },
+ {
+ role: ROLE_ENTRY
+ }
+ ]
+ }
+ ]
+ });
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
new file mode 100644
index 000000000..998da32a1
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
@@ -0,0 +1,318 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* testContainer1(browser, accDoc) {
+ const id = 't1_container';
+ const docID = getAccessibleDOMNodeID(accDoc);
+ const acc = findAccessibleChildByID(accDoc, id);
+
+ /* ================= Initial tree test ==================================== */
+ // children are swapped by ARIA owns
+ let tree = {
+ SECTION: [
+ { CHECKBUTTON: [
+ { SECTION: [] }
+ ] },
+ { PUSHBUTTON: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Change ARIA owns ====================================== */
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
+ yield onReorder;
+
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }, // checkbox, native order
+ { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
+ { SECTION: [ ] } // subdiv from the subtree, ARIA owned
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Remove ARIA owns ====================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, id, 'aria-owns');
+ yield onReorder;
+
+ // children follow the DOM order
+ tree = {
+ SECTION: [
+ { PUSHBUTTON: [ ] },
+ { CHECKBUTTON: [
+ { SECTION: [] }
+ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Set ARIA owns ========================================= */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, id, 'aria-owns', 't1_button t1_subdiv');
+ yield onReorder;
+
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }, // checkbox
+ { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
+ { SECTION: [ ] } // subdiv from the subtree, ARIA owned
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Add ID to ARIA owns =================================== */
+ onReorder = waitForEvent(EVENT_REORDER, docID);
+ yield invokeSetAttribute(browser, id, 'aria-owns',
+ 't1_button t1_subdiv t1_group');
+ yield onReorder;
+
+ // children are swapped again, button and subdiv are appended to
+ // the children.
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }, // t1_checkbox
+ { PUSHBUTTON: [ ] }, // button, t1_button
+ { SECTION: [ ] }, // subdiv from the subtree, t1_subdiv
+ { GROUPING: [ ] } // group from outside, t1_group
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Append element ======================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let div = content.document.createElement('div');
+ div.setAttribute('id', 't1_child3');
+ div.setAttribute('role', 'radio');
+ content.document.getElementById(contentId).appendChild(div);
+ });
+ yield onReorder;
+
+ // children are invalidated, they includes aria-owns swapped kids and
+ // newly inserted child.
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }, // existing explicit, t1_checkbox
+ { RADIOBUTTON: [ ] }, // new explicit, t1_child3
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { SECTION: [ ] }, // ARIA owned, t1_subdiv
+ { GROUPING: [ ] } // ARIA owned, t1_group
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Remove element ======================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () =>
+ content.document.getElementById('t1_span').parentNode.removeChild(
+ content.document.getElementById('t1_span')));
+ yield onReorder;
+
+ // subdiv should go away
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }, // explicit, t1_checkbox
+ { RADIOBUTTON: [ ] }, // explicit, t1_child3
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { GROUPING: [ ] } // ARIA owned, t1_group
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Remove ID ============================================= */
+ onReorder = waitForEvent(EVENT_REORDER, docID);
+ yield invokeSetAttribute(browser, 't1_group', 'id');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] } // ARIA owned, t1_button
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================ Set ID ================================================ */
+ onReorder = waitForEvent(EVENT_REORDER, docID);
+ yield invokeSetAttribute(browser, 't1_grouptmp', 'id', 't1_group');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] },
+ { RADIOBUTTON: [ ] },
+ { PUSHBUTTON: [ ] }, // ARIA owned, t1_button
+ { GROUPING: [ ] } // ARIA owned, t1_group, previously t1_grouptmp
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* removeContainer(browser, accDoc) {
+ const id = 't2_container1';
+ const acc = findAccessibleChildByID(accDoc, id);
+
+ let tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] } // ARIA owned, 't2_owned'
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () =>
+ content.document.getElementById('t2_container2').removeChild(
+ content.document.getElementById('t2_container3')));
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* stealAndRecacheChildren(browser, accDoc) {
+ const id1 = 't3_container1';
+ const id2 = 't3_container2';
+ const acc1 = findAccessibleChildByID(accDoc, id1);
+ const acc2 = findAccessibleChildByID(accDoc, id2);
+
+ /* ================ Steal from other ARIA owns ============================ */
+ let onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield invokeSetAttribute(browser, id2, 'aria-owns', 't3_child');
+ yield onReorder;
+
+ let tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc1, tree);
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [ ] }
+ ]
+ };
+ testAccessibleTree(acc2, tree);
+
+ /* ================ Append element to recache children ==================== */
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield ContentTask.spawn(browser, id2, id => {
+ let div = content.document.createElement('div');
+ div.setAttribute('role', 'radio');
+ content.document.getElementById(id).appendChild(div);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc1, tree);
+
+ tree = {
+ SECTION: [
+ { RADIOBUTTON: [ ] },
+ { CHECKBUTTON: [ ] } // ARIA owned
+ ]
+ };
+ testAccessibleTree(acc2, tree);
+}
+
+function* showHiddenElement(browser, accDoc) {
+ const id = 't4_container1';
+ const acc = findAccessibleChildByID(accDoc, id);
+
+ let tree = {
+ SECTION: [
+ { RADIOBUTTON: [] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetStyle(browser, 't4_child1', 'display', 'block');
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { CHECKBUTTON: [] },
+ { RADIOBUTTON: [] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* rearrangeARIAOwns(browser, accDoc) {
+ const id = 't5_container';
+ const acc = findAccessibleChildByID(accDoc, id);
+ const tests = [{
+ val: 't5_checkbox t5_radio t5_button',
+ roleList: [ 'CHECKBUTTON', 'RADIOBUTTON', 'PUSHBUTTON' ]
+ }, {
+ val: 't5_radio t5_button t5_checkbox',
+ roleList: [ 'RADIOBUTTON', 'PUSHBUTTON', 'CHECKBUTTON' ]
+ }];
+
+ for (let { val, roleList } of tests) {
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, id, 'aria-owns', val);
+ yield onReorder;
+
+ let tree = { SECTION: [ ] };
+ for (let role of roleList) {
+ let ch = {};
+ ch[role] = [];
+ tree.SECTION.push(ch);
+ }
+ testAccessibleTree(acc, tree);
+ }
+}
+
+function* removeNotARIAOwnedEl(browser, accDoc) {
+ const id = 't6_container';
+ const acc = findAccessibleChildByID(accDoc, id);
+
+ let tree = {
+ SECTION: [
+ { TEXT_LEAF: [ ] },
+ { GROUPING: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ content.document.getElementById(contentId).removeChild(
+ content.document.getElementById('t6_span'));
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { GROUPING: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+addAccessibleTask('doc_treeupdate_ariaowns.html', function*(browser, accDoc) {
+ yield testContainer1(browser, accDoc);
+ yield removeContainer(browser, accDoc);
+ yield stealAndRecacheChildren(browser, accDoc);
+ yield showHiddenElement(browser, accDoc);
+ yield rearrangeARIAOwns(browser, accDoc);
+ yield removeNotARIAOwnedEl(browser, accDoc);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_canvas.js b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js
new file mode 100644
index 000000000..e4b3b70f7
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_SHOW */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <canvas id="canvas">
+ <div id="dialog" role="dialog" style="display: none;"></div>
+ </canvas>`, function*(browser, accDoc) {
+ let canvas = findAccessibleChildByID(accDoc, 'canvas');
+ let dialog = findAccessibleChildByID(accDoc, 'dialog');
+
+ testAccessibleTree(canvas, { CANVAS: [] });
+
+ let onShow = waitForEvent(EVENT_SHOW, 'dialog');
+ yield invokeSetStyle(browser, 'dialog', 'display', 'block');
+ yield onShow;
+
+ testAccessibleTree(dialog, { DIALOG: [] });
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
new file mode 100644
index 000000000..d8b217380
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <div id="container"><div id="scrollarea" style="overflow:auto;"><input>
+ </div></div>
+ <div id="container2"><div id="scrollarea2" style="overflow:hidden;">
+ </div></div>`, function*(browser, accDoc) {
+ const id1 = 'container';
+ const id2 = 'container2';
+ const container = findAccessibleChildByID(accDoc, id1);
+ const container2 = findAccessibleChildByID(accDoc, id2);
+
+ /* ================= Change scroll range ================================== */
+ let tree = {
+ SECTION: [ {// container
+ SECTION: [ {// scroll area
+ ENTRY: [ ] // child content
+ } ]
+ } ]
+ };
+ testAccessibleTree(container, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id1);
+ yield ContentTask.spawn(browser, id1, id => {
+ let doc = content.document;
+ doc.getElementById('scrollarea').style.width = '20px';
+ doc.getElementById(id).appendChild(doc.createElement('input'));
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {// container
+ SECTION: [ {// scroll area
+ ENTRY: [ ] // child content
+ } ]
+ }, {
+ ENTRY: [ ] // inserted input
+ } ]
+ };
+ testAccessibleTree(container, tree);
+
+ /* ================= Change scrollbar styles ============================== */
+ tree = { SECTION: [ ] };
+ testAccessibleTree(container2, tree);
+
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ yield invokeSetStyle(browser, 'scrollarea2', 'overflow', 'auto');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [] } // scroll area
+ ]
+ };
+ testAccessibleTree(container2, tree);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_doc.js b/accessible/tests/browser/e10s/browser_treeupdate_doc.js
new file mode 100644
index 000000000..ccb1d1566
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_doc.js
@@ -0,0 +1,312 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global ROLE_PUSHBUTTON, ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_DOCUMENT,
+ nsIAccessibleDocument */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+const iframeSrc = `data:text/html,
+ <html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Inner Iframe</title>
+ </head>
+ <body id='inner-iframe'></body>
+ </html>`;
+
+addAccessibleTask(`
+ <iframe id="iframe" src="${iframeSrc}"></iframe>`, function*(browser, accDoc) {
+ // ID of the iframe that is being tested
+ const id = 'inner-iframe';
+
+ let iframe = findAccessibleChildByID(accDoc, id);
+
+ /* ================= Initial tree check =================================== */
+ let tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Write iframe document ================================ */
+ let reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let newHTMLNode = docNode.createElement('html');
+ let newBodyNode = docNode.createElement('body');
+ let newTextNode = docNode.createTextNode('New Wave');
+ newBodyNode.id = contentId;
+ newBodyNode.appendChild(newTextNode);
+ newHTMLNode.appendChild(newBodyNode);
+ docNode.replaceChild(newHTMLNode, docNode.documentElement);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Wave'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Replace iframe HTML element ========================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ // We can't use open/write/close outside of iframe document because of
+ // security error.
+ let script = docNode.createElement('script');
+ script.textContent = `
+ document.open();
+ document.write('<body id="${contentId}">hello</body>');
+ document.close();`;
+ docNode.body.appendChild(script);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Replace iframe body ================================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let newBodyNode = docNode.createElement('body');
+ let newTextNode = docNode.createTextNode('New Hello');
+ newBodyNode.id = contentId;
+ newBodyNode.appendChild(newTextNode);
+ newBodyNode.setAttribute('role', 'button');
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Open iframe document ================================= */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ // Open document.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let script = docNode.createElement('script');
+ script.textContent = `
+ function closeMe() {
+ document.write('Works?');
+ document.close();
+ }
+ window.closeMe = closeMe;
+ document.open();
+ document.write('<body id="${contentId}"></body>');`;
+ docNode.body.appendChild(script);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Close iframe document ================================ */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Write and close document.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.write('Works?');
+ docNode.close();
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'Works?'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Remove HTML from iframe document ===================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, iframe);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Remove HTML element.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.removeChild(docNode.firstChild);
+ });
+ let event = yield reorderEventPromise;
+
+ ok(event.accessible instanceof nsIAccessibleDocument,
+ 'Reorder should happen on the document');
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Insert HTML to iframe document ======================= */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ // Insert HTML element.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let html = docNode.createElement('html');
+ let body = docNode.createElement('body');
+ let text = docNode.createTextNode('Haha');
+ body.appendChild(text);
+ body.id = contentId;
+ html.appendChild(body);
+ docNode.appendChild(html);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'Haha'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Remove body from iframe document ===================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, iframe);
+ yield ContentTask.spawn(browser, {}, () => {
+ // Remove body element.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ docNode.documentElement.removeChild(docNode.body);
+ });
+ event = yield reorderEventPromise;
+
+ ok(event.accessible instanceof nsIAccessibleDocument,
+ 'Reorder should happen on the document');
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================ Insert element under document element while body missed */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, iframe);
+ yield ContentTask.spawn(browser, {}, () => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let inputNode = content.window.inputNode = docNode.createElement('input');
+ docNode.documentElement.appendChild(inputNode);
+ });
+ event = yield reorderEventPromise;
+
+ ok(event.accessible instanceof nsIAccessibleDocument,
+ 'Reorder should happen on the document');
+ tree = {
+ DOCUMENT: [
+ { ENTRY: [ ] }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ reorderEventPromise = waitForEvent(EVENT_REORDER, iframe);
+ yield ContentTask.spawn(browser, {}, () => {
+ let docEl =
+ content.document.getElementById('iframe').contentDocument.documentElement;
+ // Remove aftermath of this test before next test starts.
+ docEl.removeChild(docEl.firstChild);
+ });
+ // Make sure reorder event was fired and that the input was removed.
+ yield reorderEventPromise;
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Insert body to iframe document ======================= */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ // Write and close document.
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ // Insert body element.
+ let body = docNode.createElement('body');
+ let text = docNode.createTextNode('Yo ho ho i butylka roma!');
+ body.appendChild(text);
+ body.id = contentId;
+ docNode.documentElement.appendChild(body);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_DOCUMENT,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'Yo ho ho i butylka roma!'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+
+ /* ================= Change source ======================================== */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, 'iframe');
+ yield invokeSetAttribute(browser, 'iframe', 'src',
+ `data:text/html,<html><body id="${id}"><input></body></html>`);
+ event = yield reorderEventPromise;
+
+ tree = {
+ INTERNAL_FRAME: [
+ { DOCUMENT: [
+ { ENTRY: [ ] }
+ ] }
+ ]
+ };
+ testAccessibleTree(event.accessible, tree);
+ iframe = findAccessibleChildByID(event.accessible, id);
+
+ /* ================= Replace iframe body on ARIA role body ================ */
+ reorderEventPromise = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let docNode = content.document.getElementById('iframe').contentDocument;
+ let newBodyNode = docNode.createElement('body');
+ let newTextNode = docNode.createTextNode('New Hello');
+ newBodyNode.appendChild(newTextNode);
+ newBodyNode.setAttribute('role', 'button');
+ newBodyNode.id = contentId;
+ docNode.documentElement.replaceChild(newBodyNode, docNode.body);
+ });
+ yield reorderEventPromise;
+
+ tree = {
+ role: ROLE_PUSHBUTTON,
+ children: [
+ {
+ role: ROLE_TEXT_LEAF,
+ name: 'New Hello'
+ }
+ ]
+ };
+ testAccessibleTree(iframe, tree);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
new file mode 100644
index 000000000..126419288
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <style>
+ .gentext:before {
+ content: "START"
+ }
+ .gentext:after {
+ content: "END"
+ }
+ </style>
+ <div id="container1"></div>
+ <div id="container2"><div id="container2_child">text</div></div>`,
+ function*(browser, accDoc) {
+ const id1 = 'container1';
+ const id2 = 'container2';
+ let container1 = findAccessibleChildByID(accDoc, id1);
+ let container2 = findAccessibleChildByID(accDoc, id2);
+
+ let tree = {
+ SECTION: [ ] // container
+ };
+ testAccessibleTree(container1, tree);
+
+ tree = {
+ SECTION: [ { // container2
+ SECTION: [ { // container2 child
+ TEXT_LEAF: [ ] // primary text
+ } ]
+ } ]
+ };
+ testAccessibleTree(container2, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id1);
+ // Create and add an element with CSS generated content to container1
+ yield ContentTask.spawn(browser, id1, id => {
+ let node = content.document.createElement('div');
+ node.textContent = 'text';
+ node.setAttribute('class', 'gentext');
+ content.document.getElementById(id).appendChild(node);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [ // inserted node
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ]
+ };
+ testAccessibleTree(container1, tree);
+
+ onReorder = waitForEvent(EVENT_REORDER, id2);
+ // Add CSS generated content to an element in container2's subtree
+ yield invokeSetAttribute(browser, 'container2_child', 'class', 'gentext');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container2
+ { SECTION: [ // container2 child
+ { STATICTEXT: [] }, // :before
+ { TEXT_LEAF: [] }, // primary text
+ { STATICTEXT: [] } // :after
+ ] }
+ ]
+ };
+ testAccessibleTree(container2, tree);
+ });
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_hidden.js b/accessible/tests/browser/e10s/browser_treeupdate_hidden.js
new file mode 100644
index 000000000..00369ec05
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_hidden.js
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* setHidden(browser, value) {
+ let onReorder = waitForEvent(EVENT_REORDER, 'container');
+ yield invokeSetAttribute(browser, 'child', 'hidden', value);
+ yield onReorder;
+}
+
+addAccessibleTask('<div id="container"><input id="child"></div>',
+ function*(browser, accDoc) {
+ let container = findAccessibleChildByID(accDoc, 'container');
+
+ testAccessibleTree(container, { SECTION: [ { ENTRY: [ ] } ] });
+
+ // Set @hidden attribute
+ yield setHidden(browser, 'true');
+ testAccessibleTree(container, { SECTION: [ ] });
+
+ // Remove @hidden attribute
+ yield setHidden(browser);
+ testAccessibleTree(container, { SECTION: [ { ENTRY: [ ] } ] });
+ });
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
new file mode 100644
index 000000000..d299c0acb
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
@@ -0,0 +1,176 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER, ROLE_LINK */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* testImageMap(browser, accDoc) {
+ const id = 'imgmap';
+ const acc = findAccessibleChildByID(accDoc, id);
+
+ /* ================= Initial tree test ==================================== */
+ let tree = {
+ IMAGE_MAP: [
+ { role: ROLE_LINK, name: 'b', children: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Insert area ========================================== */
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ let areaElm = content.document.createElement('area');
+ let mapNode = content.document.getElementById('map');
+ areaElm.setAttribute('href',
+ 'http://www.bbc.co.uk/radio4/atoz/index.shtml#a');
+ areaElm.setAttribute('coords', '0,0,13,14');
+ areaElm.setAttribute('alt', 'a');
+ areaElm.setAttribute('shape', 'rect');
+ mapNode.insertBefore(areaElm, mapNode.firstChild);
+ });
+ yield onReorder;
+
+ tree = {
+ IMAGE_MAP: [
+ { role: ROLE_LINK, name: 'a', children: [ ] },
+ { role: ROLE_LINK, name: 'b', children: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Append area ========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ let areaElm = content.document.createElement('area');
+ let mapNode = content.document.getElementById('map');
+ areaElm.setAttribute('href',
+ 'http://www.bbc.co.uk/radio4/atoz/index.shtml#c');
+ areaElm.setAttribute('coords', '34,0,47,14');
+ areaElm.setAttribute('alt', 'c');
+ areaElm.setAttribute('shape', 'rect');
+ mapNode.appendChild(areaElm);
+ });
+ yield onReorder;
+
+ tree = {
+ IMAGE_MAP: [
+ { role: ROLE_LINK, name: 'a', children: [ ] },
+ { role: ROLE_LINK, name: 'b', children: [ ] },
+ { role: ROLE_LINK, name: 'c', children: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Remove area ========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ let mapNode = content.document.getElementById('map');
+ mapNode.removeChild(mapNode.firstElementChild);
+ });
+ yield onReorder;
+
+ tree = {
+ IMAGE_MAP: [
+ { role: ROLE_LINK, name: 'b', children: [ ] },
+ { role: ROLE_LINK, name: 'c', children: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+function* testContainer(browser) {
+ const id = 'container';
+ /* ================= Remove name on map =================================== */
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, 'map', 'name');
+ let event = yield onReorder;
+ const acc = event.accessible;
+
+ let tree = {
+ SECTION: [
+ { GRAPHIC: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Restore name on map ================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetAttribute(browser, 'map', 'name', 'atoz_map');
+ // XXX: force repainting of the image (see bug 745788 for details).
+ yield BrowserTestUtils.synthesizeMouse('#imgmap', 10, 10,
+ { type: 'mousemove' }, browser);
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {
+ IMAGE_MAP: [
+ { LINK: [ ] },
+ { LINK: [ ] }
+ ]
+ } ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Remove map =========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, {}, () => {
+ let mapNode = content.document.getElementById('map');
+ mapNode.parentNode.removeChild(mapNode);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { GRAPHIC: [ ] }
+ ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Insert map =========================================== */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ let map = content.document.createElement('map');
+ let area = content.document.createElement('area');
+
+ map.setAttribute('name', 'atoz_map');
+ map.setAttribute('id', 'map');
+
+ area.setAttribute('href',
+ 'http://www.bbc.co.uk/radio4/atoz/index.shtml#b');
+ area.setAttribute('coords', '17,0,30,14');
+ area.setAttribute('alt', 'b');
+ area.setAttribute('shape', 'rect');
+
+ map.appendChild(area);
+ content.document.getElementById(contentId).appendChild(map);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ {
+ IMAGE_MAP: [
+ { LINK: [ ] }
+ ]
+ } ]
+ };
+ testAccessibleTree(acc, tree);
+
+ /* ================= Hide image map ======================================= */
+ onReorder = waitForEvent(EVENT_REORDER, id);
+ yield invokeSetStyle(browser, 'imgmap', 'display', 'none');
+ yield onReorder;
+
+ tree = {
+ SECTION: [ ]
+ };
+ testAccessibleTree(acc, tree);
+}
+
+addAccessibleTask('doc_treeupdate_imagemap.html', function*(browser, accDoc) {
+ yield testImageMap(browser, accDoc);
+ yield testContainer(browser);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_list.js b/accessible/tests/browser/e10s/browser_treeupdate_list.js
new file mode 100644
index 000000000..023adf8e3
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_list.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_STATICTEXT, ROLE_LISTITEM */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* setDisplayAndWaitForReorder(browser, value) {
+ let onReorder = waitForEvent(EVENT_REORDER, 'ul');
+ yield invokeSetStyle(browser, 'li', 'display', value);
+ return yield onReorder;
+}
+
+addAccessibleTask(`
+ <ul id="ul">
+ <li id="li">item1</li>
+ </ul>`, function*(browser, accDoc) {
+ let li = findAccessibleChildByID(accDoc, 'li');
+ let bullet = li.firstChild;
+ let accTree = {
+ role: ROLE_LISTITEM,
+ children: [ {
+ role: ROLE_STATICTEXT,
+ children: []
+ }, {
+ role: ROLE_TEXT_LEAF,
+ children: []
+ } ]
+ };
+ testAccessibleTree(li, accTree);
+
+ yield setDisplayAndWaitForReorder(browser, 'none');
+
+ ok(isDefunct(li), 'Check that li is defunct.');
+ ok(isDefunct(bullet), 'Check that bullet is defunct.');
+
+ let event = yield setDisplayAndWaitForReorder(browser, 'list-item');
+
+ testAccessibleTree(findAccessibleChildByID(event.accessible, 'li'), accTree);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
new file mode 100644
index 000000000..7b01af87a
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global ROLE_TEXT_LEAF, EVENT_REORDER, ROLE_LISTITEM, ROLE_LIST,
+ ROLE_STATICTEXT */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<ol id="list"></ol>', function*(browser, accDoc) {
+ let list = findAccessibleChildByID(accDoc, 'list');
+
+ testAccessibleTree(list, {
+ role: ROLE_LIST,
+ children: [ ]
+ });
+
+ yield invokeSetAttribute(browser, 'body', 'contentEditable', 'true');
+ let onReorder = waitForEvent(EVENT_REORDER, 'list');
+ yield ContentTask.spawn(browser, {}, () => {
+ let li = content.document.createElement('li');
+ li.textContent = 'item';
+ content.document.getElementById('list').appendChild(li);
+ });
+ yield onReorder;
+
+ testAccessibleTree(list, {
+ role: ROLE_LIST,
+ children: [ {
+ role: ROLE_LISTITEM,
+ children: [
+ { role: ROLE_STATICTEXT, name: "1. ", children: [] },
+ { role: ROLE_TEXT_LEAF, children: [] }
+ ]
+ } ]
+ });
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_listener.js b/accessible/tests/browser/e10s/browser_treeupdate_listener.js
new file mode 100644
index 000000000..7b7880312
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_listener.js
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<span id="parent"><span id="child"></span></span>',
+ function*(browser, accDoc) {
+ is(findAccessibleChildByID(accDoc, 'parent'), null,
+ 'Check that parent is not accessible.');
+ is(findAccessibleChildByID(accDoc, 'child'), null,
+ 'Check that child is not accessible.');
+
+ let onReorder = waitForEvent(EVENT_REORDER, 'body');
+ // Add an event listener to parent.
+ yield ContentTask.spawn(browser, {}, () => {
+ content.window.dummyListener = () => {};
+ content.document.getElementById('parent').addEventListener(
+ 'click', content.window.dummyListener);
+ });
+ yield onReorder;
+
+ let tree = { TEXT: [] };
+ testAccessibleTree(findAccessibleChildByID(accDoc, 'parent'), tree);
+ });
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
new file mode 100644
index 000000000..45001afaa
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
@@ -0,0 +1,91 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('<select id="select"></select>', function*(browser, accDoc) {
+ let select = findAccessibleChildByID(accDoc, 'select');
+
+ let onEvent = waitForEvent(EVENT_REORDER, 'select');
+ // Create a combobox with grouping and 2 standalone options
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ let contentSelect = doc.getElementById('select');
+ let optGroup = doc.createElement('optgroup');
+
+ for (let i = 0; i < 2; i++) {
+ let opt = doc.createElement('option');
+ opt.value = i;
+ opt.text = 'Option: Value ' + i;
+ optGroup.appendChild(opt);
+ }
+ contentSelect.add(optGroup, null);
+
+ for (let i = 0; i < 2; i++) {
+ let opt = doc.createElement('option');
+ contentSelect.add(opt, null);
+ }
+ contentSelect.firstChild.firstChild.id = 'option1Node';
+ });
+ let event = yield onEvent;
+ let option1Node = findAccessibleChildByID(event.accessible, 'option1Node');
+
+ let tree = {
+ COMBOBOX: [ {
+ COMBOBOX_LIST: [ {
+ GROUPING: [
+ { COMBOBOX_OPTION: [ { TEXT_LEAF: [] } ] },
+ { COMBOBOX_OPTION: [ { TEXT_LEAF: [] } ] }
+ ]
+ }, {
+ COMBOBOX_OPTION: []
+ }, {
+ COMBOBOX_OPTION: []
+ } ]
+ } ]
+ };
+ testAccessibleTree(select, tree);
+ ok(!isDefunct(option1Node), 'option shouldn\'t be defunct');
+
+ onEvent = waitForEvent(EVENT_REORDER, 'select');
+ // Remove grouping from combobox
+ yield ContentTask.spawn(browser, {}, () => {
+ let contentSelect = content.document.getElementById('select');
+ contentSelect.removeChild(contentSelect.firstChild);
+ });
+ yield onEvent;
+
+ tree = {
+ COMBOBOX: [ {
+ COMBOBOX_LIST: [
+ { COMBOBOX_OPTION: [] },
+ { COMBOBOX_OPTION: [] }
+ ]
+ } ]
+ };
+ testAccessibleTree(select, tree);
+ ok(isDefunct(option1Node),
+ 'removed option shouldn\'t be accessible anymore!');
+
+ onEvent = waitForEvent(EVENT_REORDER, 'select');
+ // Remove all options from combobox
+ yield ContentTask.spawn(browser, {}, () => {
+ let contentSelect = content.document.getElementById('select');
+ while (contentSelect.length) {
+ contentSelect.remove(0);
+ }
+ });
+ yield onEvent;
+
+ tree = {
+ COMBOBOX: [ {
+ COMBOBOX_LIST: [ ]
+ } ]
+ };
+ testAccessibleTree(select, tree);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_removal.js b/accessible/tests/browser/e10s/browser_treeupdate_removal.js
new file mode 100644
index 000000000..9892bbcd6
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_removal.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('doc_treeupdate_removal.xhtml', function*(browser, accDoc) {
+ ok(isAccessible(findAccessibleChildByID(accDoc, 'the_table')),
+ 'table should be accessible');
+
+ // Move the_table element into hidden subtree.
+ let onReorder = waitForEvent(EVENT_REORDER, 'body');
+ yield ContentTask.spawn(browser, {}, () => content.document.getElementById(
+ 'the_displaynone').appendChild(content.document.getElementById(
+ 'the_table')));
+ yield onReorder;
+
+ ok(!isAccessible(findAccessibleChildByID(accDoc, 'the_table')),
+ 'table in display none tree shouldn\'t be accessible');
+ ok(!isAccessible(findAccessibleChildByID(accDoc, 'the_row')),
+ 'row shouldn\'t be accessible');
+
+ // Remove the_row element (since it did not have accessible, no event needed).
+ yield ContentTask.spawn(browser, {}, () =>
+ content.document.body.removeChild(
+ content.document.getElementById('the_row')));
+
+ // make sure no accessibles have stuck around.
+ ok(!isAccessible(findAccessibleChildByID(accDoc, 'the_row')),
+ 'row shouldn\'t be accessible');
+ ok(!isAccessible(findAccessibleChildByID(accDoc, 'the_table')),
+ 'table shouldn\'t be accessible');
+ ok(!isAccessible(findAccessibleChildByID(accDoc, 'the_displayNone')),
+ 'display none things shouldn\'t be accessible');
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_table.js b/accessible/tests/browser/e10s/browser_treeupdate_table.js
new file mode 100644
index 000000000..9609f51ac
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_table.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask(`
+ <table id="table">
+ <tr>
+ <td>cell1</td>
+ <td>cell2</td>
+ </tr>
+ </table>`, function*(browser, accDoc) {
+ let table = findAccessibleChildByID(accDoc, 'table');
+
+ let tree = {
+ TABLE: [
+ { ROW: [
+ { CELL: [ {TEXT_LEAF: [] }]},
+ { CELL: [ {TEXT_LEAF: [] }]}
+ ] }
+ ]
+ };
+ testAccessibleTree(table, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, 'table');
+ yield ContentTask.spawn(browser, {}, () => {
+ // append a caption, it should appear as a first element in the
+ // accessible tree.
+ let doc = content.document;
+ let caption = doc.createElement('caption');
+ caption.textContent = 'table caption';
+ doc.getElementById('table').appendChild(caption);
+ });
+ yield onReorder;
+
+ tree = {
+ TABLE: [
+ { CAPTION: [ { TEXT_LEAF: [] } ] },
+ { ROW: [
+ { CELL: [ {TEXT_LEAF: [] }]},
+ { CELL: [ {TEXT_LEAF: [] }]}
+ ] }
+ ]
+ };
+ testAccessibleTree(table, tree);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
new file mode 100644
index 000000000..da45e2bc9
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER, ROLE_TEXT_CONTAINER ROLE_PARAGRAPH, ROLE_TEXT_LEAF */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* removeTextData(browser, accessible, id, role) {
+ let tree = {
+ role: role,
+ children: [ { role: ROLE_TEXT_LEAF, name: "text" } ]
+ };
+ testAccessibleTree(accessible, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, id);
+ yield ContentTask.spawn(browser, id, contentId => {
+ content.document.getElementById(contentId).firstChild.textContent = '';
+ });
+ yield onReorder;
+
+ tree = { role: role, children: [] };
+ testAccessibleTree(accessible, tree);
+}
+
+addAccessibleTask(`
+ <p id="p">text</p>
+ <pre id="pre">text</pre>`, function*(browser, accDoc) {
+ let p = findAccessibleChildByID(accDoc, 'p');
+ let pre = findAccessibleChildByID(accDoc, 'pre');
+ yield removeTextData(browser, p, 'p', ROLE_PARAGRAPH);
+ yield removeTextData(browser, pre, 'pre', ROLE_TEXT_CONTAINER);
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_visibility.js b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js
new file mode 100644
index 000000000..65a55c914
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js
@@ -0,0 +1,196 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+function* testTreeOnHide(browser, accDoc, containerID, id, before, after) {
+ let acc = findAccessibleChildByID(accDoc, containerID);
+ testAccessibleTree(acc, before);
+
+ let onReorder = waitForEvent(EVENT_REORDER, containerID);
+ yield invokeSetStyle(browser, id, 'visibility', 'hidden');
+ yield onReorder;
+
+ testAccessibleTree(acc, after);
+}
+
+function* test3(browser, accessible) {
+ let tree = {
+ SECTION: [ // container
+ { SECTION: [ // parent
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] }
+ ] },
+ { SECTION: [ // parent2
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(accessible, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, 't3_container');
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ doc.getElementById('t3_container').style.color = 'red';
+ doc.getElementById('t3_parent').style.visibility = 'hidden';
+ doc.getElementById('t3_parent2').style.visibility = 'hidden';
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [ // container
+ { SECTION: [ // child
+ { TEXT_LEAF: [] }
+ ] },
+ { SECTION: [ // child2
+ { TEXT_LEAF: [] }
+ ] }
+ ] };
+ testAccessibleTree(accessible, tree);
+}
+
+function* test4(browser, accessible) {
+ let tree = {
+ SECTION: [
+ { TABLE: [
+ { ROW: [
+ { CELL: [ ] }
+ ] }
+ ] }
+ ] };
+ testAccessibleTree(accessible, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, 't4_parent');
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ doc.getElementById('t4_container').style.color = 'red';
+ doc.getElementById('t4_child').style.visibility = 'visible';
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [{
+ TABLE: [{
+ ROW: [{
+ CELL: [{
+ SECTION: [{
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }]
+ };
+ testAccessibleTree(accessible, tree);
+}
+
+addAccessibleTask('doc_treeupdate_visibility.html', function*(browser, accDoc) {
+ let t3Container = findAccessibleChildByID(accDoc, 't3_container');
+ let t4Container = findAccessibleChildByID(accDoc, 't4_container');
+
+ yield testTreeOnHide(browser, accDoc, 't1_container', 't1_parent', {
+ SECTION: [{
+ SECTION: [{
+ SECTION: [ { TEXT_LEAF: [] } ]
+ }]
+ }]
+ }, {
+ SECTION: [ {
+ SECTION: [ { TEXT_LEAF: [] } ]
+ } ]
+ });
+
+ yield testTreeOnHide(browser, accDoc, 't2_container', 't2_grandparent', {
+ SECTION: [{ // container
+ SECTION: [{ // grand parent
+ SECTION: [{
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }, {
+ SECTION: [{ // child2
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }, {
+ SECTION: [{ // container
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }, {
+ SECTION: [{ // child2
+ TEXT_LEAF: []
+ }]
+ }]
+ });
+
+ yield test3(browser, t3Container);
+ yield test4(browser, t4Container);
+
+ yield testTreeOnHide(browser, accDoc, 't5_container', 't5_subcontainer', {
+ SECTION: [{ // container
+ SECTION: [{ // subcontainer
+ TABLE: [{
+ ROW: [{
+ CELL: [{
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }]
+ }]
+ }, {
+ SECTION: [{ // container
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }]
+ });
+
+ yield testTreeOnHide(browser, accDoc, 't6_container', 't6_subcontainer', {
+ SECTION: [{ // container
+ SECTION: [{ // subcontainer
+ TABLE: [{
+ ROW: [{
+ CELL: [{
+ TABLE: [{ // nested table
+ ROW: [{
+ CELL: [{
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }]
+ }]
+ }]
+ }]
+ }, {
+ SECTION: [{ // child2
+ TEXT_LEAF: []
+ }]
+ }]
+ }]
+ }, {
+ SECTION: [{ // container
+ SECTION: [{ // child
+ TEXT_LEAF: []
+ }]
+ }, {
+ SECTION: [{ // child2
+ TEXT_LEAF: []
+ }]
+ }]
+ });
+});
diff --git a/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
new file mode 100644
index 000000000..c9dbde691
--- /dev/null
+++ b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_REORDER */
+
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
+
+addAccessibleTask('doc_treeupdate_whitespace.html', function*(browser, accDoc) {
+ let container1 = findAccessibleChildByID(accDoc, 'container1');
+ let container2Parent = findAccessibleChildByID(accDoc, 'container2-parent');
+
+ let tree = {
+ SECTION: [
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] }
+ ]
+ };
+ testAccessibleTree(container1, tree);
+
+ let onReorder = waitForEvent(EVENT_REORDER, 'container1');
+ // Remove img1 from container1
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ doc.getElementById('container1').removeChild(
+ doc.getElementById('img1'));
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { GRAPHIC: [] },
+ { TEXT_LEAF: [] },
+ { GRAPHIC: [] }
+ ]
+ };
+ testAccessibleTree(container1, tree);
+
+ tree = {
+ SECTION: [
+ { LINK: [] },
+ { LINK: [ { GRAPHIC: [] } ] }
+ ]
+ };
+ testAccessibleTree(container2Parent, tree);
+
+ onReorder = waitForEvent(EVENT_REORDER, 'container2-parent');
+ // Append an img with valid src to container2
+ yield ContentTask.spawn(browser, {}, () => {
+ let doc = content.document;
+ let img = doc.createElement('img');
+ img.setAttribute('src',
+ 'http://example.com/a11y/accessible/tests/mochitest/moz.png');
+ doc.getElementById('container2').appendChild(img);
+ });
+ yield onReorder;
+
+ tree = {
+ SECTION: [
+ { LINK: [ { GRAPHIC: [ ] } ] },
+ { TEXT_LEAF: [ ] },
+ { LINK: [ { GRAPHIC: [ ] } ] }
+ ]
+ };
+ testAccessibleTree(container2Parent, tree);
+});
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html b/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
new file mode 100644
index 000000000..9d08854b9
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
@@ -0,0 +1,23 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update ARIA Dialog Test</title>
+ </head>
+ <body id="body">
+ <div id="dialog" role="dialog" style="display: none;">
+ <table id="table" role="presentation"
+ style="display: block; position: fixed; top: 88px; left: 312.5px; z-index: 10010;">
+ <tbody>
+ <tr>
+ <td role="presentation">
+ <div role="presentation">
+ <a id="a" role="button">text</a>
+ </div>
+ <input id="input">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html b/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
new file mode 100644
index 000000000..38b5c333a
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
@@ -0,0 +1,44 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update ARIA Owns Test</title>
+ </head>
+ <body id="body">
+ <div id="t1_container" aria-owns="t1_checkbox t1_button">
+ <div role="button" id="t1_button"></div>
+ <div role="checkbox" id="t1_checkbox">
+ <span id="t1_span">
+ <div id="t1_subdiv"></div>
+ </span>
+ </div>
+ </div>
+ <div id="t1_group" role="group"></div>
+ <div id="t1_grouptmp" role="group"></div>
+
+ <div id="t2_container1" aria-owns="t2_owned"></div>
+ <div id="t2_container2">
+ <div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div>
+ </div>
+
+ <div id="t3_container1" aria-owns="t3_child"></div>
+ <div id="t3_child" role="checkbox"></div>
+ <div id="t3_container2"></div>
+
+ <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div>
+ <div id="t4_container2">
+ <div id="t4_child1" style="display:none" role="checkbox"></div>
+ <div id="t4_child2" role="radio"></div>
+ </div>
+
+ <div id="t5_container">
+ <div role="button" id="t5_button"></div>
+ <div role="checkbox" id="t5_checkbox"></div>
+ <div role="radio" id="t5_radio"></div>
+ </div>
+
+ <div id="t6_container" aria-owns="t6_fake">
+ <span id="t6_span">hey</span>
+ </div>
+ <div id="t6_fake" role="group"></div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html b/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
new file mode 100644
index 000000000..4dd230fc2
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Imagemap Test</title>
+ </head>
+ <body id="body">
+ <map name="atoz_map" id="map">
+ <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
+ coords="17,0,30,14" alt="b" shape="rect">
+ </map>
+
+ <div id="container">
+ <img id="imgmap" width="447" height="15"
+ usemap="#atoz_map"
+ src="http://example.com/a11y/accessible/tests/mochitest/letters.gif"><!--
+ Important: no whitespace between the <img> and the </div>, so we
+ don't end up with textframes there, because those would be reflected
+ in our accessible tree in some cases.
+ --></div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml b/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
new file mode 100644
index 000000000..9c59fb9d1
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Removal Test</title>
+ </head>
+ <body id="body">
+ <div id="the_displaynone" style="display: none;"></div>
+ <table id="the_table"></table>
+ <tr id="the_row"></tr>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_visibility.html b/accessible/tests/browser/e10s/doc_treeupdate_visibility.html
new file mode 100644
index 000000000..c33a2bc02
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_visibility.html
@@ -0,0 +1,78 @@
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Tree Update Visibility Test</title>
+ </head>
+ <body id="body">
+ <!-- hide parent while child stays visible -->
+ <div id="t1_container">
+ <div id="t1_parent">
+ <div id="t1_child" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- hide grandparent while its children stay visible -->
+ <div id="t2_container">
+ <div id="t2_grandparent">
+ <div>
+ <div id="t2_child" style="visibility: visible">text</div>
+ <div id="t2_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- change container style, hide parents while their children stay visible -->
+ <div id="t3_container">
+ <div id="t3_parent">
+ <div id="t3_child" style="visibility: visible">text</div>
+ </div>
+ <div id="t3_parent2">
+ <div id="t3_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+
+ <!-- change container style, show child inside the table -->
+ <div id="t4_container">
+ <table>
+ <tr>
+ <td id="t4_parent">
+ <div id="t4_child" style="visibility: hidden;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+
+ <!-- hide subcontainer while child inside the table stays visible -->
+ <div id="t5_container">
+ <div id="t5_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <div id="t5_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+
+ <!-- hide subcontainer while its child and child inside the nested table stays visible -->
+ <div id="t6_container">
+ <div id="t6_subcontainer">
+ <table>
+ <tr>
+ <td>
+ <table>
+ <tr>
+ <td>
+ <div id="t6_child" style="visibility: visible;">text</div>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ <div id="t6_child2" style="visibility: visible">text</div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html b/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
new file mode 100644
index 000000000..d1770d300
--- /dev/null
+++ b/accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
@@ -0,0 +1,10 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Whitespace text accessible creation/desctruction</title>
+ </head>
+ <body id="body">
+ <div id="container1"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img id="img1" src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> <img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"> </div>
+ <div id="container2-parent"> <a id="container2"></a> <a><img src="http://example.com/a11y/accessible/tests/mochitest/moz.png"></a> </div>
+ </body>
+</html>
diff --git a/accessible/tests/browser/e10s/events.js b/accessible/tests/browser/e10s/events.js
new file mode 100644
index 000000000..39dd743ef
--- /dev/null
+++ b/accessible/tests/browser/e10s/events.js
@@ -0,0 +1,127 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global nsIAccessibleEvent, nsIAccessibleDocument,
+ nsIAccessibleStateChangeEvent, nsIAccessibleTextChangeEvent */
+
+/* exported EVENT_REORDER, EVENT_SHOW, EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
+ EVENT_DOCUMENT_LOAD_COMPLETE, EVENT_HIDE, EVENT_TEXT_CARET_MOVED,
+ EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_STATE_CHANGE,
+ EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE, EVENT_FOCUS,
+ waitForEvent, waitForMultipleEvents */
+
+const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
+const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
+const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
+const EVENT_SHOW = nsIAccessibleEvent.EVENT_SHOW;
+const EVENT_STATE_CHANGE = nsIAccessibleEvent.EVENT_STATE_CHANGE;
+const EVENT_TEXT_CARET_MOVED = nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
+const EVENT_TEXT_INSERTED = nsIAccessibleEvent.EVENT_TEXT_INSERTED;
+const EVENT_TEXT_REMOVED = nsIAccessibleEvent.EVENT_TEXT_REMOVED;
+const EVENT_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE;
+const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
+const EVENT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_VALUE_CHANGE;
+const EVENT_TEXT_VALUE_CHANGE = nsIAccessibleEvent.EVENT_TEXT_VALUE_CHANGE;
+const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
+
+/**
+ * Describe an event in string format.
+ * @param {nsIAccessibleEvent} event event to strigify
+ */
+function eventToString(event) {
+ let type = eventTypeToString(event.eventType);
+ let info = `Event type: ${type}`;
+
+ if (event instanceof nsIAccessibleStateChangeEvent) {
+ let stateStr = statesToString(event.isExtraState ? 0 : event.state,
+ event.isExtraState ? event.state : 0);
+ info += `, state: ${stateStr}, is enabled: ${event.isEnabled}`;
+ } else if (event instanceof nsIAccessibleTextChangeEvent) {
+ let tcType = event.isInserted ? 'inserted' : 'removed';
+ info += `, start: ${event.start}, length: ${event.length}, ${tcType} text: ${event.modifiedText}`;
+ }
+
+ info += `. Target: ${prettyName(event.accessible)}`;
+ return info;
+}
+
+/**
+ * A helper function that returns a promise that resolves when an accessible
+ * event of the given type with the given target (defined by its id or
+ * accessible) is observed.
+ * @param {String|nsIAccessible} expectedIdOrAcc expected content element id
+ * for the event
+ * @param {Number} eventType expected accessible event
+ * type
+ * @return {Promise} promise that resolves to an
+ * event
+ */
+function waitForEvent(eventType, expectedIdOrAcc) {
+ return new Promise(resolve => {
+ let eventObserver = {
+ observe(subject, topic, data) {
+ if (topic !== 'accessible-event') {
+ return;
+ }
+
+ let event = subject.QueryInterface(nsIAccessibleEvent);
+ if (Logger.enabled) {
+ // Avoid calling eventToString if the logger isn't enabled in order
+ // to avoid an intermittent crash (bug 1307645).
+ Logger.log(eventToString(event));
+ }
+
+ // If event type does not match expected type, skip the event.
+ if (event.eventType !== eventType) {
+ return;
+ }
+
+ let acc = event.accessible;
+ let id = getAccessibleDOMNodeID(acc);
+ let isID = typeof expectedIdOrAcc === 'string';
+ let actualIdOrAcc = isID ? id : acc;
+ // If event's accessible does not match expected DOMNode id or
+ // accessible, skip the event.
+ if (actualIdOrAcc === expectedIdOrAcc) {
+ if (isID) {
+ Logger.log(`Correct event DOMNode id: ${id}`);
+ } else {
+ Logger.log(`Correct event accessible: ${prettyName(acc)}`);
+ }
+ Logger.log(`Correct event type: ${eventTypeToString(eventType)}`);
+ ok(event.accessibleDocument instanceof nsIAccessibleDocument,
+ 'Accessible document present.');
+
+ Services.obs.removeObserver(this, 'accessible-event');
+ resolve(event);
+ }
+ }
+ };
+ Services.obs.addObserver(eventObserver, 'accessible-event', false);
+ });
+}
+
+/**
+ * A helper function that waits for a sequence of accessible events in
+ * specified order.
+ * @param {Array} events a list of events to wait (same format as
+ * waitForEvent arguments)
+ */
+function waitForMultipleEvents(events) {
+ // Next expected event index.
+ let currentIdx = 0;
+
+ return Promise.all(events.map(({ eventType, id }, idx) =>
+ // In addition to waiting for an event, attach an order checker for the
+ // event.
+ waitForEvent(eventType, id).then(resolvedEvent => {
+ // Verify that event happens in order and increment expected index.
+ is(idx, currentIdx++,
+ `Unexpected event order: ${eventToString(resolvedEvent)}`);
+ return resolvedEvent;
+ })
+ ));
+}
diff --git a/accessible/tests/browser/e10s/head.js b/accessible/tests/browser/e10s/head.js
new file mode 100644
index 000000000..5cc102697
--- /dev/null
+++ b/accessible/tests/browser/e10s/head.js
@@ -0,0 +1,84 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* global EVENT_DOCUMENT_LOAD_COMPLETE, CURRENT_CONTENT_DIR, loadFrameScripts */
+
+/* exported addAccessibleTask */
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ 'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+ this);
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param {String} doc URL (relative to current directory) or
+ * data URL or markup snippet that is used
+ * to test content with
+ * @param {Function|Function*} task a generator or a function with tests to
+ * run
+ */
+function addAccessibleTask(doc, task) {
+ add_task(function*() {
+ let url;
+ if (doc.includes('doc_')) {
+ url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+ } else {
+ // Assume it's a markup snippet.
+ url = `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Accessibility Test</title>
+ </head>
+ <body id="body">${doc}</body>
+ </html>`;
+ }
+
+ registerCleanupFunction(() => {
+ let observers = Services.obs.enumerateObservers('accessible-event');
+ while (observers.hasMoreElements()) {
+ Services.obs.removeObserver(
+ observers.getNext().QueryInterface(Ci.nsIObserver),
+ 'accessible-event');
+ }
+ });
+
+ let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+ yield BrowserTestUtils.withNewTab({
+ gBrowser,
+ url: url
+ }, function*(browser) {
+ registerCleanupFunction(() => {
+ if (browser) {
+ let tab = gBrowser.getTabForBrowser(browser);
+ if (tab && !tab.closing && tab.linkedBrowser) {
+ gBrowser.removeTab(tab);
+ }
+ }
+ });
+
+ yield SimpleTest.promiseFocus(browser);
+
+ loadFrameScripts(browser,
+ 'let { document, window, navigator } = content;',
+ { name: 'common.js', dir: MOCHITESTS_DIR });
+
+ Logger.log(
+ `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+ Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+ let event = yield onDocLoad;
+ yield task(browser, event.accessible);
+ });
+ });
+}
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
diff --git a/accessible/tests/browser/head.js b/accessible/tests/browser/head.js
new file mode 100644
index 000000000..8e8d82205
--- /dev/null
+++ b/accessible/tests/browser/head.js
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* exported initPromise, shutdownPromise,
+ setE10sPrefs, unsetE10sPrefs, forceGC */
+
+/**
+ * Set e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are set.
+ */
+function setE10sPrefs() {
+ return new Promise(resolve =>
+ SpecialPowers.pushPrefEnv({
+ set: [
+ ['browser.tabs.remote.autostart', true],
+ ['browser.tabs.remote.force-enable', true],
+ ['extensions.e10sBlocksEnabling', false]
+ ]
+ }, resolve));
+}
+
+/**
+ * Unset e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are unset.
+ */
+function unsetE10sPrefs() {
+ return new Promise(resolve => {
+ SpecialPowers.popPrefEnv(resolve);
+ });
+}
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ 'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+ this);
+
+/**
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired.
+ * @return {Promise} event promise evaluating to event's data
+ */
+function a11yInitOrShutdownPromise() {
+ return new Promise(resolve => {
+ let observe = (subject, topic, data) => {
+ Services.obs.removeObserver(observe, 'a11y-init-or-shutdown');
+ resolve(data);
+ };
+ Services.obs.addObserver(observe, 'a11y-init-or-shutdown', false);
+ });
+}
+
+/**
+ * Returns a promise that resolves when 'a11y-init-or-shutdown' event is fired
+ * in content.
+ * @param {Object} browser current "tabbrowser" element
+ * @return {Promise} event promise evaluating to event's data
+ */
+function contentA11yInitOrShutdownPromise(browser) {
+ return ContentTask.spawn(browser, {}, a11yInitOrShutdownPromise);
+}
+
+/**
+ * A helper function that maps 'a11y-init-or-shutdown' event to a promise that
+ * resovles or rejects depending on whether accessibility service is expected to
+ * be initialized or shut down.
+ */
+function promiseOK(promise, expected) {
+ return promise.then(flag =>
+ flag === expected ? Promise.resolve() : Promise.reject());
+}
+
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * initialized with the correct flag.
+ * @param {?Object} contentBrowser optinal remove browser object that indicates
+ * that accessibility service is expected to be
+ * initialized in content process.
+ * @return {Promise} promise that resolves when the accessibility
+ * service initialized correctly.
+ */
+function initPromise(contentBrowser) {
+ let a11yInitPromise = contentBrowser ?
+ contentA11yInitOrShutdownPromise(contentBrowser) :
+ a11yInitOrShutdownPromise();
+ return promiseOK(a11yInitPromise, '1').then(
+ () => ok(true, 'Service initialized correctly'),
+ () => ok(false, 'Service shutdown incorrectly'));
+}
+
+/**
+ * Checks and returns a promise that resolves when accessibility service is
+ * shut down with the correct flag.
+ * @param {?Object} contentBrowser optinal remove browser object that indicates
+ * that accessibility service is expected to be
+ * shut down in content process.
+ * @return {Promise} promise that resolves when the accessibility
+ * service shuts down correctly.
+ */
+function shutdownPromise(contentBrowser) {
+ let a11yShutdownPromise = contentBrowser ?
+ contentA11yInitOrShutdownPromise(contentBrowser) :
+ a11yInitOrShutdownPromise();
+ return promiseOK(a11yShutdownPromise, '0').then(
+ () => ok(true, 'Service shutdown correctly'),
+ () => ok(false, 'Service initialized incorrectly'));
+}
+
+/**
+ * Force garbage collection.
+ */
+function forceGC() {
+ Cu.forceCC();
+ Cu.forceGC();
+}
diff --git a/accessible/tests/browser/shared-head.js b/accessible/tests/browser/shared-head.js
new file mode 100644
index 000000000..83a9fa612
--- /dev/null
+++ b/accessible/tests/browser/shared-head.js
@@ -0,0 +1,229 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+/* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
+ invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
+ CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
+
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+/**
+ * Current browser test directory path used to load subscripts.
+ */
+const CURRENT_DIR =
+ 'chrome://mochitests/content/browser/accessible/tests/browser/';
+/**
+ * A11y mochitest directory where we find common files used in both browser and
+ * plain tests.
+ */
+const MOCHITESTS_DIR =
+ 'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
+/**
+ * A base URL for test files used in content.
+ */
+const CURRENT_CONTENT_DIR =
+ 'http://example.com/browser/accessible/tests/browser/';
+
+/**
+ * Used to dump debug information.
+ */
+let Logger = {
+ /**
+ * Set up this variable to dump log messages into console.
+ */
+ dumpToConsole: false,
+
+ /**
+ * Set up this variable to dump log messages into error console.
+ */
+ dumpToAppConsole: false,
+
+ /**
+ * Return true if dump is enabled.
+ */
+ get enabled() {
+ return this.dumpToConsole || this.dumpToAppConsole;
+ },
+
+ /**
+ * Dump information into console if applicable.
+ */
+ log(msg) {
+ if (this.enabled) {
+ this.logToConsole(msg);
+ this.logToAppConsole(msg);
+ }
+ },
+
+ /**
+ * Log message to console.
+ */
+ logToConsole(msg) {
+ if (this.dumpToConsole) {
+ dump(`\n${msg}\n`);
+ }
+ },
+
+ /**
+ * Log message to error console.
+ */
+ logToAppConsole(msg) {
+ if (this.dumpToAppConsole) {
+ Services.console.logStringMessage(`${msg}`);
+ }
+ }
+};
+
+/**
+ * Check if an accessible object has a defunct test.
+ * @param {nsIAccessible} accessible object to test defunct state for
+ * @return {Boolean} flag indicating defunct state
+ */
+function isDefunct(accessible) {
+ let defunct = false;
+ try {
+ let extState = {};
+ accessible.getState({}, extState);
+ defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
+ } catch (x) {
+ defunct = true;
+ } finally {
+ if (defunct) {
+ Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
+ }
+ }
+ return defunct;
+}
+
+/**
+ * Asynchronously set or remove content element's attribute (in content process
+ * if e10s is enabled).
+ * @param {Object} browser current "tabbrowser" element
+ * @param {String} id content element id
+ * @param {String} attr attribute name
+ * @param {String?} value optional attribute value, if not present, remove
+ * attribute
+ * @return {Promise} promise indicating that attribute is set/removed
+ */
+function invokeSetAttribute(browser, id, attr, value) {
+ if (value) {
+ Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
+ } else {
+ Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
+ }
+ return ContentTask.spawn(browser, [id, attr, value],
+ ([contentId, contentAttr, contentValue]) => {
+ let elm = content.document.getElementById(contentId);
+ if (contentValue) {
+ elm.setAttribute(contentAttr, contentValue);
+ } else {
+ elm.removeAttribute(contentAttr);
+ }
+ });
+}
+
+/**
+ * Asynchronously set or remove content element's style (in content process if
+ * e10s is enabled).
+ * @param {Object} browser current "tabbrowser" element
+ * @param {String} id content element id
+ * @param {String} aStyle style property name
+ * @param {String?} aValue optional style property value, if not present,
+ * remove style
+ * @return {Promise} promise indicating that style is set/removed
+ */
+function invokeSetStyle(browser, id, style, value) {
+ if (value) {
+ Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
+ } else {
+ Logger.log(`Removing ${style} style from node with id: ${id}`);
+ }
+ return ContentTask.spawn(browser, [id, style, value],
+ ([contentId, contentStyle, contentValue]) => {
+ let elm = content.document.getElementById(contentId);
+ if (contentValue) {
+ elm.style[contentStyle] = contentValue;
+ } else {
+ delete elm.style[contentStyle];
+ }
+ });
+}
+
+/**
+ * Asynchronously set focus on a content element (in content process if e10s is
+ * enabled).
+ * @param {Object} browser current "tabbrowser" element
+ * @param {String} id content element id
+ * @return {Promise} promise indicating that focus is set
+ */
+function invokeFocus(browser, id) {
+ Logger.log(`Setting focus on a node with id: ${id}`);
+ return ContentTask.spawn(browser, id, contentId => {
+ let elm = content.document.getElementById(contentId);
+ if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
+ elm instanceof Ci.nsIDOMXULTextBoxElement) {
+ elm.selectionStart = elm.selectionEnd = elm.value.length;
+ }
+ elm.focus();
+ });
+}
+
+/**
+ * Traverses the accessible tree starting from a given accessible as a root and
+ * looks for an accessible that matches based on its DOMNode id.
+ * @param {nsIAccessible} accessible root accessible
+ * @param {String} id id to look up accessible for
+ * @return {nsIAccessible?} found accessible if any
+ */
+function findAccessibleChildByID(accessible, id) {
+ if (getAccessibleDOMNodeID(accessible) === id) {
+ return accessible;
+ }
+ for (let i = 0; i < accessible.children.length; ++i) {
+ let found = findAccessibleChildByID(accessible.getChildAt(i), id);
+ if (found) {
+ return found;
+ }
+ }
+}
+
+/**
+ * Load a list of scripts into the test
+ * @param {Array} scripts a list of scripts to load
+ */
+function loadScripts(...scripts) {
+ for (let script of scripts) {
+ let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
+ `${script.dir}${script.name}`;
+ Services.scriptloader.loadSubScript(path, this);
+ }
+}
+
+/**
+ * Load a list of frame scripts into test's content.
+ * @param {Object} browser browser element that content belongs to
+ * @param {Array} scripts a list of scripts to load into content
+ */
+function loadFrameScripts(browser, ...scripts) {
+ let mm = browser.messageManager;
+ for (let script of scripts) {
+ let frameScript;
+ if (typeof script === 'string') {
+ if (script.includes('.js')) {
+ // If script string includes a .js extention, assume it is a script
+ // path.
+ frameScript = `${CURRENT_DIR}${script}`;
+ } else {
+ // Otherwise it is a serealized script.
+ frameScript = `data:,${script}`;
+ }
+ } else {
+ // Script is a object that has { dir, name } format.
+ frameScript = `${script.dir}${script.name}`;
+ }
+ mm.loadFrameScript(frameScript, false, true);
+ }
+}