shared-head.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. 'use strict';
  5. /* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
  6. invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
  7. CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
  8. const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
  9. /**
  10. * Current browser test directory path used to load subscripts.
  11. */
  12. const CURRENT_DIR =
  13. 'chrome://mochitests/content/browser/accessible/tests/browser/';
  14. /**
  15. * A11y mochitest directory where we find common files used in both browser and
  16. * plain tests.
  17. */
  18. const MOCHITESTS_DIR =
  19. 'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
  20. /**
  21. * A base URL for test files used in content.
  22. */
  23. const CURRENT_CONTENT_DIR =
  24. 'http://example.com/browser/accessible/tests/browser/';
  25. /**
  26. * Used to dump debug information.
  27. */
  28. let Logger = {
  29. /**
  30. * Set up this variable to dump log messages into console.
  31. */
  32. dumpToConsole: false,
  33. /**
  34. * Set up this variable to dump log messages into error console.
  35. */
  36. dumpToAppConsole: false,
  37. /**
  38. * Return true if dump is enabled.
  39. */
  40. get enabled() {
  41. return this.dumpToConsole || this.dumpToAppConsole;
  42. },
  43. /**
  44. * Dump information into console if applicable.
  45. */
  46. log(msg) {
  47. if (this.enabled) {
  48. this.logToConsole(msg);
  49. this.logToAppConsole(msg);
  50. }
  51. },
  52. /**
  53. * Log message to console.
  54. */
  55. logToConsole(msg) {
  56. if (this.dumpToConsole) {
  57. dump(`\n${msg}\n`);
  58. }
  59. },
  60. /**
  61. * Log message to error console.
  62. */
  63. logToAppConsole(msg) {
  64. if (this.dumpToAppConsole) {
  65. Services.console.logStringMessage(`${msg}`);
  66. }
  67. }
  68. };
  69. /**
  70. * Check if an accessible object has a defunct test.
  71. * @param {nsIAccessible} accessible object to test defunct state for
  72. * @return {Boolean} flag indicating defunct state
  73. */
  74. function isDefunct(accessible) {
  75. let defunct = false;
  76. try {
  77. let extState = {};
  78. accessible.getState({}, extState);
  79. defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
  80. } catch (x) {
  81. defunct = true;
  82. } finally {
  83. if (defunct) {
  84. Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
  85. }
  86. }
  87. return defunct;
  88. }
  89. /**
  90. * Asynchronously set or remove content element's attribute (in content process
  91. * if e10s is enabled).
  92. * @param {Object} browser current "tabbrowser" element
  93. * @param {String} id content element id
  94. * @param {String} attr attribute name
  95. * @param {String?} value optional attribute value, if not present, remove
  96. * attribute
  97. * @return {Promise} promise indicating that attribute is set/removed
  98. */
  99. function invokeSetAttribute(browser, id, attr, value) {
  100. if (value) {
  101. Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
  102. } else {
  103. Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
  104. }
  105. return ContentTask.spawn(browser, [id, attr, value],
  106. ([contentId, contentAttr, contentValue]) => {
  107. let elm = content.document.getElementById(contentId);
  108. if (contentValue) {
  109. elm.setAttribute(contentAttr, contentValue);
  110. } else {
  111. elm.removeAttribute(contentAttr);
  112. }
  113. });
  114. }
  115. /**
  116. * Asynchronously set or remove content element's style (in content process if
  117. * e10s is enabled).
  118. * @param {Object} browser current "tabbrowser" element
  119. * @param {String} id content element id
  120. * @param {String} aStyle style property name
  121. * @param {String?} aValue optional style property value, if not present,
  122. * remove style
  123. * @return {Promise} promise indicating that style is set/removed
  124. */
  125. function invokeSetStyle(browser, id, style, value) {
  126. if (value) {
  127. Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
  128. } else {
  129. Logger.log(`Removing ${style} style from node with id: ${id}`);
  130. }
  131. return ContentTask.spawn(browser, [id, style, value],
  132. ([contentId, contentStyle, contentValue]) => {
  133. let elm = content.document.getElementById(contentId);
  134. if (contentValue) {
  135. elm.style[contentStyle] = contentValue;
  136. } else {
  137. delete elm.style[contentStyle];
  138. }
  139. });
  140. }
  141. /**
  142. * Asynchronously set focus on a content element (in content process if e10s is
  143. * enabled).
  144. * @param {Object} browser current "tabbrowser" element
  145. * @param {String} id content element id
  146. * @return {Promise} promise indicating that focus is set
  147. */
  148. function invokeFocus(browser, id) {
  149. Logger.log(`Setting focus on a node with id: ${id}`);
  150. return ContentTask.spawn(browser, id, contentId => {
  151. let elm = content.document.getElementById(contentId);
  152. if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
  153. elm instanceof Ci.nsIDOMXULTextBoxElement) {
  154. elm.selectionStart = elm.selectionEnd = elm.value.length;
  155. }
  156. elm.focus();
  157. });
  158. }
  159. /**
  160. * Traverses the accessible tree starting from a given accessible as a root and
  161. * looks for an accessible that matches based on its DOMNode id.
  162. * @param {nsIAccessible} accessible root accessible
  163. * @param {String} id id to look up accessible for
  164. * @return {nsIAccessible?} found accessible if any
  165. */
  166. function findAccessibleChildByID(accessible, id) {
  167. if (getAccessibleDOMNodeID(accessible) === id) {
  168. return accessible;
  169. }
  170. for (let i = 0; i < accessible.children.length; ++i) {
  171. let found = findAccessibleChildByID(accessible.getChildAt(i), id);
  172. if (found) {
  173. return found;
  174. }
  175. }
  176. }
  177. /**
  178. * Load a list of scripts into the test
  179. * @param {Array} scripts a list of scripts to load
  180. */
  181. function loadScripts(...scripts) {
  182. for (let script of scripts) {
  183. let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
  184. `${script.dir}${script.name}`;
  185. Services.scriptloader.loadSubScript(path, this);
  186. }
  187. }
  188. /**
  189. * Load a list of frame scripts into test's content.
  190. * @param {Object} browser browser element that content belongs to
  191. * @param {Array} scripts a list of scripts to load into content
  192. */
  193. function loadFrameScripts(browser, ...scripts) {
  194. let mm = browser.messageManager;
  195. for (let script of scripts) {
  196. let frameScript;
  197. if (typeof script === 'string') {
  198. if (script.includes('.js')) {
  199. // If script string includes a .js extention, assume it is a script
  200. // path.
  201. frameScript = `${CURRENT_DIR}${script}`;
  202. } else {
  203. // Otherwise it is a serealized script.
  204. frameScript = `data:,${script}`;
  205. }
  206. } else {
  207. // Script is a object that has { dir, name } format.
  208. frameScript = `${script.dir}${script.name}`;
  209. }
  210. mm.loadFrameScript(frameScript, false, true);
  211. }
  212. }