Bug 1186029 - e10s compatible name caching, events and tree update tests. r=eeejay

MozReview-Commit-ID: 5Y5sYgGI2L9
---
 .eslintignore                                      |   1 -
 accessible/.eslintrc                               |  15 +
 accessible/jsat/OutputGenerator.jsm                |   7 +-
 accessible/moz.build                               |   2 +
 accessible/tests/browser/.eslintrc                 | 202 ++++++++++
 accessible/tests/browser/browser.ini               |  50 +++
 accessible/tests/browser/browser_caching_name.js   | 434 +++++++++++++++++++++
 .../tests/browser/browser_events_caretmove.js      |  21 +
 accessible/tests/browser/browser_events_hide.js    |  32 ++
 accessible/tests/browser/browser_events_show.js    |  17 +
 .../tests/browser/browser_events_statechange.js    |  60 +++
 .../tests/browser/browser_events_textchange.js     |  72 ++++
 .../tests/browser/browser_treeupdate_ariadialog.js |  42 ++
 .../tests/browser/browser_treeupdate_ariaowns.js   | 317 +++++++++++++++
 .../tests/browser/browser_treeupdate_canvas.js     |  25 ++
 .../browser/browser_treeupdate_cssoverflow.js      |  64 +++
 accessible/tests/browser/browser_treeupdate_doc.js | 303 ++++++++++++++
 .../tests/browser/browser_treeupdate_gencontent.js |  78 ++++
 .../tests/browser/browser_treeupdate_hidden.js     |  30 ++
 .../tests/browser/browser_treeupdate_imagemap.js   | 176 +++++++++
 .../tests/browser/browser_treeupdate_list.js       |  43 ++
 .../browser/browser_treeupdate_list_editabledoc.js |  39 ++
 .../tests/browser/browser_treeupdate_listener.js   |  43 ++
 .../tests/browser/browser_treeupdate_optgroup.js   |  91 +++++
 .../tests/browser/browser_treeupdate_removal.js    |  39 ++
 .../tests/browser/browser_treeupdate_table.js      |  51 +++
 .../tests/browser/browser_treeupdate_textleaf.js   |  34 ++
 .../tests/browser/browser_treeupdate_visibility.js | 196 ++++++++++
 .../tests/browser/browser_treeupdate_whitespace.js |  80 ++++
 .../tests/browser/doc_treeupdate_ariadialog.html   |  23 ++
 .../tests/browser/doc_treeupdate_ariaowns.html     |  44 +++
 .../tests/browser/doc_treeupdate_imagemap.html     |  21 +
 .../tests/browser/doc_treeupdate_removal.xhtml     |  11 +
 .../tests/browser/doc_treeupdate_visibility.html   |  78 ++++
 accessible/tests/browser/events.js                 | 104 +++++
 accessible/tests/browser/head.js                   | 266 +++++++++++++
 accessible/tests/mochitest/common.js               |  41 +-
 37 files changed, 3145 insertions(+), 7 deletions(-)
 create mode 100644 accessible/.eslintrc
 create mode 100644 accessible/tests/browser/.eslintrc
 create mode 100644 accessible/tests/browser/browser.ini
 create mode 100644 accessible/tests/browser/browser_caching_name.js
 create mode 100644 accessible/tests/browser/browser_events_caretmove.js
 create mode 100644 accessible/tests/browser/browser_events_hide.js
 create mode 100644 accessible/tests/browser/browser_events_show.js
 create mode 100644 accessible/tests/browser/browser_events_statechange.js
 create mode 100644 accessible/tests/browser/browser_events_textchange.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_ariadialog.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_ariaowns.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_canvas.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_cssoverflow.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_doc.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_gencontent.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_hidden.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_imagemap.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_list.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_list_editabledoc.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_listener.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_optgroup.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_removal.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_table.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_textleaf.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_visibility.js
 create mode 100644 accessible/tests/browser/browser_treeupdate_whitespace.js
 create mode 100644 accessible/tests/browser/doc_treeupdate_ariadialog.html
 create mode 100644 accessible/tests/browser/doc_treeupdate_ariaowns.html
 create mode 100644 accessible/tests/browser/doc_treeupdate_imagemap.html
 create mode 100644 accessible/tests/browser/doc_treeupdate_removal.xhtml
 create mode 100644 accessible/tests/browser/doc_treeupdate_visibility.html
 create mode 100644 accessible/tests/browser/events.js
 create mode 100644 accessible/tests/browser/head.js

