michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Interfaces michael@0: michael@0: const nsIAccessibleRetrieval = Components.interfaces.nsIAccessibleRetrieval; michael@0: michael@0: const nsIAccessibleEvent = Components.interfaces.nsIAccessibleEvent; michael@0: const nsIAccessibleStateChangeEvent = michael@0: Components.interfaces.nsIAccessibleStateChangeEvent; michael@0: const nsIAccessibleCaretMoveEvent = michael@0: Components.interfaces.nsIAccessibleCaretMoveEvent; michael@0: const nsIAccessibleTextChangeEvent = michael@0: Components.interfaces.nsIAccessibleTextChangeEvent; michael@0: const nsIAccessibleVirtualCursorChangeEvent = michael@0: Components.interfaces.nsIAccessibleVirtualCursorChangeEvent; michael@0: michael@0: const nsIAccessibleStates = Components.interfaces.nsIAccessibleStates; michael@0: const nsIAccessibleRole = Components.interfaces.nsIAccessibleRole; michael@0: const nsIAccessibleScrollType = Components.interfaces.nsIAccessibleScrollType; michael@0: const nsIAccessibleCoordinateType = Components.interfaces.nsIAccessibleCoordinateType; michael@0: michael@0: const nsIAccessibleRelation = Components.interfaces.nsIAccessibleRelation; michael@0: michael@0: const nsIAccessible = Components.interfaces.nsIAccessible; michael@0: michael@0: const nsIAccessibleDocument = Components.interfaces.nsIAccessibleDocument; michael@0: const nsIAccessibleApplication = Components.interfaces.nsIAccessibleApplication; michael@0: michael@0: const nsIAccessibleText = Components.interfaces.nsIAccessibleText; michael@0: const nsIAccessibleEditableText = Components.interfaces.nsIAccessibleEditableText; michael@0: michael@0: const nsIAccessibleHyperLink = Components.interfaces.nsIAccessibleHyperLink; michael@0: const nsIAccessibleHyperText = Components.interfaces.nsIAccessibleHyperText; michael@0: michael@0: const nsIAccessibleCursorable = Components.interfaces.nsIAccessibleCursorable; michael@0: const nsIAccessibleImage = Components.interfaces.nsIAccessibleImage; michael@0: const nsIAccessiblePivot = Components.interfaces.nsIAccessiblePivot; michael@0: const nsIAccessibleSelectable = Components.interfaces.nsIAccessibleSelectable; michael@0: const nsIAccessibleTable = Components.interfaces.nsIAccessibleTable; michael@0: const nsIAccessibleTableCell = Components.interfaces.nsIAccessibleTableCell; michael@0: const nsIAccessibleTraversalRule = Components.interfaces.nsIAccessibleTraversalRule; michael@0: const nsIAccessibleValue = Components.interfaces.nsIAccessibleValue; michael@0: michael@0: const nsIObserverService = Components.interfaces.nsIObserverService; michael@0: michael@0: const nsIDOMDocument = Components.interfaces.nsIDOMDocument; michael@0: const nsIDOMEvent = Components.interfaces.nsIDOMEvent; michael@0: const nsIDOMHTMLDocument = Components.interfaces.nsIDOMHTMLDocument; michael@0: const nsIDOMNode = Components.interfaces.nsIDOMNode; michael@0: const nsIDOMHTMLElement = Components.interfaces.nsIDOMHTMLElement; michael@0: const nsIDOMWindow = Components.interfaces.nsIDOMWindow; michael@0: const nsIDOMXULElement = Components.interfaces.nsIDOMXULElement; michael@0: michael@0: const nsIPropertyElement = Components.interfaces.nsIPropertyElement; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // OS detect michael@0: michael@0: const MAC = (navigator.platform.indexOf("Mac") != -1); michael@0: const LINUX = (navigator.platform.indexOf("Linux") != -1); michael@0: const SOLARIS = (navigator.platform.indexOf("SunOS") != -1); michael@0: const WIN = (navigator.platform.indexOf("Win") != -1); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Application detect michael@0: // Firefox is assumed by default. michael@0: michael@0: const SEAMONKEY = navigator.userAgent.match(/ SeaMonkey\//); michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Accessible general michael@0: michael@0: const STATE_BUSY = nsIAccessibleStates.STATE_BUSY; michael@0: michael@0: const SCROLL_TYPE_ANYWHERE = nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE; michael@0: michael@0: const COORDTYPE_SCREEN_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE; michael@0: const COORDTYPE_WINDOW_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_WINDOW_RELATIVE; michael@0: const COORDTYPE_PARENT_RELATIVE = nsIAccessibleCoordinateType.COORDTYPE_PARENT_RELATIVE; michael@0: michael@0: const kEmbedChar = String.fromCharCode(0xfffc); michael@0: michael@0: const kDiscBulletChar = String.fromCharCode(0x2022); michael@0: const kDiscBulletText = kDiscBulletChar + " "; michael@0: const kCircleBulletText = String.fromCharCode(0x25e6) + " "; michael@0: const kSquareBulletText = String.fromCharCode(0x25aa) + " "; michael@0: michael@0: const MAX_TRIM_LENGTH = 100; michael@0: michael@0: /** michael@0: * nsIAccessibleRetrieval service. michael@0: */ michael@0: var gAccRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"]. michael@0: getService(nsIAccessibleRetrieval); michael@0: michael@0: /** michael@0: * Enable/disable logging. michael@0: */ michael@0: function enableLogging(aModules) michael@0: { michael@0: gAccRetrieval.setLogging(aModules); michael@0: } michael@0: function disableLogging() michael@0: { michael@0: gAccRetrieval.setLogging(""); michael@0: } michael@0: function isLogged(aModule) michael@0: { michael@0: return gAccRetrieval.isLogged(aModule); michael@0: } michael@0: michael@0: /** michael@0: * Invokes the given function when document is loaded and focused. Preferable michael@0: * to mochitests 'addLoadEvent' function -- additionally ensures state of the michael@0: * document accessible is not busy. michael@0: * michael@0: * @param aFunc the function to invoke michael@0: */ michael@0: function addA11yLoadEvent(aFunc, aWindow) michael@0: { michael@0: function waitForDocLoad() michael@0: { michael@0: window.setTimeout( michael@0: function() michael@0: { michael@0: var targetDocument = aWindow ? aWindow.document : document; michael@0: var accDoc = getAccessible(targetDocument); michael@0: var state = {}; michael@0: accDoc.getState(state, {}); michael@0: if (state.value & STATE_BUSY) michael@0: return waitForDocLoad(); michael@0: michael@0: window.setTimeout(aFunc, 0); michael@0: }, michael@0: 0 michael@0: ); michael@0: } michael@0: michael@0: SimpleTest.waitForFocus(waitForDocLoad, aWindow); michael@0: } michael@0: michael@0: /** michael@0: * Analogy of SimpleTest.is function used to compare objects. michael@0: */ michael@0: function isObject(aObj, aExpectedObj, aMsg) michael@0: { michael@0: if (aObj == aExpectedObj) { michael@0: ok(true, aMsg); michael@0: return; michael@0: } michael@0: michael@0: ok(false, michael@0: aMsg + " - got '" + prettyName(aObj) + michael@0: "', expected '" + prettyName(aExpectedObj) + "'"); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Helpers for getting DOM node/accessible michael@0: michael@0: /** michael@0: * Return the DOM node by identifier (may be accessible, DOM node or ID). michael@0: */ michael@0: function getNode(aAccOrNodeOrID, aDocument) michael@0: { michael@0: if (!aAccOrNodeOrID) michael@0: return null; michael@0: michael@0: if (aAccOrNodeOrID instanceof nsIDOMNode) michael@0: return aAccOrNodeOrID; michael@0: michael@0: if (aAccOrNodeOrID instanceof nsIAccessible) michael@0: return aAccOrNodeOrID.DOMNode; michael@0: michael@0: node = (aDocument || document).getElementById(aAccOrNodeOrID); michael@0: if (!node) { michael@0: ok(false, "Can't get DOM element for " + aAccOrNodeOrID); michael@0: return null; michael@0: } michael@0: michael@0: return node; michael@0: } michael@0: michael@0: /** michael@0: * Constants indicates getAccessible doesn't fail if there is no accessible. michael@0: */ michael@0: const DONOTFAIL_IF_NO_ACC = 1; michael@0: michael@0: /** michael@0: * Constants indicates getAccessible won't fail if accessible doesn't implement michael@0: * the requested interfaces. michael@0: */ michael@0: const DONOTFAIL_IF_NO_INTERFACE = 2; michael@0: michael@0: /** michael@0: * Return accessible for the given identifier (may be ID attribute or DOM michael@0: * element or accessible object) or null. michael@0: * michael@0: * @param aAccOrElmOrID [in] identifier to get an accessible implementing michael@0: * the given interfaces michael@0: * @param aInterfaces [in, optional] the interface or an array interfaces michael@0: * to query it/them from obtained accessible michael@0: * @param aElmObj [out, optional] object to store DOM element which michael@0: * accessible is obtained for michael@0: * @param aDoNotFailIf [in, optional] no error for special cases (see michael@0: * constants above) michael@0: */ michael@0: function getAccessible(aAccOrElmOrID, aInterfaces, aElmObj, aDoNotFailIf) michael@0: { michael@0: if (!aAccOrElmOrID) michael@0: return null; michael@0: michael@0: var elm = null; michael@0: michael@0: if (aAccOrElmOrID instanceof nsIAccessible) { michael@0: elm = aAccOrElmOrID.DOMNode; michael@0: michael@0: } else if (aAccOrElmOrID instanceof nsIDOMNode) { michael@0: elm = aAccOrElmOrID; michael@0: michael@0: } else { michael@0: elm = document.getElementById(aAccOrElmOrID); michael@0: if (!elm) { michael@0: ok(false, "Can't get DOM element for " + aAccOrElmOrID); michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: if (aElmObj && (typeof aElmObj == "object")) michael@0: aElmObj.value = elm; michael@0: michael@0: var acc = (aAccOrElmOrID instanceof nsIAccessible) ? aAccOrElmOrID : null; michael@0: if (!acc) { michael@0: try { michael@0: acc = gAccRetrieval.getAccessibleFor(elm); michael@0: } catch (e) { michael@0: } michael@0: michael@0: if (!acc) { michael@0: if (!(aDoNotFailIf & DONOTFAIL_IF_NO_ACC)) michael@0: ok(false, "Can't get accessible for " + aAccOrElmOrID); michael@0: michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: if (!aInterfaces) michael@0: return acc; michael@0: michael@0: if (!(aInterfaces instanceof Array)) michael@0: aInterfaces = [ aInterfaces ]; michael@0: michael@0: for (var index = 0; index < aInterfaces.length; index++) { michael@0: try { michael@0: acc.QueryInterface(aInterfaces[index]); michael@0: } catch (e) { michael@0: if (!(aDoNotFailIf & DONOTFAIL_IF_NO_INTERFACE)) michael@0: ok(false, "Can't query " + aInterfaces[index] + " for " + aAccOrElmOrID); michael@0: michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: return acc; michael@0: } michael@0: michael@0: /** michael@0: * Return true if the given identifier has an accessible, or exposes the wanted michael@0: * interfaces. michael@0: */ michael@0: function isAccessible(aAccOrElmOrID, aInterfaces) michael@0: { michael@0: return getAccessible(aAccOrElmOrID, aInterfaces, null, michael@0: DONOTFAIL_IF_NO_ACC | DONOTFAIL_IF_NO_INTERFACE) ? michael@0: true : false; michael@0: } michael@0: michael@0: /** michael@0: * Return an accessible that contains the DOM node for the given identifier. michael@0: */ michael@0: function getContainerAccessible(aAccOrElmOrID) michael@0: { michael@0: var node = getNode(aAccOrElmOrID); michael@0: if (!node) michael@0: return null; michael@0: michael@0: while ((node = node.parentNode) && !isAccessible(node)); michael@0: return node ? getAccessible(node) : null; michael@0: } michael@0: michael@0: /** michael@0: * Return root accessible for the given identifier. michael@0: */ michael@0: function getRootAccessible(aAccOrElmOrID) michael@0: { michael@0: var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document); michael@0: return acc ? acc.rootDocument.QueryInterface(nsIAccessible) : null; michael@0: } michael@0: michael@0: /** michael@0: * Return tab document accessible the given accessible is contained by. michael@0: */ michael@0: function getTabDocAccessible(aAccOrElmOrID) michael@0: { michael@0: var acc = getAccessible(aAccOrElmOrID ? aAccOrElmOrID : document); michael@0: michael@0: var docAcc = acc.document.QueryInterface(nsIAccessible); michael@0: var containerDocAcc = docAcc.parent.document; michael@0: michael@0: // Test is running is stand-alone mode. michael@0: if (acc.rootDocument == containerDocAcc) michael@0: return docAcc; michael@0: michael@0: // In the case of running all tests together. michael@0: return containerDocAcc.QueryInterface(nsIAccessible); michael@0: } michael@0: michael@0: /** michael@0: * Return application accessible. michael@0: */ michael@0: function getApplicationAccessible() michael@0: { michael@0: return gAccRetrieval.getApplicationAccessible(). michael@0: QueryInterface(nsIAccessibleApplication); michael@0: } michael@0: michael@0: /** michael@0: * A version of accessible tree testing, doesn't fail if tree is not complete. michael@0: */ michael@0: function testElm(aID, aTreeObj) michael@0: { michael@0: testAccessibleTree(aID, aTreeObj, kSkipTreeFullCheck); michael@0: } michael@0: michael@0: /** michael@0: * Flags used for testAccessibleTree michael@0: */ michael@0: const kSkipTreeFullCheck = 1; michael@0: michael@0: /** michael@0: * Compare expected and actual accessibles trees. michael@0: * michael@0: * @param aAccOrElmOrID [in] accessible identifier michael@0: * @param aAccTree [in] JS object, each field corresponds to property of michael@0: * accessible object. Additionally special properties michael@0: * are presented: michael@0: * children - an array of JS objects representing michael@0: * children of accessible michael@0: * states - an object having states and extraStates michael@0: * fields michael@0: * @param aFlags [in, optional] flags, see constants above michael@0: */ michael@0: function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags) michael@0: { michael@0: var acc = getAccessible(aAccOrElmOrID); michael@0: if (!acc) michael@0: return; michael@0: michael@0: var accTree = aAccTree; michael@0: michael@0: // Support of simplified accessible tree object. michael@0: var key = Object.keys(accTree)[0]; michael@0: var roleName = "ROLE_" + key; michael@0: if (roleName in nsIAccessibleRole) { michael@0: accTree = { michael@0: role: nsIAccessibleRole[roleName], michael@0: children: accTree[key] michael@0: }; michael@0: } michael@0: michael@0: // Test accessible properties. michael@0: for (var prop in accTree) { michael@0: var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + "."; michael@0: michael@0: switch (prop) { michael@0: case "actions": { michael@0: testActionNames(acc, accTree.actions); michael@0: break; michael@0: } michael@0: michael@0: case "attributes": michael@0: testAttrs(acc, accTree[prop], true); michael@0: break; michael@0: michael@0: case "absentAttributes": michael@0: testAbsentAttrs(acc, accTree[prop]); michael@0: break; michael@0: michael@0: case "interfaces": { michael@0: var ifaces = (accTree[prop] instanceof Array) ? michael@0: accTree[prop] : [ accTree[prop] ]; michael@0: for (var i = 0; i < ifaces.length; i++) { michael@0: ok((acc instanceof ifaces[i]), michael@0: "No " + ifaces[i] + " interface on " + prettyName(acc)); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case "relations": { michael@0: for (var rel in accTree[prop]) michael@0: testRelation(acc, window[rel], accTree[prop][rel]); michael@0: break; michael@0: } michael@0: michael@0: case "role": michael@0: isRole(acc, accTree[prop], msg); michael@0: break; michael@0: michael@0: case "states": michael@0: case "extraStates": michael@0: case "absentStates": michael@0: case "absentExtraStates": { michael@0: testStates(acc, accTree["states"], accTree["extraStates"], michael@0: accTree["absentStates"], accTree["absentExtraStates"]); michael@0: break; michael@0: } michael@0: michael@0: case "tagName": michael@0: is(accTree[prop], acc.DOMNode.tagName, msg); michael@0: break; michael@0: michael@0: case "textAttrs": { michael@0: var prevOffset = -1; michael@0: for (var offset in accTree[prop]) { michael@0: if (prevOffset !=- 1) { michael@0: var attrs = accTree[prop][prevOffset]; michael@0: testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, offset, true); michael@0: } michael@0: prevOffset = offset; michael@0: } michael@0: michael@0: if (prevOffset != -1) { michael@0: var charCount = getAccessible(acc, [nsIAccessibleText]).characterCount; michael@0: var attrs = accTree[prop][prevOffset]; michael@0: testTextAttrs(acc, prevOffset, attrs, { }, prevOffset, charCount, true); michael@0: } michael@0: michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: if (prop.indexOf("todo_") == 0) michael@0: todo(false, msg); michael@0: else if (prop != "children") michael@0: is(acc[prop], accTree[prop], msg); michael@0: } michael@0: } michael@0: michael@0: // Test children. michael@0: if ("children" in accTree && accTree["children"] instanceof Array) { michael@0: var children = acc.children; michael@0: var childCount = children.length; michael@0: michael@0: is(childCount, accTree.children.length, michael@0: "Different amount of expected children of " + prettyName(acc) + "."); michael@0: michael@0: if (accTree.children.length == childCount) { michael@0: if (aFlags & kSkipTreeFullCheck) { michael@0: for (var i = 0; i < childCount; i++) { michael@0: var child = children.queryElementAt(i, nsIAccessible); michael@0: testAccessibleTree(child, accTree.children[i], aFlags); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // nsIAccessible::firstChild michael@0: var expectedFirstChild = childCount > 0 ? michael@0: children.queryElementAt(0, nsIAccessible) : null; michael@0: var firstChild = null; michael@0: try { firstChild = acc.firstChild; } catch (e) {} michael@0: is(firstChild, expectedFirstChild, michael@0: "Wrong first child of " + prettyName(acc)); michael@0: michael@0: // nsIAccessible::lastChild michael@0: var expectedLastChild = childCount > 0 ? michael@0: children.queryElementAt(childCount - 1, nsIAccessible) : null; michael@0: var lastChild = null; michael@0: try { lastChild = acc.lastChild; } catch (e) {} michael@0: is(lastChild, expectedLastChild, michael@0: "Wrong last child of " + prettyName(acc)); michael@0: michael@0: for (var i = 0; i < childCount; i++) { michael@0: var child = children.queryElementAt(i, nsIAccessible); michael@0: michael@0: // nsIAccessible::parent michael@0: var parent = null; michael@0: try { parent = child.parent; } catch (e) {} michael@0: is(parent, acc, "Wrong parent of " + prettyName(child)); michael@0: michael@0: // nsIAccessible::indexInParent michael@0: var indexInParent = -1; michael@0: try { indexInParent = child.indexInParent; } catch(e) {} michael@0: is(indexInParent, i, michael@0: "Wrong index in parent of " + prettyName(child)); michael@0: michael@0: // nsIAccessible::nextSibling michael@0: var expectedNextSibling = (i < childCount - 1) ? michael@0: children.queryElementAt(i + 1, nsIAccessible) : null; michael@0: var nextSibling = null; michael@0: try { nextSibling = child.nextSibling; } catch (e) {} michael@0: is(nextSibling, expectedNextSibling, michael@0: "Wrong next sibling of " + prettyName(child)); michael@0: michael@0: // nsIAccessible::previousSibling michael@0: var expectedPrevSibling = (i > 0) ? michael@0: children.queryElementAt(i - 1, nsIAccessible) : null; michael@0: var prevSibling = null; michael@0: try { prevSibling = child.previousSibling; } catch (e) {} michael@0: is(prevSibling, expectedPrevSibling, michael@0: "Wrong previous sibling of " + prettyName(child)); michael@0: michael@0: // Go down through subtree michael@0: testAccessibleTree(child, accTree.children[i], aFlags); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Return true if accessible for the given node is in cache. michael@0: */ michael@0: function isAccessibleInCache(aNodeOrId) michael@0: { michael@0: var node = getNode(aNodeOrId); michael@0: return gAccRetrieval.getAccessibleFromCache(node) ? true : false; michael@0: } michael@0: michael@0: /** michael@0: * Test accessible tree for defunct accessible. michael@0: * michael@0: * @param aAcc [in] the defunct accessible michael@0: * @param aNodeOrId [in] the DOM node identifier for the defunct accessible michael@0: */ michael@0: function testDefunctAccessible(aAcc, aNodeOrId) michael@0: { michael@0: if (aNodeOrId) michael@0: ok(!isAccessible(aNodeOrId), michael@0: "Accessible for " + aNodeOrId + " wasn't properly shut down!"); michael@0: michael@0: var msg = " doesn't fail for shut down accessible " + prettyName(aNodeOrId) + "!"; michael@0: michael@0: // firstChild michael@0: var success = false; michael@0: try { michael@0: aAcc.firstChild; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE) michael@0: } michael@0: ok(success, "firstChild" + msg); michael@0: michael@0: // lastChild michael@0: success = false; michael@0: try { michael@0: aAcc.lastChild; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE) michael@0: } michael@0: ok(success, "lastChild" + msg); michael@0: michael@0: // childCount michael@0: success = false; michael@0: try { michael@0: aAcc.childCount; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE) michael@0: } michael@0: ok(success, "childCount" + msg); michael@0: michael@0: // children michael@0: success = false; michael@0: try { michael@0: aAcc.children; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE) michael@0: } michael@0: ok(success, "children" + msg); michael@0: michael@0: // nextSibling michael@0: success = false; michael@0: try { michael@0: aAcc.nextSibling; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE); michael@0: } michael@0: ok(success, "nextSibling" + msg); michael@0: michael@0: // previousSibling michael@0: success = false; michael@0: try { michael@0: aAcc.previousSibling; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE); michael@0: } michael@0: ok(success, "previousSibling" + msg); michael@0: michael@0: // parent michael@0: success = false; michael@0: try { michael@0: aAcc.parent; michael@0: } catch (e) { michael@0: success = (e.result == Components.results.NS_ERROR_FAILURE); michael@0: } michael@0: ok(success, "parent" + msg); michael@0: } michael@0: michael@0: /** michael@0: * Convert role to human readable string. michael@0: */ michael@0: function roleToString(aRole) michael@0: { michael@0: return gAccRetrieval.getStringRole(aRole); michael@0: } michael@0: michael@0: /** michael@0: * Convert states to human readable string. michael@0: */ michael@0: function statesToString(aStates, aExtraStates) michael@0: { michael@0: var list = gAccRetrieval.getStringStates(aStates, aExtraStates); michael@0: michael@0: var str = ""; michael@0: for (var index = 0; index < list.length - 1; index++) michael@0: str += list.item(index) + ", "; michael@0: michael@0: if (list.length != 0) michael@0: str += list.item(index) michael@0: michael@0: return str; michael@0: } michael@0: michael@0: /** michael@0: * Convert event type to human readable string. michael@0: */ michael@0: function eventTypeToString(aEventType) michael@0: { michael@0: return gAccRetrieval.getStringEventType(aEventType); michael@0: } michael@0: michael@0: /** michael@0: * Convert relation type to human readable string. michael@0: */ michael@0: function relationTypeToString(aRelationType) michael@0: { michael@0: return gAccRetrieval.getStringRelationType(aRelationType); michael@0: } michael@0: michael@0: function getLoadContext() { michael@0: const Ci = Components.interfaces; michael@0: return window.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Ci.nsIWebNavigation) michael@0: .QueryInterface(Ci.nsILoadContext); michael@0: } michael@0: michael@0: /** michael@0: * Return text from clipboard. michael@0: */ michael@0: function getTextFromClipboard() michael@0: { michael@0: var clip = Components.classes["@mozilla.org/widget/clipboard;1"]. michael@0: getService(Components.interfaces.nsIClipboard); michael@0: if (!clip) michael@0: return ""; michael@0: michael@0: var trans = Components.classes["@mozilla.org/widget/transferable;1"]. michael@0: createInstance(Components.interfaces.nsITransferable); michael@0: trans.init(getLoadContext()); michael@0: if (!trans) michael@0: return ""; michael@0: michael@0: trans.addDataFlavor("text/unicode"); michael@0: clip.getData(trans, clip.kGlobalClipboard); michael@0: michael@0: var str = new Object(); michael@0: var strLength = new Object(); michael@0: trans.getTransferData("text/unicode", str, strLength); michael@0: michael@0: if (str) michael@0: str = str.value.QueryInterface(Components.interfaces.nsISupportsString); michael@0: if (str) michael@0: return str.data.substring(0, strLength.value / 2); michael@0: michael@0: return ""; michael@0: } michael@0: michael@0: /** michael@0: * Return pretty name for identifier, it may be ID, DOM node or accessible. michael@0: */ michael@0: function prettyName(aIdentifier) michael@0: { michael@0: if (aIdentifier instanceof Array) { michael@0: var msg = ""; michael@0: for (var idx = 0; idx < aIdentifier.length; idx++) { michael@0: if (msg != "") michael@0: msg += ", "; michael@0: michael@0: msg += prettyName(aIdentifier[idx]); michael@0: } michael@0: return msg; michael@0: } michael@0: michael@0: if (aIdentifier instanceof nsIAccessible) { michael@0: var acc = getAccessible(aIdentifier); michael@0: var msg = "[" + getNodePrettyName(acc.DOMNode); michael@0: try { michael@0: msg += ", role: " + roleToString(acc.role); michael@0: if (acc.name) michael@0: msg += ", name: '" + shortenString(acc.name) + "'"; michael@0: } catch (e) { michael@0: msg += "defunct"; michael@0: } michael@0: michael@0: if (acc) michael@0: msg += ", address: " + getObjAddress(acc); michael@0: msg += "]"; michael@0: michael@0: return msg; michael@0: } michael@0: michael@0: if (aIdentifier instanceof nsIDOMNode) michael@0: return "[ " + getNodePrettyName(aIdentifier) + " ]"; michael@0: michael@0: return " '" + aIdentifier + "' "; michael@0: } michael@0: michael@0: /** michael@0: * Shorten a long string if it exceeds MAX_TRIM_LENGTH. michael@0: * @param aString the string to shorten. michael@0: * @returns the shortened string. michael@0: */ michael@0: function shortenString(aString, aMaxLength) michael@0: { michael@0: if (aString.length <= MAX_TRIM_LENGTH) michael@0: return aString; michael@0: michael@0: // Trim the string if its length is > MAX_TRIM_LENGTH characters. michael@0: var trimOffset = MAX_TRIM_LENGTH / 2; michael@0: return aString.substring(0, trimOffset - 1) + "..." + michael@0: aString.substring(aString.length - trimOffset, aString.length); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // General Utils michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: /** michael@0: * Return main chrome window (crosses chrome boundary) michael@0: */ michael@0: function getMainChromeWindow(aWindow) michael@0: { michael@0: return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) michael@0: .getInterface(Components.interfaces.nsIWebNavigation) michael@0: .QueryInterface(Components.interfaces.nsIDocShellTreeItem) michael@0: .rootTreeItem michael@0: .QueryInterface(Components.interfaces.nsIInterfaceRequestor) michael@0: .getInterface(Components.interfaces.nsIDOMWindow); michael@0: } michael@0: michael@0: /** Sets the test plugin(s) initially expected enabled state. michael@0: * It will automatically be reset to it's previous value after the test michael@0: * ends. michael@0: * @param aNewEnabledState [in] the enabled state, e.g. SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED michael@0: * @param aPluginName [in, optional] The name of the plugin, defaults to "Test Plug-in" michael@0: */ michael@0: function setTestPluginEnabledState(aNewEnabledState, aPluginName) michael@0: { michael@0: var plugin = getTestPluginTag(aPluginName); michael@0: var oldEnabledState = plugin.enabledState; michael@0: plugin.enabledState = aNewEnabledState; michael@0: SimpleTest.registerCleanupFunction(function() { michael@0: getTestPluginTag(aPluginName).enabledState = oldEnabledState; michael@0: }); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Private michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Accessible general michael@0: michael@0: function getNodePrettyName(aNode) michael@0: { michael@0: try { michael@0: var tag = ""; michael@0: if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE) { michael@0: tag = "document"; michael@0: } else { michael@0: tag = aNode.localName; michael@0: if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id")) michael@0: tag += "@id=\"" + aNode.getAttribute("id") + "\""; michael@0: } michael@0: michael@0: return "'" + tag + " node', address: " + getObjAddress(aNode); michael@0: } catch (e) { michael@0: return "' no node info '"; michael@0: } michael@0: } michael@0: michael@0: function getObjAddress(aObj) michael@0: { michael@0: var exp = /native\s*@\s*(0x[a-f0-9]+)/g; michael@0: var match = exp.exec(aObj.toString()); michael@0: if (match) michael@0: return match[1]; michael@0: michael@0: return aObj.toString(); michael@0: } michael@0: michael@0: function getTestPluginTag(aPluginName) michael@0: { michael@0: var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"] michael@0: .getService(SpecialPowers.Ci.nsIPluginHost); michael@0: var tags = ph.getPluginTags(); michael@0: var name = aPluginName || "Test Plug-in"; michael@0: for (var tag of tags) { michael@0: if (tag.name == name) { michael@0: return tag; michael@0: } michael@0: } michael@0: michael@0: ok(false, "Could not find plugin tag with plugin name '" + name + "'"); michael@0: return null; michael@0: }