2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2008-08-28 18:48:41 +04:00
|
|
|
// Interfaces
|
|
|
|
|
2016-08-08 18:47:48 +03:00
|
|
|
const nsIAccessibilityService = Components.interfaces.nsIAccessibilityService;
|
2008-08-28 18:48:41 +04:00
|
|
|
|
|
|
|
const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent;
|
2008-12-03 12:04:02 +03:00
|
|
|
const nsIAccessibleStateChangeEvent =
|
|
|
|
Components.interfaces.nsIAccessibleStateChangeEvent;
|
2008-12-16 13:14:20 +03:00
|
|
|
const nsIAccessibleCaretMoveEvent =
|
|
|
|
Components.interfaces.nsIAccessibleCaretMoveEvent;
|
2010-05-17 20:17:50 +04:00
|
|
|
const nsIAccessibleTextChangeEvent =
|
|
|
|
Components.interfaces.nsIAccessibleTextChangeEvent;
|
2012-03-10 05:52:13 +04:00
|
|
|
const nsIAccessibleVirtualCursorChangeEvent =
|
|
|
|
Components.interfaces.nsIAccessibleVirtualCursorChangeEvent;
|
2014-08-28 16:42:06 +04:00
|
|
|
const nsIAccessibleObjectAttributeChangedEvent =
|
|
|
|
Components.interfaces.nsIAccessibleObjectAttributeChangedEvent;
|
2008-12-03 12:04:02 +03:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates;
|
|
|
|
const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole;
|
2011-09-28 05:46:11 +04:00
|
|
|
const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType;
|
|
|
|
const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType;
|
2008-08-28 18:48:41 +04:00
|
|
|
|
|
|
|
const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation;
|
2014-05-23 04:10:19 +04:00
|
|
|
const nsIAccessibleTextRange = Components.interfaces.nsIAccessibleTextRange;
|
2008-08-28 18:48:41 +04:00
|
|
|
|
|
|
|
const nsIAccessible = Components.interfaces.nsIAccessible;
|
|
|
|
|
|
|
|
const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument;
|
2010-03-18 21:49:39 +03:00
|
|
|
const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication;
|
2008-08-28 18:48:41 +04:00
|
|
|
|
|
|
|
const nsIAccessibleText = Components.interfaces.nsIAccessibleText;
|
|
|
|
const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText;
|
|
|
|
|
|
|
|
const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink;
|
|
|
|
const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText;
|
|
|
|
|
|
|
|
const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage;
|
2012-02-02 10:14:51 +04:00
|
|
|
const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot;
|
2008-08-28 18:48:41 +04:00
|
|
|
const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable;
|
|
|
|
const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable;
|
2009-09-11 05:07:56 +04:00
|
|
|
const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell;
|
2012-02-02 10:14:51 +04:00
|
|
|
const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule;
|
2008-08-28 18:48:41 +04:00
|
|
|
const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue;
|
|
|
|
|
|
|
|
const nsIObserverService = Components.interfaces.nsIObserverService;
|
|
|
|
|
2009-02-05 08:43:46 +03:00
|
|
|
const nsIDOMDocument = Components.interfaces.nsIDOMDocument;
|
2009-02-27 13:45:21 +03:00
|
|
|
const nsIDOMEvent = Components.interfaces.nsIDOMEvent;
|
|
|
|
const nsIDOMHTMLDocument = Components.interfaces.nsIDOMHTMLDocument;
|
2008-08-28 18:48:41 +04:00
|
|
|
const nsIDOMNode = Components.interfaces.nsIDOMNode;
|
2011-10-30 00:03:55 +04:00
|
|
|
const nsIDOMHTMLElement = Components.interfaces.nsIDOMHTMLElement;
|
2009-02-05 08:43:46 +03:00
|
|
|
const nsIDOMWindow = Components.interfaces.nsIDOMWindow;
|
2009-05-11 14:57:28 +04:00
|
|
|
const nsIDOMXULElement = Components.interfaces.nsIDOMXULElement;
|
2009-02-05 08:43:46 +03:00
|
|
|
|
2008-10-28 14:54:57 +03:00
|
|
|
const nsIPropertyElement = Components.interfaces.nsIPropertyElement;
|
2008-08-28 18:48:41 +04:00
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2009-01-25 10:01:24 +03:00
|
|
|
// OS detect
|
2012-02-20 08:25:17 +04:00
|
|
|
|
|
|
|
const MAC = (navigator.platform.indexOf("Mac") != -1);
|
|
|
|
const LINUX = (navigator.platform.indexOf("Linux") != -1);
|
|
|
|
const SOLARIS = (navigator.platform.indexOf("SunOS") != -1);
|
|
|
|
const WIN = (navigator.platform.indexOf("Win") != -1);
|
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2012-02-20 08:25:17 +04:00
|
|
|
// Application detect
|
|
|
|
// Firefox is assumed by default.
|
|
|
|
|
|
|
|
const SEAMONKEY = navigator.userAgent.match(/ SeaMonkey\//);
|
2009-01-25 10:01:24 +03:00
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2008-09-01 05:52:40 +04:00
|
|
|
// Accessible general
|
2008-08-28 18:48:41 +04:00
|
|
|
|
2010-10-20 19:04:18 +04:00
|
|
|
const STATE_BUSY = nsIAccessibleStates.STATE_BUSY;
|
|
|
|
|
2011-09-28 05:46:11 +04:00
|
|
|
const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE;
|
|
|
|
|
2012-10-19 19:10:28 +04:00
|
|
|
const COORDTYPE_SCREEN_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE;
|
|
|
|
const COORDTYPE_WINDOW_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_WINDOW_RELATIVE;
|
|
|
|
const COORDTYPE_PARENT_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_PARENT_RELATIVE;
|
|
|
|
|
2010-07-02 05:49:42 +04:00
|
|
|
const kEmbedChar = String.fromCharCode(0xfffc);
|
|
|
|
|
2013-12-10 16:40:54 +04:00
|
|
|
const kDiscBulletChar = String.fromCharCode(0x2022);
|
|
|
|
const kDiscBulletText = kDiscBulletChar + " ";
|
2011-03-28 17:59:54 +04:00
|
|
|
const kCircleBulletText = String.fromCharCode(0x25e6) + " ";
|
2014-06-12 05:13:00 +04:00
|
|
|
const kSquareBulletText = String.fromCharCode(0x25fe) + " ";
|
2011-03-28 17:59:54 +04:00
|
|
|
|
2012-10-23 09:04:43 +04:00
|
|
|
const MAX_TRIM_LENGTH = 100;
|
|
|
|
|
2016-05-02 18:09:28 +03:00
|
|
|
/**
|
|
|
|
* Services to determine if e10s is enabled.
|
|
|
|
*/
|
2017-08-01 19:13:27 +03:00
|
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
2016-05-02 18:09:28 +03:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
/**
|
2016-08-08 18:47:48 +03:00
|
|
|
* nsIAccessibilityService service.
|
2008-08-28 18:48:41 +04:00
|
|
|
*/
|
2016-08-08 18:47:48 +03:00
|
|
|
var gAccService = Components.classes["@mozilla.org/accessibilityService;1"].
|
|
|
|
getService(nsIAccessibilityService);
|
2008-08-28 18:48:41 +04:00
|
|
|
|
2012-06-02 05:58:44 +04:00
|
|
|
/**
|
|
|
|
* Enable/disable logging.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function enableLogging(aModules) {
|
2016-08-08 18:47:48 +03:00
|
|
|
gAccService.setLogging(aModules);
|
2012-06-02 05:58:44 +04:00
|
|
|
}
|
2017-08-01 21:08:02 +03:00
|
|
|
function disableLogging() {
|
2016-08-08 18:47:48 +03:00
|
|
|
gAccService.setLogging("");
|
2012-06-02 05:58:44 +04:00
|
|
|
}
|
2017-08-01 21:08:02 +03:00
|
|
|
function isLogged(aModule) {
|
2016-08-08 18:47:48 +03:00
|
|
|
return gAccService.isLogged(aModule);
|
2012-11-04 06:19:56 +04:00
|
|
|
}
|
2012-06-02 05:58:44 +04:00
|
|
|
|
2016-02-19 21:11:33 +03:00
|
|
|
/**
|
|
|
|
* Dumps the accessible tree into console.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function dumpTree(aId, aMsg) {
|
|
|
|
function dumpTreeIntl(acc, indent) {
|
2016-02-19 21:11:33 +03:00
|
|
|
dump(indent + prettyName(acc) + "\n");
|
|
|
|
|
|
|
|
var children = acc.children;
|
|
|
|
for (var i = 0; i < children.length; i++) {
|
|
|
|
var child = children.queryElementAt(i, nsIAccessible);
|
|
|
|
dumpTreeIntl(child, indent + " ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 21:08:02 +03:00
|
|
|
function dumpDOMTreeIntl(node, indent) {
|
2016-02-25 15:09:59 +03:00
|
|
|
dump(indent + prettyName(node) + "\n");
|
|
|
|
|
|
|
|
var children = node.childNodes;
|
|
|
|
for (var i = 0; i < children.length; i++) {
|
|
|
|
var child = children.item(i);
|
|
|
|
dumpDOMTreeIntl(child, indent + " ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-19 21:11:33 +03:00
|
|
|
dump(aMsg + "\n");
|
|
|
|
var root = getAccessible(aId);
|
|
|
|
dumpTreeIntl(root, " ");
|
2016-02-25 15:09:59 +03:00
|
|
|
|
|
|
|
dump("DOM tree:\n");
|
|
|
|
dumpDOMTreeIntl(getNode(aId), " ");
|
2016-02-19 21:11:33 +03:00
|
|
|
}
|
|
|
|
|
2008-12-17 10:10:41 +03:00
|
|
|
/**
|
2009-09-23 18:21:47 +04:00
|
|
|
* Invokes the given function when document is loaded and focused. Preferable
|
|
|
|
* to mochitests 'addLoadEvent' function -- additionally ensures state of the
|
|
|
|
* document accessible is not busy.
|
2008-12-17 10:10:41 +03:00
|
|
|
*
|
|
|
|
* @param aFunc the function to invoke
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function addA11yLoadEvent(aFunc, aWindow) {
|
|
|
|
function waitForDocLoad() {
|
2008-12-17 10:10:41 +03:00
|
|
|
window.setTimeout(
|
2017-08-01 21:08:02 +03:00
|
|
|
function() {
|
2011-09-28 05:46:11 +04:00
|
|
|
var targetDocument = aWindow ? aWindow.document : document;
|
|
|
|
var accDoc = getAccessible(targetDocument);
|
2008-12-17 10:10:41 +03:00
|
|
|
var state = {};
|
|
|
|
accDoc.getState(state, {});
|
2009-06-25 06:12:38 +04:00
|
|
|
if (state.value & STATE_BUSY)
|
2008-12-17 10:10:41 +03:00
|
|
|
return waitForDocLoad();
|
|
|
|
|
2011-05-27 07:26:48 +04:00
|
|
|
window.setTimeout(aFunc, 0);
|
2008-12-17 10:10:41 +03:00
|
|
|
},
|
2009-12-18 18:47:15 +03:00
|
|
|
0
|
2008-12-17 10:10:41 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-08-24 19:39:39 +03:00
|
|
|
if (aWindow &&
|
|
|
|
aWindow.document.activeElement &&
|
|
|
|
aWindow.document.activeElement.localName == "browser") {
|
|
|
|
waitForDocLoad();
|
|
|
|
} else {
|
|
|
|
SimpleTest.waitForFocus(waitForDocLoad, aWindow);
|
|
|
|
}
|
2008-12-17 10:10:41 +03:00
|
|
|
}
|
|
|
|
|
2012-03-26 16:19:07 +04:00
|
|
|
/**
|
|
|
|
* Analogy of SimpleTest.is function used to compare objects.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function isObject(aObj, aExpectedObj, aMsg) {
|
2012-03-26 16:19:07 +04:00
|
|
|
if (aObj == aExpectedObj) {
|
|
|
|
ok(true, aMsg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok(false,
|
|
|
|
aMsg + " - got '" + prettyName(aObj) +
|
|
|
|
"', expected '" + prettyName(aExpectedObj) + "'");
|
|
|
|
}
|
|
|
|
|
2017-09-12 02:05:38 +03:00
|
|
|
/**
|
|
|
|
* is() function checking the expected value is within the range.
|
|
|
|
*/
|
|
|
|
function isWithin(aExpected, aGot, aWithin, aMsg) {
|
|
|
|
if (Math.abs(aGot - aExpected) <= aWithin) {
|
|
|
|
ok(true, `${aMsg} - Got ${aGot}`);
|
|
|
|
} else {
|
|
|
|
ok(false,
|
|
|
|
`${aMsg} - Got ${aGot}, expected ${aExpected} with error of ${aWithin}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2009-12-18 18:47:15 +03:00
|
|
|
// Helpers for getting DOM node/accessible
|
2008-12-17 10:10:41 +03:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
/**
|
2009-05-11 05:32:09 +04:00
|
|
|
* Return the DOM node by identifier (may be accessible, DOM node or ID).
|
2008-12-16 13:14:20 +03:00
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getNode(aAccOrNodeOrID, aDocument) {
|
2009-05-11 05:32:09 +04:00
|
|
|
if (!aAccOrNodeOrID)
|
2008-12-16 13:14:20 +03:00
|
|
|
return null;
|
|
|
|
|
2009-05-11 05:32:09 +04:00
|
|
|
if (aAccOrNodeOrID instanceof nsIDOMNode)
|
|
|
|
return aAccOrNodeOrID;
|
2008-12-16 13:14:20 +03:00
|
|
|
|
2012-02-07 17:18:33 +04:00
|
|
|
if (aAccOrNodeOrID instanceof nsIAccessible)
|
2009-05-11 05:32:09 +04:00
|
|
|
return aAccOrNodeOrID.DOMNode;
|
2008-12-16 13:14:20 +03:00
|
|
|
|
2014-10-22 04:49:28 +04:00
|
|
|
var node = (aDocument || document).getElementById(aAccOrNodeOrID);
|
2009-05-11 05:32:09 +04:00
|
|
|
if (!node) {
|
|
|
|
ok(false, "Can't get DOM element for " + aAccOrNodeOrID);
|
|
|
|
return null;
|
2008-12-16 13:14:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2009-02-27 13:45:21 +03:00
|
|
|
/**
|
|
|
|
* Constants indicates getAccessible doesn't fail if there is no accessible.
|
|
|
|
*/
|
|
|
|
const DONOTFAIL_IF_NO_ACC = 1;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constants indicates getAccessible won't fail if accessible doesn't implement
|
|
|
|
* the requested interfaces.
|
|
|
|
*/
|
|
|
|
const DONOTFAIL_IF_NO_INTERFACE = 2;
|
|
|
|
|
2008-12-16 13:14:20 +03:00
|
|
|
/**
|
2009-01-20 18:08:38 +03:00
|
|
|
* Return accessible for the given identifier (may be ID attribute or DOM
|
2009-08-18 19:06:54 +04:00
|
|
|
* element or accessible object) or null.
|
2008-08-28 18:48:41 +04:00
|
|
|
*
|
2009-01-20 18:08:38 +03:00
|
|
|
* @param aAccOrElmOrID [in] identifier to get an accessible implementing
|
|
|
|
* the given interfaces
|
|
|
|
* @param aInterfaces [in, optional] the interface or an array interfaces
|
|
|
|
* to query it/them from obtained accessible
|
|
|
|
* @param aElmObj [out, optional] object to store DOM element which
|
|
|
|
* accessible is obtained for
|
2009-02-27 13:45:21 +03:00
|
|
|
* @param aDoNotFailIf [in, optional] no error for special cases (see
|
|
|
|
* constants above)
|
2008-08-28 18:48:41 +04:00
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIf) {
|
2009-02-19 10:06:14 +03:00
|
|
|
if (!aAccOrElmOrID)
|
2009-08-18 19:06:54 +04:00
|
|
|
return null;
|
2009-02-19 10:06:14 +03:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
var elm = null;
|
|
|
|
|
2008-09-01 05:52:40 +04:00
|
|
|
if (aAccOrElmOrID instanceof nsIAccessible) {
|
2017-07-04 16:27:02 +03:00
|
|
|
try { elm = aAccOrElmOrID.DOMNode; } catch (e) { }
|
2008-09-01 05:52:40 +04:00
|
|
|
|
|
|
|
} else if (aAccOrElmOrID instanceof nsIDOMNode) {
|
|
|
|
elm = aAccOrElmOrID;
|
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
} else {
|
2010-06-08 23:17:54 +04:00
|
|
|
elm = document.getElementById(aAccOrElmOrID);
|
2008-08-28 18:48:41 +04:00
|
|
|
if (!elm) {
|
2008-10-14 12:27:02 +04:00
|
|
|
ok(false, "Can't get DOM element for " + aAccOrElmOrID);
|
2008-08-28 18:48:41 +04:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aElmObj && (typeof aElmObj == "object"))
|
|
|
|
aElmObj.value = elm;
|
|
|
|
|
2008-09-01 05:52:40 +04:00
|
|
|
var acc = (aAccOrElmOrID instanceof nsIAccessible) ? aAccOrElmOrID : null;
|
2008-08-28 18:48:41 +04:00
|
|
|
if (!acc) {
|
2008-09-01 05:52:40 +04:00
|
|
|
try {
|
2016-08-08 18:47:48 +03:00
|
|
|
acc = gAccService.getAccessibleFor(elm);
|
2008-09-01 05:52:40 +04:00
|
|
|
} catch (e) {
|
|
|
|
}
|
2008-09-17 17:09:34 +04:00
|
|
|
|
2008-09-01 05:52:40 +04:00
|
|
|
if (!acc) {
|
2009-02-27 13:45:21 +03:00
|
|
|
if (!(aDoNotFailIf & DONOTFAIL_IF_NO_ACC))
|
2014-09-11 23:58:28 +04:00
|
|
|
ok(false, "Can't get accessible for " + prettyName(aAccOrElmOrID));
|
2009-01-20 18:08:38 +03:00
|
|
|
|
2008-09-01 05:52:40 +04:00
|
|
|
return null;
|
|
|
|
}
|
2008-08-28 18:48:41 +04:00
|
|
|
}
|
2008-09-17 17:09:34 +04:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
if (!aInterfaces)
|
|
|
|
return acc;
|
2008-09-17 17:09:34 +04:00
|
|
|
|
2013-12-18 21:25:07 +04:00
|
|
|
if (!(aInterfaces instanceof Array))
|
|
|
|
aInterfaces = [ aInterfaces ];
|
|
|
|
|
|
|
|
for (var index = 0; index < aInterfaces.length; index++) {
|
2016-06-06 17:29:24 +03:00
|
|
|
if (acc instanceof aInterfaces[index]) {
|
|
|
|
continue;
|
|
|
|
}
|
2013-12-18 21:25:07 +04:00
|
|
|
try {
|
|
|
|
acc.QueryInterface(aInterfaces[index]);
|
|
|
|
} catch (e) {
|
|
|
|
if (!(aDoNotFailIf & DONOTFAIL_IF_NO_INTERFACE))
|
|
|
|
ok(false, "Can't query " + aInterfaces[index] + " for " + aAccOrElmOrID);
|
|
|
|
|
|
|
|
return null;
|
2008-08-28 18:48:41 +04:00
|
|
|
}
|
|
|
|
}
|
2013-12-18 21:25:07 +04:00
|
|
|
|
2008-08-28 18:48:41 +04:00
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
|
2009-01-20 18:08:38 +03:00
|
|
|
/**
|
2009-03-05 15:55:54 +03:00
|
|
|
* Return true if the given identifier has an accessible, or exposes the wanted
|
|
|
|
* interfaces.
|
2009-01-20 18:08:38 +03:00
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function isAccessible(aAccOrElmOrID, aInterfaces) {
|
2017-06-12 21:53:54 +03:00
|
|
|
return !!getAccessible(aAccOrElmOrID, aInterfaces, null,
|
|
|
|
DONOTFAIL_IF_NO_ACC | DONOTFAIL_IF_NO_INTERFACE);
|
2009-01-20 18:08:38 +03:00
|
|
|
}
|
|
|
|
|
2010-10-21 08:16:10 +04:00
|
|
|
/**
|
|
|
|
* Return an accessible that contains the DOM node for the given identifier.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getContainerAccessible(aAccOrElmOrID) {
|
2010-10-21 08:16:10 +04:00
|
|
|
var node = getNode(aAccOrElmOrID);
|
|
|
|
if (!node)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
while ((node = node.parentNode) && !isAccessible(node));
|
|
|
|
return node ? getAccessible(node) : null;
|
|
|
|
}
|
|
|
|
|
2009-08-21 17:20:18 +04:00
|
|
|
/**
|
|
|
|
* Return root accessible for the given identifier.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getRootAccessible(aAccOrElmOrID) {
|
2012-02-07 17:18:33 +04:00
|
|
|
var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document);
|
2010-03-18 21:50:31 +03:00
|
|
|
return acc ? acc.rootDocument.QueryInterface(nsIAccessible) : null;
|
2009-08-21 17:20:18 +04:00
|
|
|
}
|
|
|
|
|
2010-10-20 19:04:18 +04:00
|
|
|
/**
|
|
|
|
* Return tab document accessible the given accessible is contained by.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getTabDocAccessible(aAccOrElmOrID) {
|
2012-02-07 17:18:33 +04:00
|
|
|
var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document);
|
2010-10-20 19:04:18 +04:00
|
|
|
|
|
|
|
var docAcc = acc.document.QueryInterface(nsIAccessible);
|
2012-02-07 17:18:33 +04:00
|
|
|
var containerDocAcc = docAcc.parent.document;
|
2010-10-20 19:04:18 +04:00
|
|
|
|
|
|
|
// Test is running is stand-alone mode.
|
|
|
|
if (acc.rootDocument == containerDocAcc)
|
|
|
|
return docAcc;
|
|
|
|
|
|
|
|
// In the case of running all tests together.
|
|
|
|
return containerDocAcc.QueryInterface(nsIAccessible);
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
/**
|
|
|
|
* Return application accessible.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getApplicationAccessible() {
|
2016-08-08 18:47:48 +03:00
|
|
|
return gAccService.getApplicationAccessible().
|
2010-03-18 21:49:39 +03:00
|
|
|
QueryInterface(nsIAccessibleApplication);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
|
|
|
|
2013-12-08 01:37:04 +04:00
|
|
|
/**
|
|
|
|
* A version of accessible tree testing, doesn't fail if tree is not complete.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function testElm(aID, aTreeObj) {
|
2013-12-08 01:37:04 +04:00
|
|
|
testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck);
|
|
|
|
}
|
|
|
|
|
2013-08-20 02:50:14 +04:00
|
|
|
/**
|
|
|
|
* Flags used for testAccessibleTree
|
|
|
|
*/
|
|
|
|
const kSkipTreeFullCheck = 1;
|
|
|
|
|
2009-04-20 11:09:21 +04:00
|
|
|
/**
|
|
|
|
* Compare expected and actual accessibles trees.
|
2009-09-11 05:07:56 +04:00
|
|
|
*
|
|
|
|
* @param aAccOrElmOrID [in] accessible identifier
|
|
|
|
* @param aAccTree [in] JS object, each field corresponds to property of
|
|
|
|
* accessible object. Additionally special properties
|
|
|
|
* are presented:
|
|
|
|
* children - an array of JS objects representing
|
|
|
|
* children of accessible
|
|
|
|
* states - an object having states and extraStates
|
|
|
|
* fields
|
2013-08-20 02:50:14 +04:00
|
|
|
* @param aFlags [in, optional] flags, see constants above
|
2009-04-20 11:09:21 +04:00
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags) {
|
2009-04-20 11:09:21 +04:00
|
|
|
var acc = getAccessible(aAccOrElmOrID);
|
|
|
|
if (!acc)
|
|
|
|
return;
|
|
|
|
|
2010-11-13 20:49:26 +03:00
|
|
|
var accTree = aAccTree;
|
|
|
|
|
|
|
|
// Support of simplified accessible tree object.
|
2016-02-25 04:34:56 +03:00
|
|
|
accTree = normalizeAccTreeObj(accTree);
|
2010-11-13 20:49:26 +03:00
|
|
|
|
|
|
|
// Test accessible properties.
|
|
|
|
for (var prop in accTree) {
|
2015-03-16 20:05:50 +03:00
|
|
|
var msg = "Wrong value of property '" + prop + "' for " +
|
|
|
|
prettyName(acc) + ".";
|
2009-05-22 20:09:51 +04:00
|
|
|
|
2013-08-20 02:50:14 +04:00
|
|
|
switch (prop) {
|
|
|
|
case "actions": {
|
2013-12-08 01:37:04 +04:00
|
|
|
testActionNames(acc, accTree.actions);
|
2013-08-20 02:50:14 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "attributes":
|
|
|
|
testAttrs(acc, accTree[prop], true);
|
|
|
|
break;
|
2009-05-22 20:09:51 +04:00
|
|
|
|
2013-08-20 02:50:14 +04:00
|
|
|
case "absentAttributes":
|
|
|
|
testAbsentAttrs(acc, accTree[prop]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "interfaces": {
|
|
|
|
var ifaces = (accTree[prop] instanceof Array) ?
|
|
|
|
accTree[prop] : [ accTree[prop] ];
|
|
|
|
for (var i = 0; i < ifaces.length; i++) {
|
|
|
|
ok((acc instanceof ifaces[i]),
|
|
|
|
"No " + ifaces[i] + " interface on " + prettyName(acc));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "relations": {
|
|
|
|
for (var rel in accTree[prop])
|
|
|
|
testRelation(acc, window[rel], accTree[prop][rel]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "role":
|
|
|
|
isRole(acc, accTree[prop], msg);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "states":
|
|
|
|
case "extraStates":
|
|
|
|
case "absentStates":
|
|
|
|
case "absentExtraStates": {
|
2017-07-25 21:15:41 +03:00
|
|
|
testStates(acc, accTree.states, accTree.extraStates,
|
|
|
|
accTree.absentStates, accTree.absentExtraStates);
|
2013-08-20 02:50:14 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case "tagName":
|
2012-11-30 11:33:01 +04:00
|
|
|
is(accTree[prop], acc.DOMNode.tagName, msg);
|
2013-08-20 02:50:14 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "textAttrs": {
|
|
|
|
var prevOffset = -1;
|
|
|
|
for (var offset in accTree[prop]) {
|
2017-08-01 19:15:51 +03:00
|
|
|
if (prevOffset != - 1) {
|
2013-08-20 02:50:14 +04:00
|
|
|
var attrs = accTree[prop][prevOffset];
|
2015-04-14 16:28:13 +03:00
|
|
|
testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, +offset, true);
|
2013-08-20 02:50:14 +04:00
|
|
|
}
|
2015-04-14 16:28:13 +03:00
|
|
|
prevOffset = +offset;
|
2013-08-20 02:50:14 +04:00
|
|
|
}
|
2012-11-30 11:33:01 +04:00
|
|
|
|
2013-08-20 02:50:14 +04:00
|
|
|
if (prevOffset != -1) {
|
|
|
|
var charCount = getAccessible(acc, [nsIAccessibleText]).characterCount;
|
|
|
|
var attrs = accTree[prop][prevOffset];
|
|
|
|
testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, charCount, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2014-05-23 04:10:19 +04:00
|
|
|
}
|
2013-08-20 02:50:14 +04:00
|
|
|
|
|
|
|
default:
|
|
|
|
if (prop.indexOf("todo_") == 0)
|
|
|
|
todo(false, msg);
|
|
|
|
else if (prop != "children")
|
|
|
|
is(acc[prop], accTree[prop], msg);
|
2009-05-22 20:09:51 +04:00
|
|
|
}
|
2009-04-20 11:09:21 +04:00
|
|
|
}
|
|
|
|
|
2010-11-13 20:49:26 +03:00
|
|
|
// Test children.
|
2017-07-25 21:15:41 +03:00
|
|
|
if ("children" in accTree && accTree.children instanceof Array) {
|
2009-04-20 11:09:21 +04:00
|
|
|
var children = acc.children;
|
2013-08-20 02:50:14 +04:00
|
|
|
var childCount = children.length;
|
|
|
|
|
2015-03-16 20:05:50 +03:00
|
|
|
if (accTree.children.length != childCount) {
|
|
|
|
for (var i = 0; i < Math.max(accTree.children.length, childCount); i++) {
|
2015-09-29 22:17:40 +03:00
|
|
|
var accChild = null, testChild = null;
|
2015-03-16 20:05:50 +03:00
|
|
|
try {
|
2015-09-29 22:17:40 +03:00
|
|
|
testChild = accTree.children[i];
|
2015-03-16 20:05:50 +03:00
|
|
|
accChild = children.queryElementAt(i, nsIAccessible);
|
2015-09-15 19:01:51 +03:00
|
|
|
|
|
|
|
if (!testChild) {
|
2015-03-16 20:05:50 +03:00
|
|
|
ok(false, prettyName(acc) + " has an extra child at index " + i +
|
|
|
|
" : " + prettyName(accChild));
|
2015-09-15 19:01:51 +03:00
|
|
|
continue;
|
2015-03-16 20:05:50 +03:00
|
|
|
}
|
2015-09-15 19:01:51 +03:00
|
|
|
|
2016-02-25 04:34:56 +03:00
|
|
|
testChild = normalizeAccTreeObj(testChild);
|
2015-09-15 19:01:51 +03:00
|
|
|
if (accChild.role !== testChild.role) {
|
2015-03-16 20:05:50 +03:00
|
|
|
ok(false, prettyName(accTree) + " and " + prettyName(acc) +
|
|
|
|
" have different children at index " + i + " : " +
|
2015-09-15 19:01:51 +03:00
|
|
|
prettyName(testChild) + ", " + prettyName(accChild));
|
2015-03-16 20:05:50 +03:00
|
|
|
}
|
|
|
|
info("Matching " + prettyName(accTree) + " and " + prettyName(acc) +
|
|
|
|
" child at index " + i + " : " + prettyName(accChild));
|
2015-09-29 22:17:40 +03:00
|
|
|
|
2015-03-16 20:05:50 +03:00
|
|
|
} catch (e) {
|
2015-09-29 22:17:40 +03:00
|
|
|
ok(false, prettyName(accTree) + " is expected to have a child at index " + i +
|
2016-02-25 04:34:56 +03:00
|
|
|
" : " + prettyName(testChild) + ", original tested: " +
|
|
|
|
prettyName(aAccOrElmOrID) + ", " + e);
|
2015-03-16 20:05:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-08-20 02:50:14 +04:00
|
|
|
if (aFlags & kSkipTreeFullCheck) {
|
|
|
|
for (var i = 0; i < childCount; i++) {
|
|
|
|
var child = children.queryElementAt(i, nsIAccessible);
|
|
|
|
testAccessibleTree(child, accTree.children[i], aFlags);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2009-07-28 06:28:06 +04:00
|
|
|
|
|
|
|
// nsIAccessible::firstChild
|
|
|
|
var expectedFirstChild = childCount > 0 ?
|
|
|
|
children.queryElementAt(0, nsIAccessible) : null;
|
|
|
|
var firstChild = null;
|
|
|
|
try { firstChild = acc.firstChild; } catch (e) {}
|
|
|
|
is(firstChild, expectedFirstChild,
|
|
|
|
"Wrong first child of " + prettyName(acc));
|
|
|
|
|
|
|
|
// nsIAccessible::lastChild
|
|
|
|
var expectedLastChild = childCount > 0 ?
|
|
|
|
children.queryElementAt(childCount - 1, nsIAccessible) : null;
|
|
|
|
var lastChild = null;
|
|
|
|
try { lastChild = acc.lastChild; } catch (e) {}
|
|
|
|
is(lastChild, expectedLastChild,
|
|
|
|
"Wrong last child of " + prettyName(acc));
|
|
|
|
|
2013-08-20 02:50:14 +04:00
|
|
|
for (var i = 0; i < childCount; i++) {
|
2009-04-20 11:09:21 +04:00
|
|
|
var child = children.queryElementAt(i, nsIAccessible);
|
2009-07-28 06:28:06 +04:00
|
|
|
|
|
|
|
// nsIAccessible::parent
|
|
|
|
var parent = null;
|
|
|
|
try { parent = child.parent; } catch (e) {}
|
|
|
|
is(parent, acc, "Wrong parent of " + prettyName(child));
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
// nsIAccessible::indexInParent
|
|
|
|
var indexInParent = -1;
|
2017-07-04 16:27:02 +03:00
|
|
|
try { indexInParent = child.indexInParent; } catch (e) {}
|
2009-12-10 22:12:19 +03:00
|
|
|
is(indexInParent, i,
|
|
|
|
"Wrong index in parent of " + prettyName(child));
|
|
|
|
|
2009-07-28 06:28:06 +04:00
|
|
|
// nsIAccessible::nextSibling
|
|
|
|
var expectedNextSibling = (i < childCount - 1) ?
|
|
|
|
children.queryElementAt(i + 1, nsIAccessible) : null;
|
|
|
|
var nextSibling = null;
|
|
|
|
try { nextSibling = child.nextSibling; } catch (e) {}
|
|
|
|
is(nextSibling, expectedNextSibling,
|
|
|
|
"Wrong next sibling of " + prettyName(child));
|
|
|
|
|
|
|
|
// nsIAccessible::previousSibling
|
|
|
|
var expectedPrevSibling = (i > 0) ?
|
|
|
|
children.queryElementAt(i - 1, nsIAccessible) : null;
|
|
|
|
var prevSibling = null;
|
|
|
|
try { prevSibling = child.previousSibling; } catch (e) {}
|
|
|
|
is(prevSibling, expectedPrevSibling,
|
|
|
|
"Wrong previous sibling of " + prettyName(child));
|
|
|
|
|
|
|
|
// Go down through subtree
|
2013-08-20 02:50:14 +04:00
|
|
|
testAccessibleTree(child, accTree.children[i], aFlags);
|
2009-04-20 11:09:21 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-18 06:43:58 +04:00
|
|
|
/**
|
|
|
|
* Return true if accessible for the given node is in cache.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function isAccessibleInCache(aNodeOrId) {
|
2010-06-18 06:43:58 +04:00
|
|
|
var node = getNode(aNodeOrId);
|
2017-06-12 21:53:54 +03:00
|
|
|
return !!gAccService.getAccessibleFromCache(node);
|
2010-06-18 06:43:58 +04:00
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
/**
|
|
|
|
* Test accessible tree for defunct accessible.
|
|
|
|
*
|
|
|
|
* @param aAcc [in] the defunct accessible
|
|
|
|
* @param aNodeOrId [in] the DOM node identifier for the defunct accessible
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function testDefunctAccessible(aAcc, aNodeOrId) {
|
2009-12-10 22:12:19 +03:00
|
|
|
if (aNodeOrId)
|
|
|
|
ok(!isAccessible(aNodeOrId),
|
|
|
|
"Accessible for " + aNodeOrId + " wasn't properly shut down!");
|
|
|
|
|
2015-03-16 20:05:50 +03:00
|
|
|
var msg = " doesn't fail for shut down accessible " +
|
|
|
|
prettyName(aNodeOrId) + "!";
|
2009-12-10 22:12:19 +03:00
|
|
|
|
|
|
|
// firstChild
|
|
|
|
var success = false;
|
|
|
|
try {
|
|
|
|
aAcc.firstChild;
|
|
|
|
} catch (e) {
|
2017-10-15 21:50:30 +03:00
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
|
|
|
ok(success, "firstChild" + msg);
|
|
|
|
|
|
|
|
// lastChild
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.lastChild;
|
|
|
|
} catch (e) {
|
2017-10-15 21:50:30 +03:00
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
|
|
|
ok(success, "lastChild" + msg);
|
|
|
|
|
|
|
|
// childCount
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.childCount;
|
|
|
|
} catch (e) {
|
2017-10-15 21:50:30 +03:00
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
|
|
|
ok(success, "childCount" + msg);
|
|
|
|
|
|
|
|
// children
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.children;
|
|
|
|
} catch (e) {
|
2017-10-15 21:50:30 +03:00
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
|
|
|
ok(success, "children" + msg);
|
|
|
|
|
|
|
|
// nextSibling
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.nextSibling;
|
|
|
|
} catch (e) {
|
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
ok(success, "nextSibling" + msg);
|
|
|
|
|
|
|
|
// previousSibling
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.previousSibling;
|
|
|
|
} catch (e) {
|
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
ok(success, "previousSibling" + msg);
|
|
|
|
|
|
|
|
// parent
|
|
|
|
success = false;
|
|
|
|
try {
|
|
|
|
aAcc.parent;
|
|
|
|
} catch (e) {
|
|
|
|
success = (e.result == Components.results.NS_ERROR_FAILURE);
|
|
|
|
}
|
|
|
|
ok(success, "parent" + msg);
|
|
|
|
}
|
|
|
|
|
2009-01-27 20:27:51 +03:00
|
|
|
/**
|
|
|
|
* Convert role to human readable string.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function roleToString(aRole) {
|
2016-08-08 18:47:48 +03:00
|
|
|
return gAccService.getStringRole(aRole);
|
2009-01-27 20:27:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert states to human readable string.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function statesToString(aStates, aExtraStates) {
|
2016-08-08 18:47:48 +03:00
|
|
|
var list = gAccService.getStringStates(aStates, aExtraStates);
|
2009-01-27 20:27:51 +03:00
|
|
|
|
|
|
|
var str = "";
|
2009-06-25 06:12:38 +04:00
|
|
|
for (var index = 0; index < list.length - 1; index++)
|
2009-01-27 20:27:51 +03:00
|
|
|
str += list.item(index) + ", ";
|
|
|
|
|
2009-06-25 06:12:38 +04:00
|
|
|
if (list.length != 0)
|
2017-10-15 21:50:30 +03:00
|
|
|
str += list.item(index);
|
2009-06-25 06:12:38 +04:00
|
|
|
|
2009-01-27 20:27:51 +03:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert event type to human readable string.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function eventTypeToString(aEventType) {
|
2016-08-08 18:47:48 +03:00
|
|
|
return gAccService.getStringEventType(aEventType);
|
2009-01-27 20:27:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert relation type to human readable string.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function relationTypeToString(aRelationType) {
|
2016-08-08 18:47:48 +03:00
|
|
|
return gAccService.getStringRelationType(aRelationType);
|
2009-01-27 20:27:51 +03:00
|
|
|
}
|
|
|
|
|
2012-04-17 06:14:01 +04:00
|
|
|
function getLoadContext() {
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
return window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
|
|
.QueryInterface(Ci.nsILoadContext);
|
|
|
|
}
|
|
|
|
|
2010-12-16 00:23:19 +03:00
|
|
|
/**
|
|
|
|
* Return text from clipboard.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getTextFromClipboard() {
|
2010-12-16 00:23:19 +03:00
|
|
|
var clip = Components.classes["@mozilla.org/widget/clipboard;1"].
|
|
|
|
getService(Components.interfaces.nsIClipboard);
|
|
|
|
if (!clip)
|
2013-03-01 08:06:16 +04:00
|
|
|
return "";
|
2010-12-16 00:23:19 +03:00
|
|
|
|
|
|
|
var trans = Components.classes["@mozilla.org/widget/transferable;1"].
|
|
|
|
createInstance(Components.interfaces.nsITransferable);
|
2012-04-17 06:14:01 +04:00
|
|
|
trans.init(getLoadContext());
|
2010-12-16 00:23:19 +03:00
|
|
|
if (!trans)
|
2013-03-01 08:06:16 +04:00
|
|
|
return "";
|
2010-12-16 00:23:19 +03:00
|
|
|
|
|
|
|
trans.addDataFlavor("text/unicode");
|
|
|
|
clip.getData(trans, clip.kGlobalClipboard);
|
|
|
|
|
|
|
|
var str = new Object();
|
|
|
|
var strLength = new Object();
|
|
|
|
trans.getTransferData("text/unicode", str, strLength);
|
|
|
|
|
|
|
|
if (str)
|
|
|
|
str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
|
|
|
|
if (str)
|
|
|
|
return str.data.substring(0, strLength.value / 2);
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2016-05-02 18:09:28 +03:00
|
|
|
/**
|
|
|
|
* Extract DOMNode id from an accessible. If e10s is enabled, DOMNode is not
|
|
|
|
* present in parent process but, if available, DOMNode id is attached to an
|
|
|
|
* accessible object.
|
|
|
|
* @param {nsIAccessible} accessible accessible
|
|
|
|
* @return {String?} DOMNode id if available
|
|
|
|
*/
|
|
|
|
function getAccessibleDOMNodeID(accessible) {
|
|
|
|
if (accessible instanceof nsIAccessibleDocument) {
|
|
|
|
// If accessible is a document, trying to find its document body id.
|
|
|
|
try {
|
|
|
|
return accessible.DOMNode.body.id;
|
|
|
|
} catch (e) { /* This only works if accessible is not a proxy. */ }
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
return accessible.DOMNode.id;
|
|
|
|
} catch (e) { /* This will fail if DOMNode is in different process. */ }
|
|
|
|
try {
|
|
|
|
// When e10s is enabled, accessible will have an "id" property if its
|
|
|
|
// corresponding DOMNode has an id. If accessible is a document, its "id"
|
|
|
|
// property corresponds to the "id" of its body element.
|
|
|
|
return accessible.id;
|
|
|
|
} catch (e) { /* This will fail if accessible is not a proxy. */ }
|
|
|
|
}
|
|
|
|
|
2009-02-10 13:03:30 +03:00
|
|
|
/**
|
|
|
|
* Return pretty name for identifier, it may be ID, DOM node or accessible.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function prettyName(aIdentifier) {
|
2013-02-26 11:17:10 +04:00
|
|
|
if (aIdentifier instanceof Array) {
|
|
|
|
var msg = "";
|
|
|
|
for (var idx = 0; idx < aIdentifier.length; idx++) {
|
|
|
|
if (msg != "")
|
|
|
|
msg += ", ";
|
|
|
|
|
|
|
|
msg += prettyName(aIdentifier[idx]);
|
|
|
|
}
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2009-02-10 13:03:30 +03:00
|
|
|
if (aIdentifier instanceof nsIAccessible) {
|
2012-02-07 17:18:33 +04:00
|
|
|
var acc = getAccessible(aIdentifier);
|
2016-05-02 18:09:28 +03:00
|
|
|
var domID = getAccessibleDOMNodeID(acc);
|
2015-02-19 07:37:32 +03:00
|
|
|
var msg = "[";
|
2010-06-08 20:39:58 +04:00
|
|
|
try {
|
2016-05-02 18:09:28 +03:00
|
|
|
if (Services.appinfo.browserTabsRemoteAutostart) {
|
|
|
|
if (domID) {
|
|
|
|
msg += `DOM node id: ${domID}, `;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
msg += `${getNodePrettyName(acc.DOMNode)}, `;
|
|
|
|
}
|
|
|
|
msg += "role: " + roleToString(acc.role);
|
2010-06-08 20:39:58 +04:00
|
|
|
if (acc.name)
|
2012-10-23 09:04:43 +04:00
|
|
|
msg += ", name: '" + shortenString(acc.name) + "'";
|
2010-06-08 20:39:58 +04:00
|
|
|
} catch (e) {
|
|
|
|
msg += "defunct";
|
|
|
|
}
|
2010-10-21 08:16:10 +04:00
|
|
|
|
2011-09-28 05:46:11 +04:00
|
|
|
if (acc)
|
|
|
|
msg += ", address: " + getObjAddress(acc);
|
2009-05-14 09:27:40 +04:00
|
|
|
msg += "]";
|
|
|
|
|
|
|
|
return msg;
|
2009-02-10 13:03:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aIdentifier instanceof nsIDOMNode)
|
2011-09-28 05:46:11 +04:00
|
|
|
return "[ " + getNodePrettyName(aIdentifier) + " ]";
|
2009-02-10 13:03:30 +03:00
|
|
|
|
2015-03-16 20:05:50 +03:00
|
|
|
if (aIdentifier && typeof aIdentifier === "object" ) {
|
2016-02-25 04:34:56 +03:00
|
|
|
var treeObj = normalizeAccTreeObj(aIdentifier);
|
|
|
|
if ("role" in treeObj) {
|
|
|
|
function stringifyTree(aObj) {
|
|
|
|
var text = roleToString(aObj.role) + ": [ ";
|
|
|
|
if ("children" in aObj) {
|
|
|
|
for (var i = 0; i < aObj.children.length; i++) {
|
|
|
|
var c = normalizeAccTreeObj(aObj.children[i]);
|
|
|
|
text += stringifyTree(c);
|
|
|
|
if (i < aObj.children.length - 1) {
|
|
|
|
text += ", ";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return text + "] ";
|
|
|
|
}
|
|
|
|
return `{ ${stringifyTree(treeObj)} }`;
|
|
|
|
}
|
2015-03-16 20:05:50 +03:00
|
|
|
return JSON.stringify(aIdentifier);
|
|
|
|
}
|
|
|
|
|
2009-02-10 13:03:30 +03:00
|
|
|
return " '" + aIdentifier + "' ";
|
|
|
|
}
|
|
|
|
|
2012-10-23 09:04:43 +04:00
|
|
|
/**
|
|
|
|
* Shorten a long string if it exceeds MAX_TRIM_LENGTH.
|
|
|
|
* @param aString the string to shorten.
|
|
|
|
* @returns the shortened string.
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function shortenString(aString, aMaxLength) {
|
2012-10-23 09:04:43 +04:00
|
|
|
if (aString.length <= MAX_TRIM_LENGTH)
|
|
|
|
return aString;
|
|
|
|
|
|
|
|
// Trim the string if its length is > MAX_TRIM_LENGTH characters.
|
|
|
|
var trimOffset = MAX_TRIM_LENGTH / 2;
|
|
|
|
return aString.substring(0, trimOffset - 1) + "..." +
|
|
|
|
aString.substring(aString.length - trimOffset, aString.length);
|
|
|
|
}
|
2012-10-10 04:14:44 +04:00
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2013-04-23 21:39:15 +04:00
|
|
|
// General Utils
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2013-04-23 21:39:15 +04:00
|
|
|
/**
|
|
|
|
* Return main chrome window (crosses chrome boundary)
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function getMainChromeWindow(aWindow) {
|
2013-04-23 21:39:15 +04:00
|
|
|
return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIWebNavigation)
|
|
|
|
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
|
|
|
|
.rootTreeItem
|
|
|
|
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindow);
|
|
|
|
}
|
|
|
|
|
2013-09-04 18:07:53 +04:00
|
|
|
/** Sets the test plugin(s) initially expected enabled state.
|
|
|
|
* It will automatically be reset to it's previous value after the test
|
|
|
|
* ends.
|
|
|
|
* @param aNewEnabledState [in] the enabled state, e.g. SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED
|
|
|
|
* @param aPluginName [in, optional] The name of the plugin, defaults to "Test Plug-in"
|
|
|
|
*/
|
2017-08-01 21:08:02 +03:00
|
|
|
function setTestPluginEnabledState(aNewEnabledState, aPluginName) {
|
2013-09-04 18:07:53 +04:00
|
|
|
var plugin = getTestPluginTag(aPluginName);
|
|
|
|
var oldEnabledState = plugin.enabledState;
|
|
|
|
plugin.enabledState = aNewEnabledState;
|
|
|
|
SimpleTest.registerCleanupFunction(function() {
|
|
|
|
getTestPluginTag(aPluginName).enabledState = oldEnabledState;
|
|
|
|
});
|
|
|
|
}
|
2013-04-23 21:39:15 +04:00
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2008-08-28 18:48:41 +04:00
|
|
|
// Private
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2008-08-28 18:48:41 +04:00
|
|
|
|
2017-08-01 19:15:51 +03:00
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
2008-09-01 05:52:40 +04:00
|
|
|
// Accessible general
|
2008-08-28 18:48:41 +04:00
|
|
|
|
2017-08-01 21:08:02 +03:00
|
|
|
function getNodePrettyName(aNode) {
|
2009-05-11 05:32:09 +04:00
|
|
|
try {
|
2011-09-28 05:46:11 +04:00
|
|
|
var tag = "";
|
|
|
|
if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE) {
|
|
|
|
tag = "document";
|
|
|
|
} else {
|
|
|
|
tag = aNode.localName;
|
|
|
|
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
|
|
|
|
tag += "@id=\"" + aNode.getAttribute("id") + "\"";
|
|
|
|
}
|
2010-10-21 08:16:10 +04:00
|
|
|
|
2011-09-28 05:46:11 +04:00
|
|
|
return "'" + tag + " node', address: " + getObjAddress(aNode);
|
2009-05-11 05:32:09 +04:00
|
|
|
} catch (e) {
|
2010-10-21 08:16:10 +04:00
|
|
|
return "' no node info '";
|
2009-05-11 05:32:09 +04:00
|
|
|
}
|
2009-02-10 13:03:30 +03:00
|
|
|
}
|
2011-09-28 05:46:11 +04:00
|
|
|
|
2017-08-01 21:08:02 +03:00
|
|
|
function getObjAddress(aObj) {
|
2011-09-28 05:46:11 +04:00
|
|
|
var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
|
2012-03-07 22:19:50 +04:00
|
|
|
var match = exp.exec(aObj.toString());
|
2011-09-28 05:46:11 +04:00
|
|
|
if (match)
|
|
|
|
return match[1];
|
|
|
|
|
2012-03-07 22:19:50 +04:00
|
|
|
return aObj.toString();
|
2011-09-28 05:46:11 +04:00
|
|
|
}
|
2013-09-04 18:07:53 +04:00
|
|
|
|
2017-08-01 21:08:02 +03:00
|
|
|
function getTestPluginTag(aPluginName) {
|
2013-09-04 18:07:53 +04:00
|
|
|
var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
|
|
|
|
.getService(SpecialPowers.Ci.nsIPluginHost);
|
|
|
|
var tags = ph.getPluginTags();
|
|
|
|
var name = aPluginName || "Test Plug-in";
|
|
|
|
for (var tag of tags) {
|
|
|
|
if (tag.name == name) {
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ok(false, "Could not find plugin tag with plugin name '" + name + "'");
|
|
|
|
return null;
|
|
|
|
}
|
2016-02-25 04:34:56 +03:00
|
|
|
|
2017-08-01 21:08:02 +03:00
|
|
|
function normalizeAccTreeObj(aObj) {
|
2016-02-25 04:34:56 +03:00
|
|
|
var key = Object.keys(aObj)[0];
|
|
|
|
var roleName = "ROLE_" + key;
|
|
|
|
if (roleName in nsIAccessibleRole) {
|
|
|
|
return {
|
|
|
|
role: nsIAccessibleRole[roleName],
|
|
|
|
children: aObj[key]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return aObj;
|
|
|
|
}
|