--HG--
extra : rebase_source : 8b317297af584d0d19c13f34c3d6797d63efb345
This commit is contained in:
Yura Zenevich 2016-04-21 15:38:54 -04:00
Родитель c2d655f141
Коммит b6d43d1c3f
37 изменённых файлов: 3145 добавлений и 7 удалений

Просмотреть файл

@ -7,7 +7,6 @@ obj*/**
# We ignore all these directories by default, until we get them enabled.
# If you are enabling a directory, please add directory specific exclusions
# below.
accessible/**
addon-sdk/**
build/**
caps/**

15
accessible/.eslintrc Normal file
Просмотреть файл

@ -0,0 +1,15 @@
{
"extends": [
"../.eslintrc"
],
"globals": {
"Cc": true,
"Ci": true,
"Components": true,
"console": true,
"Cu": true,
"dump": true,
"Services": true,
"XPCOMUtils": true
}
}

Просмотреть файл

@ -276,10 +276,9 @@ var OutputGenerator = {
_addMencloseNotations: function _addMencloseNotations(aOutput, aAccessible) {
let notations = Utils.getAttributes(aAccessible).notation || 'longdiv';
aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift'].apply(
aOutput, [for (notation of notations.split(' '))
{string: this._getOutputName('notation-' + notation)}
]
);
aOutput, notations.split(' ').map(notation => {
return { string: this._getOutputName('notation-' + notation) };
}));
},
/**

Просмотреть файл

@ -21,3 +21,5 @@ if CONFIG['MOZ_XUL']:
DIRS += ['xul']
TEST_DIRS += ['tests/mochitest']
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']

Просмотреть файл

@ -0,0 +1,202 @@
{
"extends": [
"../../../testing/mochitest/browser.eslintrc"
],
// All globals made available in the test environment.
"globals": {
// Content scripts have global 'content' object
"content": true,
// Defined in accessible/tests/mochitest/ common.js, name.js, states.js
"prettyName": true,
"statesToString": true,
"eventTypeToString": true,
"testName": true,
"testStates": true,
"testAccessibleTree": true,
"isAccessible": true,
"getAccessibleDOMNodeID": true,
// Defined for all accessibility browser tests.
"addAccessibleTask": true,
"BrowserTestUtils": true,
"ContentTask": true,
"gBrowser": true,
"isDefunct": true,
"loadScripts": true,
"Logger": true,
"MOCHITESTS_DIR": true,
"waitForEvent": true,
"waitForMultipleEvents": true,
"invokeSetAttribute": true,
"invokeSetStyle": true,
"invokeFocus": true,
"findAccessibleChildByID": true
},
"rules": {
"mozilla/mark-test-function-used": 1,
"mozilla/no-aArgs": 1,
"mozilla/no-cpows-in-tests": 1,
"mozilla/reject-importGlobalProperties": 1,
"mozilla/var-only-at-top-level": 1,
"block-scoped-var": 2,
"brace-style": [2, "1tbs"],
"camelcase": 2,
"comma-dangle": [2, "never"],
"comma-spacing": 2,
"comma-style": [2, "last"],
"complexity": [2, 35],
"consistent-this": 0,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 2,
"eol-last": 2,
"eqeqeq": 0,
"func-names": 0,
"func-style": 0,
"generator-star": 0,
"global-strict": 0,
"handle-callback-err": [2, "er"],
"indent": [2, 2, {"SwitchCase": 1}],
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
"linebreak-style": 0,
"max-depth": 0,
"max-nested-callbacks": [2, 3],
"max-params": 0,
"max-statements": 0,
"new-cap": [2, {"capIsNew": false}],
"new-parens": 2,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 2,
"no-comma-dangle": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 2,
"no-empty": 2,
"no-empty-character-class": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 2,
"no-extra-strict": 0,
"no-fallthrough": 2,
"no-floating-decimal": 0,
"no-inline-comments": 0,
"no-lonely-if": 2,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, {"max": 1}],
"no-native-reassign": 2,
"no-nested-ternary": 2,
"no-new-require": 0,
"no-octal": 2,
"no-param-reassign": 0,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 1,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 1,
"no-shadow-restricted-names": 2,
"no-space-before-semi": 0,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-underscore-dangle": 0,
"no-undefined": 0,
"no-unneeded-ternary": 2,
"no-unreachable": 2,
"no-unused-vars": [2, {"vars": "all", "args": "none"}],
"no-use-before-define": 0,
"no-var": 0,
"no-warning-comments": 0,
"no-with": 2,
"object-shorthand": 0,
"one-var": [2, "never"],
"padded-blocks": [2, "never"],
"quote-props": 0,
"radix": 2,
"semi": [2, "always"],
"semi-spacing": [2, {"before": false, "after": true}],
"sort-vars": 0,
"space-after-function-name": 0,
"keyword-spacing": 2,
"space-before-blocks": 2,
"space-before-function-parentheses": 0,
"space-before-function-paren": [2, "never"],
"space-in-brackets": 0,
"space-in-parens": [2, "never"],
"space-infix-ops": [2, {"int32Hint": true}],
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"space-unary-word-ops": 0,
"spaced-comment": [2, "always"],
"strict": [2, "global"],
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": 0,
"wrap-regex": 0,
"yoda": 2,
"guard-for-in": 0,
"newline-after-var": 0,
"no-alert": 0,
"no-eq-null": 0,
"no-func-assign": 0,
"no-implied-eval": 0,
"no-inner-declarations": 0,
"no-invalid-regexp": 0,
"no-irregular-whitespace": 0,
"no-iterator": 0,
"no-label-var": 0,
"no-labels": 2,
"no-lone-blocks": 0,
"no-loop-func": 0,
"no-negated-in-lhs": 0,
"no-new": 0,
"no-new-func": 0,
"no-new-object": 0,
"no-new-wrappers": 0,
"no-obj-calls": 0,
"no-octal-escape": 0,
"no-undef-init": 2,
"no-unexpected-multiline": 2,
"object-curly-spacing": 0,
"no-unused-expressions": 0,
"no-void": 0,
"no-wrap-func": 0,
"operator-assignment": 0,
"operator-linebreak": [2, "after"],
}
}

Просмотреть файл

@ -0,0 +1,50 @@
[DEFAULT]
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
!/accessible/tests/mochitest/*.js
!/accessible/tests/mochitest/letters.gif
!/accessible/tests/mochitest/moz.png
# Caching tests
[browser_caching_name.js]
skip-if = e10s
# Events tests
[browser_events_caretmove.js]
[browser_events_hide.js]
skip-if = e10s
[browser_events_show.js]
skip-if = e10s
[browser_events_statechange.js]
[browser_events_textchange.js]
skip-if = e10s
# Tree update tests
[browser_treeupdate_ariadialog.js]
skip-if = e10s
[browser_treeupdate_ariaowns.js]
skip-if = e10s
[browser_treeupdate_canvas.js]
skip-if = e10s
[browser_treeupdate_cssoverflow.js]
[browser_treeupdate_doc.js]
skip-if = e10s
[browser_treeupdate_gencontent.js]
[browser_treeupdate_hidden.js]
[browser_treeupdate_imagemap.js]
skip-if = e10s
[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]

Просмотреть файл

@ -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);
}));

Просмотреть файл

@ -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.');
});

Просмотреть файл

@ -0,0 +1,32 @@
/* 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="div"></div>
<div id="next"></div>
</div>`, function*(browser) {
let onHide = waitForEvent(EVENT_HIDE, 'div');
yield invokeSetStyle(browser, 'div', '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.');
});

Просмотреть файл

@ -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;
});

Просмотреть файл

@ -0,0 +1,60 @@
/* 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);
});

Просмотреть файл

@ -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/. */
/* 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 }, ({ id, value }) =>
content.document.getElementById(id).firstChild.textContent = value);
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 }, ({ id, start, end }) => {
let el = content.document.getElementById(id);
el.focus();
el.setSelectionRange(start, end);
});
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);
});

Просмотреть файл

@ -0,0 +1,42 @@
/* 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
}
]
}
]
});
});

Просмотреть файл

@ -0,0 +1,317 @@
/* 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, id => {
let div = content.document.createElement('div');
div.setAttribute('id', 't1_child3');
div.setAttribute('role', 'radio');
content.document.getElementById(id).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, id =>
content.document.getElementById(id).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);
});

Просмотреть файл

@ -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: [] });
});

Просмотреть файл

@ -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);
});

Просмотреть файл

@ -0,0 +1,303 @@
/* 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 onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
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 = id;
newBodyNode.appendChild(newTextNode);
newHTMLNode.appendChild(newBodyNode);
docNode.replaceChild(newHTMLNode, docNode.documentElement);
});
yield onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'New Wave'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Replace iframe HTML element ========================== */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
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="${id}">hello</body>');
document.close();`;
docNode.body.appendChild(script);
});
yield onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'hello'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Replace iframe body ================================== */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
let docNode = content.document.getElementById('iframe').contentDocument;
let newBodyNode = docNode.createElement('body');
let newTextNode = docNode.createTextNode('New Hello');
newBodyNode.id = id;
newBodyNode.appendChild(newTextNode);
newBodyNode.setAttribute('role', 'button');
docNode.documentElement.replaceChild(newBodyNode, docNode.body);
});
yield onReorder;
tree = {
role: ROLE_PUSHBUTTON,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'New Hello'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Open iframe document ================================= */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
// 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="${id}"></body>');`;
docNode.body.appendChild(script);
});
yield onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [ ]
};
testAccessibleTree(iframe, tree);
/* ================= Close iframe document ================================ */
onReorder = 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 onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'Works?'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Remove HTML from iframe document ===================== */
onReorder = waitForEvent(EVENT_REORDER);
yield ContentTask.spawn(browser, {}, () => {
// Remove HTML element.
let docNode = content.document.getElementById('iframe').contentDocument;
docNode.removeChild(docNode.firstChild);
});
let event = yield onReorder;
ok(event.accessible instanceof nsIAccessibleDocument,
'Reorder should happen on the document');
tree = {
role: ROLE_DOCUMENT,
children: [ ]
};
testAccessibleTree(iframe, tree);
/* ================= Insert HTML to iframe document ======================= */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
// 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 = id;
html.appendChild(body);
docNode.appendChild(html);
});
yield onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'Haha'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Remove body from iframe document ===================== */
onReorder = waitForEvent(EVENT_REORDER);
yield ContentTask.spawn(browser, {}, () => {
// Remove body element.
let docNode = content.document.getElementById('iframe').contentDocument;
docNode.documentElement.removeChild(docNode.body);
});
event = yield onReorder;
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 */
onReorder = waitForEvent(EVENT_REORDER);
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 onReorder;
ok(event.accessible instanceof nsIAccessibleDocument,
'Reorder should happen on the document');
tree = {
DOCUMENT: [
{ ENTRY: [ ] }
]
};
testAccessibleTree(iframe, tree);
yield ContentTask.spawn(browser, {}, () => {
let docNode = content.document.getElementById('iframe').contentDocument;
// Remove aftermath of this test before next test starts.
docNode.documentElement.removeChild(content.window.inputNode);
});
/* ================= Insert body to iframe document ======================= */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
// 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 = id;
docNode.documentElement.appendChild(body);
});
yield onReorder;
tree = {
role: ROLE_DOCUMENT,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'Yo ho ho i butylka roma!'
}
]
};
testAccessibleTree(iframe, tree);
/* ================= Change source ======================================== */
onReorder = waitForEvent(EVENT_REORDER, 'iframe');
yield invokeSetAttribute(browser, 'iframe', 'src',
`data:text/html,<html><body id="${id}"><input></body></html>`);
event = yield onReorder;
tree = {
INTERNAL_FRAME: [
{ DOCUMENT: [
{ ENTRY: [ ] }
] }
]
};
testAccessibleTree(event.accessible, tree);
iframe = findAccessibleChildByID(event.accessible, id);
/* ================= Replace iframe body on ARIA role body ================ */
onReorder = waitForEvent(EVENT_REORDER, id);
yield ContentTask.spawn(browser, id, id => {
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 = id;
docNode.documentElement.replaceChild(newBodyNode, docNode.body);
});
yield onReorder;
tree = {
role: ROLE_PUSHBUTTON,
children: [
{
role: ROLE_TEXT_LEAF,
name: 'New Hello'
}
]
};
testAccessibleTree(iframe, tree);
});

