diff options
Diffstat (limited to 'accessible/tests/browser/shared-head.js')
-rw-r--r-- | accessible/tests/browser/shared-head.js | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/accessible/tests/browser/shared-head.js b/accessible/tests/browser/shared-head.js new file mode 100644 index 000000000..83a9fa612 --- /dev/null +++ b/accessible/tests/browser/shared-head.js @@ -0,0 +1,229 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +/* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus, + invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID, + CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */ + +const { interfaces: Ci, utils: Cu, classes: Cc } = Components; + +/** + * Current browser test directory path used to load subscripts. + */ +const CURRENT_DIR = + 'chrome://mochitests/content/browser/accessible/tests/browser/'; +/** + * A11y mochitest directory where we find common files used in both browser and + * plain tests. + */ +const MOCHITESTS_DIR = + 'chrome://mochitests/content/a11y/accessible/tests/mochitest/'; +/** + * A base URL for test files used in content. + */ +const CURRENT_CONTENT_DIR = + 'http://example.com/browser/accessible/tests/browser/'; + +/** + * Used to dump debug information. + */ +let Logger = { + /** + * Set up this variable to dump log messages into console. + */ + dumpToConsole: false, + + /** + * Set up this variable to dump log messages into error console. + */ + dumpToAppConsole: false, + + /** + * Return true if dump is enabled. + */ + get enabled() { + return this.dumpToConsole || this.dumpToAppConsole; + }, + + /** + * Dump information into console if applicable. + */ + log(msg) { + if (this.enabled) { + this.logToConsole(msg); + this.logToAppConsole(msg); + } + }, + + /** + * Log message to console. + */ + logToConsole(msg) { + if (this.dumpToConsole) { + dump(`\n${msg}\n`); + } + }, + + /** + * Log message to error console. + */ + logToAppConsole(msg) { + if (this.dumpToAppConsole) { + Services.console.logStringMessage(`${msg}`); + } + } +}; + +/** + * Check if an accessible object has a defunct test. + * @param {nsIAccessible} accessible object to test defunct state for + * @return {Boolean} flag indicating defunct state + */ +function isDefunct(accessible) { + let defunct = false; + try { + let extState = {}; + accessible.getState({}, extState); + defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT; + } catch (x) { + defunct = true; + } finally { + if (defunct) { + Logger.log(`Defunct accessible: ${prettyName(accessible)}`); + } + } + return defunct; +} + +/** + * Asynchronously set or remove content element's attribute (in content process + * if e10s is enabled). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @param {String} attr attribute name + * @param {String?} value optional attribute value, if not present, remove + * attribute + * @return {Promise} promise indicating that attribute is set/removed + */ +function invokeSetAttribute(browser, id, attr, value) { + if (value) { + Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`); + } else { + Logger.log(`Removing ${attr} attribute from node with id: ${id}`); + } + return ContentTask.spawn(browser, [id, attr, value], + ([contentId, contentAttr, contentValue]) => { + let elm = content.document.getElementById(contentId); + if (contentValue) { + elm.setAttribute(contentAttr, contentValue); + } else { + elm.removeAttribute(contentAttr); + } + }); +} + +/** + * Asynchronously set or remove content element's style (in content process if + * e10s is enabled). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @param {String} aStyle style property name + * @param {String?} aValue optional style property value, if not present, + * remove style + * @return {Promise} promise indicating that style is set/removed + */ +function invokeSetStyle(browser, id, style, value) { + if (value) { + Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`); + } else { + Logger.log(`Removing ${style} style from node with id: ${id}`); + } + return ContentTask.spawn(browser, [id, style, value], + ([contentId, contentStyle, contentValue]) => { + let elm = content.document.getElementById(contentId); + if (contentValue) { + elm.style[contentStyle] = contentValue; + } else { + delete elm.style[contentStyle]; + } + }); +} + +/** + * Asynchronously set focus on a content element (in content process if e10s is + * enabled). + * @param {Object} browser current "tabbrowser" element + * @param {String} id content element id + * @return {Promise} promise indicating that focus is set + */ +function invokeFocus(browser, id) { + Logger.log(`Setting focus on a node with id: ${id}`); + return ContentTask.spawn(browser, id, contentId => { + let elm = content.document.getElementById(contentId); + if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor || + elm instanceof Ci.nsIDOMXULTextBoxElement) { + elm.selectionStart = elm.selectionEnd = elm.value.length; + } + elm.focus(); + }); +} + +/** + * Traverses the accessible tree starting from a given accessible as a root and + * looks for an accessible that matches based on its DOMNode id. + * @param {nsIAccessible} accessible root accessible + * @param {String} id id to look up accessible for + * @return {nsIAccessible?} found accessible if any + */ +function findAccessibleChildByID(accessible, id) { + if (getAccessibleDOMNodeID(accessible) === id) { + return accessible; + } + for (let i = 0; i < accessible.children.length; ++i) { + let found = findAccessibleChildByID(accessible.getChildAt(i), id); + if (found) { + return found; + } + } +} + +/** + * Load a list of scripts into the test + * @param {Array} scripts a list of scripts to load + */ +function loadScripts(...scripts) { + for (let script of scripts) { + let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` : + `${script.dir}${script.name}`; + Services.scriptloader.loadSubScript(path, this); + } +} + +/** + * Load a list of frame scripts into test's content. + * @param {Object} browser browser element that content belongs to + * @param {Array} scripts a list of scripts to load into content + */ +function loadFrameScripts(browser, ...scripts) { + let mm = browser.messageManager; + for (let script of scripts) { + let frameScript; + if (typeof script === 'string') { + if (script.includes('.js')) { + // If script string includes a .js extention, assume it is a script + // path. + frameScript = `${CURRENT_DIR}${script}`; + } else { + // Otherwise it is a serealized script. + frameScript = `data:,${script}`; + } + } else { + // Script is a object that has { dir, name } format. + frameScript = `${script.dir}${script.name}`; + } + mm.loadFrameScript(frameScript, false, true); + } +} |