diff options
Diffstat (limited to '')
41 files changed, 3972 insertions, 0 deletions
diff --git a/accessible/tests/browser/e10s/browser.ini b/accessible/tests/browser/e10s/browser.ini new file mode 100644 index 0000000000..a582b9b8bb --- /dev/null +++ b/accessible/tests/browser/e10s/browser.ini @@ -0,0 +1,60 @@ +[DEFAULT] +support-files = + 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/browser/*.jsm + !/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] +skip-if = (os == "linux" && bits == 64) || (debug && os == "mac") || (debug && os == "win") #Bug 1388256 +[browser_caching_relations.js] +[browser_caching_states.js] +[browser_caching_value.js] +[browser_caching_uniqueid.js] + +# Events tests +[browser_events_announcement.js] +skip-if = e10s && os == 'win' # Bug 1288839 +[browser_events_caretmove.js] +[browser_events_hide.js] +[browser_events_show.js] +[browser_events_statechange.js] +[browser_events_textchange.js] +[browser_events_vcchange.js] + +# Text tests +[browser_text_paragraph_boundary.js] + +# Tree update tests +[browser_treeupdate_ariadialog.js] +[browser_treeupdate_ariaowns.js] +[browser_treeupdate_canvas.js] +skip-if = (os == 'win' && os_version == '10.0' && bits == 64 && !debug) #Bug 1462638 - Disabled on Win10 opt/pgo for frequent failures +[browser_treeupdate_cssoverflow.js] +[browser_treeupdate_doc.js] +skip-if = e10s && os == 'win' # Bug 1288839 +[browser_treeupdate_gencontent.js] +[browser_treeupdate_hidden.js] +[browser_treeupdate_imagemap.js] +[browser_treeupdate_list.js] +[browser_treeupdate_list_editabledoc.js] +[browser_treeupdate_listener.js] +[browser_treeupdate_optgroup.js] +[browser_treeupdate_removal.js] +[browser_treeupdate_select_dropdown.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 0000000000..b8e157b068 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_attributes.js @@ -0,0 +1,134 @@ +/* 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"; + +/* import-globals-from ../../mochitest/attributes.js */ +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-block", +}; + +/** + * 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 {?AsyncFunction} an optional action that awaits 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", + async action(browser) { + await 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">`, + async 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) { + await action(browser); + } else if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, "textbox", attr, value); + } + } + + await onUpdate; + testAttrs(textbox, expected); + testAbsentAttrs(textbox, unexpected); + } + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..5b9e70ce07 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_description.js @@ -0,0 +1,214 @@ +/* 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"; + +/* import-globals-from ../../mochitest/name.js */ +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: [[EVENT_DESCRIPTION_CHANGE, "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: [[EVENT_REORDER, matchContentDoc]], + expected: "", + }, + { + desc: + "Description from @aria-describedby attribute when @alt and " + + "@aria-describedby are not the same", + attrs: [ + { + attr: "aria-describedby", + value: "description2", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "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: [[EVENT_REORDER, matchContentDoc]], + 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: [[EVENT_NAME_CHANGE, "image"]], + expected: "", + }, + { + desc: "No description with only @title attribute which is used as the name", + attrs: [ + { + attr: "aria-describedby", + }, + ], + waitFor: [[EVENT_DESCRIPTION_CHANGE, "image"]], + expected: "", + }, + { + desc: + "Description from @title attribute when @alt and @atitle are not the " + + "same", + attrs: [ + { + attr: "alt", + value: "aria description", + }, + ], + waitFor: [[EVENT_REORDER, matchContentDoc]], + 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: [[EVENT_NAME_CHANGE, "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: [[EVENT_DESCRIPTION_CHANGE, "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: [[EVENT_DESCRIPTION_CHANGE, "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: [[EVENT_NAME_CHANGE, "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: [[EVENT_DESCRIPTION_CHANGE, "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" />`, + async function(browser, accDoc) { + let imgAcc = findAccessibleChildByID(accDoc, "image"); + + for (let { desc, waitFor, attrs, expected } of tests) { + info(desc); + let onUpdate; + if (waitFor) { + onUpdate = waitForOrderedEvents(waitFor); + } + if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, "image", attr, value); + } + } + await onUpdate; + // When attribute change (alt) triggers reorder event, accessible will + // become defunct. + if (isDefunct(imgAcc)) { + imgAcc = findAccessibleChildByID(accDoc, "image"); + } + testDescr(imgAcc, expected); + } + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..cac05a04de --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_name.js @@ -0,0 +1,539 @@ +/* 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"; + +/* import-globals-from ../../mochitest/name.js */ +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: + * * waitFor - changes in the subtree will result in an accessible event + * being fired, the test must only continue after the event + * is receieved. + */ +const ARIARule = [ + { attr: "aria-labelledby" }, + { attr: "aria-label", waitFor: EVENT_NAME_CHANGE }, +]; +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", waitFor: EVENT_NAME_CHANGE }, + { attr: "title" }, + ], + HTMLImgEmptyAlt: [...ARIARule, { attr: "title" }, { attr: "alt" }], + HTMLInputButton: [ + ...HTMLControlHeadRule, + { attr: "value" }, + { attr: "title" }, + ], + HTMLInputImage: [ + ...HTMLControlHeadRule, + { attr: "alt", waitFor: EVENT_NAME_CHANGE }, + { attr: "value" }, + { attr: "title" }, + ], + HTMLInputImageNoValidSrc: [ + ...HTMLControlHeadRule, + { attr: "alt", waitFor: EVENT_NAME_CHANGE }, + { attr: "value" }, + ], + HTMLInputReset: [ + ...HTMLControlHeadRule, + { attr: "value", waitFor: EVENT_TEXT_INSERTED }, + ], + HTMLInputSubmit: [ + ...HTMLControlHeadRule, + { attr: "value", waitFor: EVENT_TEXT_INSERTED }, + ], + 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 link \u2022 Listitem1 \u2022 Listitem2", + "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: ` + <div role="main"> + <style> + button::before { + content: "do not "; + } + </style> + <button id="btn">press me</button> + </div>`, + 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"], + }, +]; + +/** + * 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 + */ +async function testAttrRule(browser, target, rule, expected) { + let { id, parent, acc } = target; + let { waitFor, attr } = rule; + + testName(acc, expected); + + if (waitFor) { + let [event] = await contentSpawnMutation( + browser, + { + expected: [[waitFor, waitFor === EVENT_REORDER ? parent : id]], + }, + (contentId, contentAttr) => + content.document.getElementById(contentId).removeAttribute(contentAttr), + [id, attr] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); + } else { + await invokeSetAttribute(browser, id, attr); + } +} + +/** + * 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 + */ +async function testElmRule(browser, target, rule, expected) { + let { id, parent, acc } = target; + let { isSibling, elm } = rule; + + testName(acc, expected); + let [event] = await contentSpawnMutation( + browser, + { + expected: [[EVENT_REORDER, isSibling ? parent : id]], + }, + contentElm => content.document.querySelector(`${contentElm}`).remove(), + [elm] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); +} + +/** + * 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 + */ +async function testSubtreeRule(browser, target, rule, expected) { + let { id, acc } = target; + + testName(acc, expected); + let [event] = await contentSpawnMutation( + browser, + { + expected: [[EVENT_REORDER, id]], + }, + contentId => { + let elm = content.document.getElementById(contentId); + while (elm.firstChild) { + elm.firstChild.remove(); + } + }, + [id] + ); + + // Update accessible just in case it is now defunct. + target.acc = findAccessibleChildByID(event.accessible, id); +} + +/** + * 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 + */ +async 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; + } + await testFn(browser, target, rule, expected[i]); + } +} + +markupTests.forEach(({ id, ruleset, markup, expected }) => + addAccessibleTask( + markup, + async function(browser, accDoc) { + const observer = { + observe(subject, topic, data) { + const event = subject.QueryInterface(nsIAccessibleEvent); + console.log(eventToString(event)); + }, + }; + Services.obs.addObserver(observer, "accessible-event"); + // 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 }; + await testNameRule(browser, target, rules[ruleset], expected); + Services.obs.removeObserver(observer, "accessible-event"); + }, + { iframe: true, remoteIframe: true } + ) +); 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 0000000000..38d3a0c63b --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_relations.js @@ -0,0 +1,96 @@ +/* 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"; + +/* import-globals-from ../../mochitest/relations.js */ +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], +]; + +async 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) { + await 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>`, + async function(browser, accDoc) { + for (let spec of attrRelationsSpec) { + await testRelated(browser, accDoc, ...spec); + } + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..e6dbc33d65 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_states.js @@ -0,0 +1,154 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +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], + }, +]; + +async 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) { + await invokeSetAttribute(browser, id, attr, value); + } + await onUpdate; + testStates(acc, ...expected); + } +} + +/** + * Test caching of accessible object states + */ +addAccessibleTask( + ` + <input id="checkbox" type="checkbox"> + <input id="file" type="file"> + <input id="text">`, + async function(browser, accDoc) { + await runStateTests(browser, accDoc, "checkbox", attributeTests); + await runStateTests(browser, accDoc, "file", ariaTests); + await runStateTests(browser, accDoc, "text", extraStateTests); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_caching_uniqueid.js b/accessible/tests/browser/e10s/browser_caching_uniqueid.js new file mode 100644 index 0000000000..287f896c36 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_uniqueid.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"; + +/** + * Test UniqueID property. + */ +addAccessibleTask( + '<div id="div"></div>', + async function(browser, accDoc) { + const div = findAccessibleChildByID(accDoc, "div"); + const accUniqueID = await invokeContentTask(browser, [], () => { + const accService = Cc["@mozilla.org/accessibilityService;1"].getService( + Ci.nsIAccessibilityService + ); + + return accService.getAccessibleFor(content.document.getElementById("div")) + .uniqueID; + }); + + is( + accUniqueID, + div.uniqueID, + "Both proxy and the accessible return correct unique ID." + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..c727fc5252 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_caching_value.js @@ -0,0 +1,195 @@ +/* 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"; + +/* import-globals-from ../../mochitest/value.js */ +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 {?AsyncFunction} an optional action that awaits 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", + async action(browser) { + await invokeFocus(browser, "select"); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("3", {}, content); + }); + }, + 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!", 3.5, 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", + async action(browser) { + await invokeFocus(browser, "range"); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_LEFT", {}, content); + }); + }, + 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">`, + async 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) { + await action(browser); + } else if (attrs) { + for (let { attr, value } of attrs) { + await invokeSetAttribute(browser, id, attr, value); + } + } + + await onUpdate; + if (Array.isArray(expected)) { + acc.QueryInterface(nsIAccessibleValue); + testValue(acc, ...expected); + } else { + is(acc.value, expected, `Correct value for ${prettyName(acc)}`); + } + } + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_announcement.js b/accessible/tests/browser/e10s/browser_events_announcement.js new file mode 100644 index 0000000000..2de6d4b005 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_announcement.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"; + +addAccessibleTask( + `<p id="p">abc</p>`, + async function(browser, accDoc) { + let acc = findAccessibleChildByID(accDoc, "p"); + let onAnnounce = waitForEvent(EVENT_ANNOUNCEMENT, acc); + acc.announce("please", nsIAccessibleAnnouncementEvent.POLITE); + let evt = await onAnnounce; + evt.QueryInterface(nsIAccessibleAnnouncementEvent); + is(evt.announcement, "please", "announcement matches."); + is(evt.priority, nsIAccessibleAnnouncementEvent.POLITE, "priority matches"); + + onAnnounce = waitForEvent(EVENT_ANNOUNCEMENT, acc); + acc.announce("do it", nsIAccessibleAnnouncementEvent.ASSERTIVE); + evt = await onAnnounce; + evt.QueryInterface(nsIAccessibleAnnouncementEvent); + is(evt.announcement, "do it", "announcement matches."); + is( + evt.priority, + nsIAccessibleAnnouncementEvent.ASSERTIVE, + "priority matches" + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..a39d16e710 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_caretmove.js @@ -0,0 +1,22 @@ +/* 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"; + +/** + * Test caret move event and its interface: + * - caretOffset + */ +addAccessibleTask( + '<input id="textbox" value="hello"/>', + async function(browser) { + let onCaretMoved = waitForEvent(EVENT_TEXT_CARET_MOVED, "textbox"); + await invokeFocus(browser, "textbox"); + let event = await onCaretMoved; + + let caretMovedEvent = event.QueryInterface(nsIAccessibleCaretMoveEvent); + is(caretMovedEvent.caretOffset, 5, "Correct caret offset."); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..d46921d051 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_hide.js @@ -0,0 +1,44 @@ +/* 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"; + +/** + * 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>`, + async function(browser, accDoc) { + let acc = findAccessibleChildByID(accDoc, "to-hide"); + let onHide = waitForEvent(EVENT_HIDE, acc); + await invokeSetStyle(browser, "to-hide", "visibility", "hidden"); + let event = await 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." + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..d464d8fb9d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_show.js @@ -0,0 +1,22 @@ +/* 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"; + +/** + * Test show event + */ +addAccessibleTask( + '<div id="div" style="visibility: hidden;"></div>', + async function(browser) { + let onShow = waitForEvent(EVENT_SHOW, "div"); + await invokeSetStyle(browser, "div", "visibility", "visible"); + let showEvent = await onShow; + ok( + showEvent.accessibleDocument instanceof nsIAccessibleDocument, + "Accessible document not present." + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..a027a974e4 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_statechange.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"; + +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +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" />`, + async function(browser) { + // Test state change + let onStateChange = waitForEvent(EVENT_STATE_CHANGE, "checkbox"); + // Set checked for a checkbox. + await invokeContentTask(browser, [], () => { + content.document.getElementById("checkbox").checked = true; + }); + let event = await 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. + await invokeContentTask(browser, [], () => { + content.document.getElementById("iframe").contentDocument.designMode = + "on"; + }); + event = await onStateChange; + + checkStateChangeEvent(event, EXT_STATE_EDITABLE, true, true); + testStates(event.accessible, 0, EXT_STATE_EDITABLE); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..1e822dc65a --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_textchange.js @@ -0,0 +1,114 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"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)}` + ); + ok( + tcEvent.accessibleDocument instanceof nsIAccessibleDocument, + "Accessible document not present." + ); +} + +async function changeText(browser, id, value, events) { + let onEvents = waitForOrderedEvents( + events.map(({ isInserted }) => { + let eventType = isInserted ? EVENT_TEXT_INSERTED : EVENT_TEXT_REMOVED; + return [eventType, id]; + }) + ); + // Change text in the subtree. + await invokeContentTask(browser, [id, value], (contentId, contentValue) => { + content.document.getElementById( + contentId + ).firstChild.textContent = contentValue; + }); + let resolvedEvents = await onEvents; + + events.forEach(({ isInserted, str, offset }, idx) => + checkTextChangeEvent( + resolvedEvents[idx], + id, + str, + offset, + offset + str.length, + isInserted, + false + ) + ); +} + +async function removeTextFromInput(browser, id, value, start, end) { + let onTextRemoved = waitForEvent(EVENT_TEXT_REMOVED, id); + // Select text and delete it. + await invokeContentTask( + browser, + [id, start, end], + (contentId, contentStart, contentEnd) => { + let el = content.document.getElementById(contentId); + el.focus(); + el.setSelectionRange(contentStart, contentEnd); + } + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.sendChar("VK_DELETE", content); + }); + + let event = await 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" />`, + async function(browser) { + let events = [ + { isInserted: false, str: "abc", offset: 0 }, + { isInserted: true, str: "def", offset: 0 }, + ]; + await changeText(browser, "p", "def", events); + + events = [{ isInserted: true, str: "DEF", offset: 2 }]; + await changeText(browser, "p", "deDEFf", events); + + // Test isFromUserInput property. + await removeTextFromInput(browser, "input", "n", 1, 2); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_events_vcchange.js b/accessible/tests/browser/e10s/browser_events_vcchange.js new file mode 100644 index 0000000000..bf649fe045 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_events_vcchange.js @@ -0,0 +1,87 @@ +/* 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"; + +addAccessibleTask( + ` + <p id="p1">abc</p> + <input id="input1" value="input" />`, + async function(browser) { + let onVCChanged = waitForEvent( + EVENT_VIRTUALCURSOR_CHANGED, + matchContentDoc + ); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.position = CommonUtils.getAccessible( + "p1", + null, + null, + null, + content.document + ); + }); + let vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + is(vccEvent.newAccessible.id, "p1", "New position is correct"); + is(vccEvent.newStartOffset, -1, "New start offset is correct"); + is(vccEvent.newEndOffset, -1, "New end offset is correct"); + ok(!vccEvent.isFromUserInput, "not user initiated"); + + onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.moveNextByText(Ci.nsIAccessiblePivot.CHAR_BOUNDARY); + }); + vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + is(vccEvent.newAccessible.id, vccEvent.oldAccessible.id, "Same position"); + is(vccEvent.newStartOffset, 0, "New start offset is correct"); + is(vccEvent.newEndOffset, 1, "New end offset is correct"); + ok(vccEvent.isFromUserInput, "user initiated"); + + onVCChanged = waitForEvent(EVENT_VIRTUALCURSOR_CHANGED, matchContentDoc); + await invokeContentTask(browser, [], () => { + const { CommonUtils } = ChromeUtils.import( + "chrome://mochitests/content/browser/accessible/tests/browser/Common.jsm" + ); + let vc = CommonUtils.getAccessible( + content.document, + Ci.nsIAccessibleDocument + ).virtualCursor; + vc.position = CommonUtils.getAccessible( + "input1", + null, + null, + null, + content.document + ); + }); + vccEvent = (await onVCChanged).QueryInterface( + nsIAccessibleVirtualCursorChangeEvent + ); + isnot(vccEvent.oldAccessible, vccEvent.newAccessible, "positions differ"); + is(vccEvent.oldAccessible.id, "p1", "Old position is correct"); + is(vccEvent.newAccessible.id, "input1", "New position is correct"); + is(vccEvent.newStartOffset, -1, "New start offset is correct"); + is(vccEvent.newEndOffset, -1, "New end offset is correct"); + ok(!vccEvent.isFromUserInput, "not user initiated"); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js b/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js new file mode 100644 index 0000000000..04e64520e8 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_text_paragraph_boundary.js @@ -0,0 +1,22 @@ +/* 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"; + +// Test that we don't crash the parent process when querying the paragraph +// boundary on an Accessible which has remote ProxyAccessible descendants. +addAccessibleTask( + `test`, + async function testParagraphBoundaryWithRemoteDescendants(browser, accDoc) { + const root = getRootAccessible(document).QueryInterface( + Ci.nsIAccessibleText + ); + let start = {}; + let end = {}; + // The offsets will change as the Firefox UI changes. We don't really care + // what they are, just that we don't crash. + root.getTextAtOffset(0, nsIAccessibleText.BOUNDARY_PARAGRAPH, start, end); + ok(true, "Getting paragraph boundary succeeded"); + } +); 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 0000000000..8b4a575d75 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js @@ -0,0 +1,45 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +// Test ARIA Dialog +addAccessibleTask( + "e10s/doc_treeupdate_ariadialog.html", + async function(browser, accDoc) { + testAccessibleTree(accDoc, { + role: ROLE_DOCUMENT, + children: [], + }); + + // Make dialog visible and update its inner content. + let onShow = waitForEvent(EVENT_SHOW, "dialog"); + await invokeContentTask(browser, [], () => { + content.document.getElementById("dialog").style.display = "block"; + }); + await onShow; + + testAccessibleTree(accDoc, { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_DIALOG, + children: [ + { + role: ROLE_PUSHBUTTON, + children: [{ role: ROLE_TEXT_LEAF }], + }, + { + role: ROLE_ENTRY, + }, + ], + }, + ], + }); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..dfd6401c48 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js @@ -0,0 +1,294 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async 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); + await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv"); + await 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); + await invokeSetAttribute(browser, id, "aria-owns"); + await onReorder; + + // children follow the DOM order + tree = { + SECTION: [{ PUSHBUTTON: [] }, { CHECKBUTTON: [{ SECTION: [] }] }], + }; + testAccessibleTree(acc, tree); + + /* ================ Set ARIA owns ========================================= */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, id, "aria-owns", "t1_button t1_subdiv"); + await 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); + await invokeSetAttribute( + browser, + id, + "aria-owns", + "t1_button t1_subdiv t1_group" + ); + await 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); + await invokeContentTask(browser, [id], contentId => { + let div = content.document.createElement("div"); + div.setAttribute("id", "t1_child3"); + div.setAttribute("role", "radio"); + content.document.getElementById(contentId).appendChild(div); + }); + await 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); + await invokeContentTask(browser, [], () => { + content.document.getElementById("t1_span").remove(); + }); + await 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); + await invokeSetAttribute(browser, "t1_group", "id"); + await onReorder; + + tree = { + SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] }, + { PUSHBUTTON: [] }, // ARIA owned, t1_button + ], + }; + testAccessibleTree(acc, tree); + + /* ================ Set ID ================================================ */ + onReorder = waitForEvent(EVENT_REORDER, docID); + await invokeSetAttribute(browser, "t1_grouptmp", "id", "t1_group"); + await onReorder; + + tree = { + SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] }, + { PUSHBUTTON: [] }, // ARIA owned, t1_button + { GROUPING: [] }, // ARIA owned, t1_group, previously t1_grouptmp + ], + }; + testAccessibleTree(acc, tree); +} + +async 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); + await invokeContentTask(browser, [], () => { + content.document + .getElementById("t2_container2") + .removeChild(content.document.getElementById("t2_container3")); + }); + await onReorder; + + tree = { + SECTION: [], + }; + testAccessibleTree(acc, tree); +} + +async function stealAndRecacheChildren(browser, accDoc) { + const id1 = "t3_container1"; + const id2 = "t3_container2"; + const acc1 = findAccessibleChildByID(accDoc, id1); + const acc2 = findAccessibleChildByID(accDoc, id2); + + /* ================ Attempt to steal from other ARIA owns ================= */ + let onReorder = waitForEvent(EVENT_REORDER, id2); + await invokeSetAttribute(browser, id2, "aria-owns", "t3_child"); + await invokeContentTask(browser, [id2], id => { + let div = content.document.createElement("div"); + div.setAttribute("role", "radio"); + content.document.getElementById(id).appendChild(div); + }); + await onReorder; + + let tree = { + SECTION: [ + { CHECKBUTTON: [] }, // ARIA owned + ], + }; + testAccessibleTree(acc1, tree); + + tree = { + SECTION: [{ RADIOBUTTON: [] }], + }; + testAccessibleTree(acc2, tree); +} + +async 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); + await invokeSetStyle(browser, "t4_child1", "display", "block"); + await onReorder; + + tree = { + SECTION: [{ CHECKBUTTON: [] }, { RADIOBUTTON: [] }], + }; + testAccessibleTree(acc, tree); +} + +async 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); + await invokeSetAttribute(browser, id, "aria-owns", val); + await onReorder; + + let tree = { SECTION: [] }; + for (let role of roleList) { + let ch = {}; + ch[role] = []; + tree.SECTION.push(ch); + } + testAccessibleTree(acc, tree); + } +} + +async 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); + await invokeContentTask(browser, [id], contentId => { + content.document + .getElementById(contentId) + .removeChild(content.document.getElementById("t6_span")); + }); + await onReorder; + + tree = { + SECTION: [{ GROUPING: [] }], + }; + testAccessibleTree(acc, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_ariaowns.html", + async function(browser, accDoc) { + await testContainer1(browser, accDoc); + await removeContainer(browser, accDoc); + await stealAndRecacheChildren(browser, accDoc); + await showHiddenElement(browser, accDoc); + await rearrangeARIAOwns(browser, accDoc); + await removeNotARIAOwnedEl(browser, accDoc); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..5fcd1eb773 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_canvas.js @@ -0,0 +1,28 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + <canvas id="canvas"> + <div id="dialog" role="dialog" style="display: none;"></div> + </canvas>`, + async function(browser, accDoc) { + let canvas = findAccessibleChildByID(accDoc, "canvas"); + let dialog = findAccessibleChildByID(accDoc, "dialog"); + + testAccessibleTree(canvas, { CANVAS: [] }); + + let onShow = waitForEvent(EVENT_SHOW, "dialog"); + await invokeSetStyle(browser, "dialog", "display", "block"); + await onShow; + + testAccessibleTree(dialog, { DIALOG: [] }); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..629f9fb89f --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js @@ -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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + <div id="container"><div id="scrollarea" style="overflow:auto;"><input>`, + async function(browser, accDoc) { + const id1 = "container"; + const container = findAccessibleChildByID(accDoc, id1); + + /* ================= Change scroll range ================================== */ + let tree = { + SECTION: [ + { + // container + SECTION: [ + { + // scroll area + ENTRY: [], // child content + }, + ], + }, + ], + }; + testAccessibleTree(container, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id1); + await invokeContentTask(browser, [id1], id => { + let doc = content.document; + doc.getElementById("scrollarea").style.width = "20px"; + doc.getElementById(id).appendChild(doc.createElement("input")); + }); + await onReorder; + + tree = { + SECTION: [ + { + // container + SECTION: [ + { + // scroll area + ENTRY: [], // child content + }, + ], + }, + { + ENTRY: [], // inserted input + }, + ], + }; + testAccessibleTree(container, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..98f399695c --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_doc.js @@ -0,0 +1,320 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +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>`, + async 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); + await invokeContentTask(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); + }); + await 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); + await invokeContentTask(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); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Replace iframe body ================================== */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(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", "application"); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }); + await reorderEventPromise; + + tree = { + role: ROLE_APPLICATION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + + /* ================= Open iframe document ================================= */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(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); + }); + await reorderEventPromise; + + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Close iframe document ================================ */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + // Write and close document. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.write("Works?"); + docNode.close(); + }); + await 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); + await invokeContentTask(browser, [], () => { + // Remove HTML element. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.firstChild.remove(); + }); + let event = await 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); + await invokeContentTask(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); + }); + await 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); + await invokeContentTask(browser, [], () => { + // Remove body element. + let docNode = content.document.getElementById("iframe").contentDocument; + docNode.documentElement.removeChild(docNode.body); + }); + event = await 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); + await invokeContentTask(browser, [], () => { + let docNode = content.document.getElementById("iframe").contentDocument; + let inputNode = (content.window.inputNode = docNode.createElement( + "input" + )); + docNode.documentElement.appendChild(inputNode); + }); + event = await reorderEventPromise; + + ok( + event.accessible instanceof nsIAccessibleDocument, + "Reorder should happen on the document" + ); + tree = { + DOCUMENT: [{ ENTRY: [] }], + }; + testAccessibleTree(iframe, tree); + + reorderEventPromise = waitForEvent(EVENT_REORDER, iframe); + await invokeContentTask(browser, [], () => { + let docEl = content.document.getElementById("iframe").contentDocument + .documentElement; + // Remove aftermath of this test before next test starts. + docEl.firstChild.remove(); + }); + // Make sure reorder event was fired and that the input was removed. + await reorderEventPromise; + tree = { + role: ROLE_DOCUMENT, + children: [], + }; + testAccessibleTree(iframe, tree); + + /* ================= Insert body to iframe document ======================= */ + reorderEventPromise = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(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); + }); + await 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"); + await invokeSetAttribute( + browser, + "iframe", + "src", + `data:text/html,<html><body id="${id}"><input></body></html>` + ); + event = await 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); + await invokeContentTask(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", "application"); + newBodyNode.id = contentId; + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }); + await reorderEventPromise; + + tree = { + role: ROLE_APPLICATION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Hello", + }, + ], + }; + testAccessibleTree(iframe, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..ca1150f9dd --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_gencontent.js @@ -0,0 +1,94 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +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>`, + async 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 + await invokeContentTask(browser, [id1], id => { + let node = content.document.createElement("div"); + node.textContent = "text"; + node.setAttribute("class", "gentext"); + content.document.getElementById(id).appendChild(node); + }); + await onReorder; + + tree = { + SECTION: [ + // container + { + SECTION: [ + // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ], + }, + ], + }; + testAccessibleTree(container1, tree); + + onReorder = waitForEvent(EVENT_REORDER, "container2_child"); + // Add CSS generated content to an element in container2's subtree + await invokeSetAttribute(browser, "container2_child", "class", "gentext"); + await onReorder; + + tree = { + SECTION: [ + // container2 + { + SECTION: [ + // container2 child + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ], + }, + ], + }; + testAccessibleTree(container2, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..725999db36 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_hidden.js @@ -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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function setHidden(browser, value) { + let onReorder = waitForEvent(EVENT_REORDER, "container"); + await invokeSetAttribute(browser, "child", "hidden", value); + await onReorder; +} + +addAccessibleTask( + '<div id="container"><input id="child"></div>', + async function(browser, accDoc) { + let container = findAccessibleChildByID(accDoc, "container"); + + testAccessibleTree(container, { SECTION: [{ ENTRY: [] }] }); + + // Set @hidden attribute + await setHidden(browser, "true"); + testAccessibleTree(container, { SECTION: [] }); + + // Remove @hidden attribute + await setHidden(browser); + testAccessibleTree(container, { SECTION: [{ ENTRY: [] }] }); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..e206b83276 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_imagemap.js @@ -0,0 +1,187 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async 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); + await invokeContentTask(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); + }); + await 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); + await invokeContentTask(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); + }); + await 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); + await invokeContentTask(browser, [], () => { + let mapNode = content.document.getElementById("map"); + mapNode.removeChild(mapNode.firstElementChild); + }); + await onReorder; + + tree = { + IMAGE_MAP: [ + { role: ROLE_LINK, name: "b", children: [] }, + { role: ROLE_LINK, name: "c", children: [] }, + ], + }; + testAccessibleTree(acc, tree); +} + +async function testContainer(browser) { + const id = "container"; + /* ================= Remove name on map =================================== */ + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, "map", "name"); + let event = await onReorder; + const acc = event.accessible; + + let tree = { + SECTION: [{ GRAPHIC: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================= Restore name on map ================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetAttribute(browser, "map", "name", "atoz_map"); + // XXX: force repainting of the image (see bug 745788 for details). + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeMouse( + content.document.getElementById("imgmap"), + 10, + 10, + { type: "mousemove" }, + content + ); + }); + await onReorder; + + tree = { + SECTION: [ + { + IMAGE_MAP: [{ LINK: [] }, { LINK: [] }], + }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Remove map =========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [], () => { + let mapNode = content.document.getElementById("map"); + mapNode.remove(); + }); + await onReorder; + + tree = { + SECTION: [{ GRAPHIC: [] }], + }; + testAccessibleTree(acc, tree); + + /* ================= Insert map =========================================== */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(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); + }); + await onReorder; + + tree = { + SECTION: [ + { + IMAGE_MAP: [{ LINK: [] }], + }, + ], + }; + testAccessibleTree(acc, tree); + + /* ================= Hide image map ======================================= */ + onReorder = waitForEvent(EVENT_REORDER, id); + await invokeSetStyle(browser, "imgmap", "display", "none"); + await onReorder; + + tree = { + SECTION: [], + }; + testAccessibleTree(acc, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_imagemap.html", + async function(browser, accDoc) { + await waitForImageMap(browser, accDoc); + await testImageMap(browser, accDoc); + await testContainer(browser); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..d14b983c10 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_list.js @@ -0,0 +1,52 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function setDisplayAndWaitForReorder(browser, value) { + let onReorder = waitForEvent(EVENT_REORDER, "ul"); + await invokeSetStyle(browser, "li", "display", value); + return onReorder; +} + +addAccessibleTask( + ` + <ul id="ul"> + <li id="li">item1</li> + </ul>`, + async function(browser, accDoc) { + let li = findAccessibleChildByID(accDoc, "li"); + let bullet = li.firstChild; + let accTree = { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_LISTITEM_MARKER, + children: [], + }, + { + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + testAccessibleTree(li, accTree); + + await setDisplayAndWaitForReorder(browser, "none"); + + ok(isDefunct(li), "Check that li is defunct."); + ok(isDefunct(bullet), "Check that bullet is defunct."); + + let event = await setDisplayAndWaitForReorder(browser, "list-item"); + + testAccessibleTree( + findAccessibleChildByID(event.accessible, "li"), + accTree + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..9c672f3c7c --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '<ol id="list"></ol>', + async function(browser, accDoc) { + let list = findAccessibleChildByID(accDoc, "list"); + + testAccessibleTree(list, { + role: ROLE_LIST, + children: [], + }); + + await invokeSetAttribute( + browser, + currentContentDoc(), + "contentEditable", + "true" + ); + let onReorder = waitForEvent(EVENT_REORDER, "list"); + await invokeContentTask(browser, [], () => { + let li = content.document.createElement("li"); + li.textContent = "item"; + content.document.getElementById("list").appendChild(li); + }); + await onReorder; + + testAccessibleTree(list, { + role: ROLE_LIST, + children: [ + { + role: ROLE_LISTITEM, + children: [ + { role: ROLE_LISTITEM_MARKER, name: "1. ", children: [] }, + { role: ROLE_TEXT_LEAF, children: [] }, + ], + }, + ], + }); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..35baf28667 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_listener.js @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '<span id="parent"><span id="child"></span></span>', + async 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, matchContentDoc); + // Add an event listener to parent. + await invokeContentTask(browser, [], () => { + content.window.dummyListener = () => {}; + content.document + .getElementById("parent") + .addEventListener("click", content.window.dummyListener); + }); + await onReorder; + + let tree = { TEXT: [] }; + testAccessibleTree(findAccessibleChildByID(accDoc, "parent"), tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..15fed8112d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_optgroup.js @@ -0,0 +1,103 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + '<select id="select"></select>', + async function(browser, accDoc) { + let select = findAccessibleChildByID(accDoc, "select"); + + let onEvent = waitForEvent(EVENT_REORDER, "select"); + // Create a combobox with grouping and 2 standalone options + await invokeContentTask(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 = await 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 + await invokeContentTask(browser, [], () => { + let contentSelect = content.document.getElementById("select"); + contentSelect.firstChild.remove(); + }); + await 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 + await invokeContentTask(browser, [], () => { + let contentSelect = content.document.getElementById("select"); + while (contentSelect.length) { + contentSelect.remove(0); + } + }); + await onEvent; + + tree = { + COMBOBOX: [ + { + COMBOBOX_LIST: [], + }, + ], + }; + testAccessibleTree(select, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..eb791525b3 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_removal.js @@ -0,0 +1,58 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "e10s/doc_treeupdate_removal.xhtml", + async 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, matchContentDoc); + await invokeContentTask(browser, [], () => { + content.document + .getElementById("the_displaynone") + .appendChild(content.document.getElementById("the_table")); + }); + await 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). + await invokeContentTask(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" + ); + }, + { iframe: true, remoteIframe: true } +); diff --git a/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js new file mode 100644 index 0000000000..add454ceac --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_select_dropdown.js @@ -0,0 +1,77 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +const snippet = ` +<select id="select"> + <option>o1</option> + <optgroup label="g1"> + <option>g1o1</option> + <option>g1o2</option> + </optgroup> + <optgroup label="g2"> + <option>g2o1</option> + <option>g2o2</option> + </optgroup> + <option>o2</option> +</select> +`; + +addAccessibleTask( + snippet, + async function(browser, accDoc) { + await invokeFocus(browser, "select"); + // Expand the select. A dropdown item should get focus. + // Note that the dropdown is rendered in the parent process. + let focused = waitForEvent( + EVENT_FOCUS, + event => event.accessible.role == ROLE_COMBOBOX_OPTION, + "Dropdown item focused after select expanded" + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_DOWN", { altKey: true }, content); + }); + let event = await focused; + let dropdown = event.accessible.parent; + + let selectedOptionChildren = []; + if (MAC) { + // Checkmark is part of the Mac menu styling. + selectedOptionChildren = [{ STATICTEXT: [] }]; + } + let tree = { + COMBOBOX_LIST: [ + { COMBOBOX_OPTION: selectedOptionChildren }, + { GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] }, + { GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] }, + { COMBOBOX_OPTION: [] }, + ], + }; + testAccessibleTree(dropdown, tree); + + // Collapse the select. Focus should return to the select. + focused = waitForEvent( + EVENT_FOCUS, + "select", + "select focused after collapsed" + ); + await invokeContentTask(browser, [], () => { + const { ContentTaskUtils } = ChromeUtils.import( + "resource://testing-common/ContentTaskUtils.jsm" + ); + const EventUtils = ContentTaskUtils.getEventUtils(content); + EventUtils.synthesizeKey("VK_ESCAPE", {}, content); + }); + await focused; + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..5c2903225a --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_table.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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + ` + <table id="table"> + <tr> + <td>cell1</td> + <td>cell2</td> + </tr> + </table>`, + async 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"); + await invokeContentTask(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); + }); + await onReorder; + + tree = { + TABLE: [ + { CAPTION: [{ TEXT_LEAF: [] }] }, + { ROW: [{ CELL: [{ TEXT_LEAF: [] }] }, { CELL: [{ TEXT_LEAF: [] }] }] }, + ], + }; + testAccessibleTree(table, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..6f89105b86 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_textleaf.js @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function removeTextData(browser, accessible, id, role) { + let tree = { + role, + children: [{ role: ROLE_TEXT_LEAF, name: "text" }], + }; + testAccessibleTree(accessible, tree); + + let onReorder = waitForEvent(EVENT_REORDER, id); + await invokeContentTask(browser, [id], contentId => { + content.document.getElementById(contentId).firstChild.textContent = ""; + }); + await onReorder; + + tree = { role, children: [] }; + testAccessibleTree(accessible, tree); +} + +addAccessibleTask( + ` + <p id="p">text</p> + <pre id="pre">text</pre>`, + async function(browser, accDoc) { + let p = findAccessibleChildByID(accDoc, "p"); + let pre = findAccessibleChildByID(accDoc, "pre"); + await removeTextData(browser, p, "p", ROLE_PARAGRAPH); + await removeTextData(browser, pre, "pre", ROLE_TEXT_CONTAINER); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..4583056586 --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_visibility.js @@ -0,0 +1,342 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +async function testTreeOnHide(browser, accDoc, containerID, id, before, after) { + let acc = findAccessibleChildByID(accDoc, containerID); + testAccessibleTree(acc, before); + + let onReorder = waitForEvent(EVENT_REORDER, containerID); + await invokeSetStyle(browser, id, "visibility", "hidden"); + await onReorder; + + testAccessibleTree(acc, after); +} + +async 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"); + await invokeContentTask(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"; + }); + await onReorder; + + tree = { + SECTION: [ + // container + { + SECTION: [ + // child + { TEXT_LEAF: [] }, + ], + }, + { + SECTION: [ + // child2 + { TEXT_LEAF: [] }, + ], + }, + ], + }; + testAccessibleTree(accessible, tree); +} + +async function test4(browser, accessible) { + let tree = { + SECTION: [{ TABLE: [{ ROW: [{ CELL: [] }] }] }], + }; + testAccessibleTree(accessible, tree); + + let onReorder = waitForEvent(EVENT_REORDER, "t4_parent"); + await invokeContentTask(browser, [], () => { + let doc = content.document; + doc.getElementById("t4_container").style.color = "red"; + doc.getElementById("t4_child").style.visibility = "visible"; + }); + await onReorder; + + tree = { + SECTION: [ + { + TABLE: [ + { + ROW: [ + { + CELL: [ + { + SECTION: [ + { + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + testAccessibleTree(accessible, tree); +} + +addAccessibleTask( + "e10s/doc_treeupdate_visibility.html", + async function(browser, accDoc) { + let t3Container = findAccessibleChildByID(accDoc, "t3_container"); + let t4Container = findAccessibleChildByID(accDoc, "t4_container"); + + await testTreeOnHide( + browser, + accDoc, + "t1_container", + "t1_parent", + { + SECTION: [ + { + SECTION: [ + { + SECTION: [{ TEXT_LEAF: [] }], + }, + ], + }, + ], + }, + { + SECTION: [ + { + SECTION: [{ TEXT_LEAF: [] }], + }, + ], + } + ); + + await 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: [], + }, + ], + }, + ], + } + ); + + await test3(browser, t3Container); + await test4(browser, t4Container); + + await testTreeOnHide( + browser, + accDoc, + "t5_container", + "t5_subcontainer", + { + SECTION: [ + { + // container + SECTION: [ + { + // subcontainer + TABLE: [ + { + ROW: [ + { + CELL: [ + { + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + SECTION: [ + { + // container + SECTION: [ + { + // child + TEXT_LEAF: [], + }, + ], + }, + ], + } + ); + + await 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: [], + }, + ], + }, + ], + } + ); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..7f7e4ba97d --- /dev/null +++ b/accessible/tests/browser/e10s/browser_treeupdate_whitespace.js @@ -0,0 +1,68 @@ +/* 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"; + +/* import-globals-from ../../mochitest/role.js */ +loadScripts({ name: "role.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "e10s/doc_treeupdate_whitespace.html", + async 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 + await invokeContentTask(browser, [], () => { + let doc = content.document; + doc.getElementById("container1").removeChild(doc.getElementById("img1")); + }); + await 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 + await invokeContentTask(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); + }); + await onReorder; + + tree = { + SECTION: [ + { LINK: [{ GRAPHIC: [] }] }, + { TEXT_LEAF: [] }, + { LINK: [{ GRAPHIC: [] }] }, + ], + }; + testAccessibleTree(container2Parent, tree); + }, + { iframe: true, remoteIframe: true } +); 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 0000000000..9d08854b9a --- /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 0000000000..38b5c333a1 --- /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 0000000000..4dd230fc28 --- /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 0000000000..9c59fb9d11 --- /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 0000000000..00213b2b70 --- /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 id="t2_parent"> + <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 0000000000..f17dbbd60e --- /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/destruction</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/head.js b/accessible/tests/browser/e10s/head.js new file mode 100644 index 0000000000..672aa46171 --- /dev/null +++ b/accessible/tests/browser/e10s/head.js @@ -0,0 +1,19 @@ +/* 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"; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js", + this +); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as promisified-events.js. +loadScripts( + { name: "common.js", dir: MOCHITESTS_DIR }, + { name: "promisified-events.js", dir: MOCHITESTS_DIR } +); |