Просмотреть файл

@ -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);
});

Просмотреть файл

@ -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: [ ] } ] });
});

Просмотреть файл

@ -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, id => {
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(id).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);
});

Просмотреть файл

@ -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);
});

Просмотреть файл

@ -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: [] }
]
} ]
});
});

Просмотреть файл

@ -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_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);
onReorder = waitForEvent(EVENT_REORDER, 'body');
// Remove an event listener from parent.
yield ContentTask.spawn(browser, {}, () => {
content.document.getElementById('parent').removeEventListener(
'click', content.window.dummyListener);
delete content.window.dummyListener;
});
yield onReorder;
is(findAccessibleChildByID(accDoc, 'parent'), null,
'Check that parent is not accessible.');
is(findAccessibleChildByID(accDoc, 'child'), null,
'Check that child is not accessible.');
});

Просмотреть файл

@ -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 select = 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);
}
select.add(optGroup, null);
for (let i = 0; i < 2; i++) {
let opt = doc.createElement('option');
select.add(opt, null);
}
select.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 select = content.document.getElementById('select');
select.removeChild(select.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 select = content.document.getElementById('select');
while (select.length) {
select.remove(0);
}
});
yield onEvent;
tree = {
COMBOBOX: [ {
COMBOBOX_LIST: [ ]
} ]
};
testAccessibleTree(select, tree);
});

Просмотреть файл

@ -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');
});

Просмотреть файл

@ -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);
});

Просмотреть файл

@ -0,0 +1,34 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
/* 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, id =>
content.document.getElementById(id).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);
});

Просмотреть файл

@ -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: []
}]
}]
});
});

Просмотреть файл

@ -0,0 +1,80 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
/* global EVENT_REORDER */
loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR });
addAccessibleTask(`
<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>`, 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);
});

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -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>

Просмотреть файл

@ -0,0 +1,104 @@
/* 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_STATE_CHANGE, 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;
/**
* 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 waits for an accessible event of certain type that
* belongs to a certain DOMNode (defined by its id).
* @param {String} id 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, id) {
return new Promise(resolve => {
let eventObserver = {
observe(subject, topic, data) {
if (topic !== 'accessible-event') {
return;
}
let event = subject.QueryInterface(nsIAccessibleEvent);
if (Logger.enabled) {
Logger.log(eventToString(event));
}
let domID = getAccessibleDOMNodeID(event.accessible);
// If event's accessible does not match expected event type or DOMNode
// id, skip thie event.
if (domID === id && event.eventType === eventType) {
if (Logger.enabled) {
Logger.log(`Correct event DOMNode id: ${id}`);
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;
})
));
}

Просмотреть файл

@ -0,0 +1,266 @@
/* 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 */
/* exported Logger, MOCHITESTS_DIR, isDefunct, addAccessibleTask,
invokeSetAttribute, invokeFocus, invokeSetStyle,
findAccessibleChildByID, getAccessibleDOMNodeID */
const { interfaces: Ci, utils: Cu } = Components;
Cu.import('resource://gre/modules/Services.jsm');
/**
* 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) {
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) {
try {
let extState = {};
accessible.getState({}, extState);
return extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
} catch (x) {
return true;
}
}
/**
* 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) {
return ContentTask.spawn(browser, { id, attr, value },
({ id, attr, value }) => {
let elm = content.document.getElementById(id);
if (value) {
elm.setAttribute(attr, value);
} else {
elm.removeAttribute(attr);
}
});
}
/**
* 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) {
return ContentTask.spawn(browser, { id, style, value },
({ id, style, value }) => {
let elm = content.document.getElementById(id);
if (value) {
elm.style[style] = value;
} else {
delete elm.style[style];
}
});
}
/**
* 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) {
return ContentTask.spawn(browser, id, id => {
let elm = content.document.getElementById(id);
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);
}
}
/**
* 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}${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>`;
}
yield BrowserTestUtils.withNewTab({
gBrowser,
url: url
}, function*(browser) {
yield SimpleTest.promiseFocus(browser);
loadFrameScripts(browser,
'let { document, window, navigator } = content;',
{ name: 'common.js', dir: MOCHITESTS_DIR });
info(`e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
info(`Actually remote browser: ${browser.isRemoteBrowser}`);
let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
browser.reload();
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 }, 'events.js');

Просмотреть файл

@ -88,6 +88,11 @@ const kSquareBulletText = String.fromCharCode(0x25fe) + " ";
const MAX_TRIM_LENGTH = 100;
/**
* Services to determine if e10s is enabled.
*/
Components.utils.import('resource://gre/modules/Services.jsm');
/**
* nsIAccessibleRetrieval service.
*/
@ -737,6 +742,31 @@ function getTextFromClipboard()
return "";
}
/**
* Extract DOMNode id from an accessible. If e10s is enabled, DOMNode is not
* present in parent process but, if available, DOMNode id is attached to an
* accessible object.
* @param {nsIAccessible} accessible accessible
* @return {String?} DOMNode id if available
*/
function getAccessibleDOMNodeID(accessible) {
if (accessible instanceof nsIAccessibleDocument) {
// If accessible is a document, trying to find its document body id.
try {
return accessible.DOMNode.body.id;
} catch (e) { /* This only works if accessible is not a proxy. */ }
}
try {
return accessible.DOMNode.id;
} catch (e) { /* This will fail if DOMNode is in different process. */ }
try {
// When e10s is enabled, accessible will have an "id" property if its
// corresponding DOMNode has an id. If accessible is a document, its "id"
// property corresponds to the "id" of its body element.
return accessible.id;
} catch (e) { /* This will fail if accessible is not a proxy. */ }
}
/**
* Return pretty name for identifier, it may be ID, DOM node or accessible.
*/
@ -755,10 +785,17 @@ function prettyName(aIdentifier)
if (aIdentifier instanceof nsIAccessible) {
var acc = getAccessible(aIdentifier);
var domID = getAccessibleDOMNodeID(acc);
var msg = "[";
try {
msg += getNodePrettyName(acc.DOMNode);
msg += ", role: " + roleToString(acc.role);
if (Services.appinfo.browserTabsRemoteAutostart) {
if (domID) {
msg += `DOM node id: ${domID}, `;
}
} else {
msg += `${getNodePrettyName(acc.DOMNode)}, `;
}
msg += "role: " + roleToString(acc.role);
if (acc.name)
msg += ", name: '" + shortenString(acc.name) + "'";
} catch (e) {