michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Object attributes. michael@0: michael@0: /** michael@0: * Test object attributes. michael@0: * michael@0: * @param aAccOrElmOrID [in] the accessible identifier michael@0: * @param aAttrs [in] the map of expected object attributes michael@0: * (name/value pairs) michael@0: * @param aSkipUnexpectedAttrs [in] points this function doesn't fail if michael@0: * unexpected attribute is encountered michael@0: */ michael@0: function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs) michael@0: { michael@0: testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs); michael@0: } michael@0: michael@0: /** michael@0: * Test object attributes that must not be present. michael@0: * michael@0: * @param aAccOrElmOrID [in] the accessible identifier michael@0: * @param aAbsentAttrs [in] map of attributes that should not be michael@0: * present (name/value pairs) michael@0: */ michael@0: function testAbsentAttrs(aAccOrElmOrID, aAbsentAttrs) michael@0: { michael@0: testAttrsInternal(aAccOrElmOrID, {}, true, aAbsentAttrs); michael@0: } michael@0: michael@0: /** michael@0: * Test CSS based object attributes. michael@0: */ michael@0: function testCSSAttrs(aID) michael@0: { michael@0: var node = document.getElementById(aID); michael@0: var computedStyle = document.defaultView.getComputedStyle(node, ""); michael@0: michael@0: var attrs = { michael@0: "display": computedStyle.display, michael@0: "text-align": computedStyle.textAlign, michael@0: "text-indent": computedStyle.textIndent, michael@0: "margin-left": computedStyle.marginLeft, michael@0: "margin-right": computedStyle.marginRight, michael@0: "margin-top": computedStyle.marginTop, michael@0: "margin-bottom": computedStyle.marginBottom michael@0: }; michael@0: testAttrs(aID, attrs, true); michael@0: } michael@0: michael@0: /** michael@0: * Test the accessible that it doesn't have CSS-based object attributes. michael@0: */ michael@0: function testAbsentCSSAttrs(aID) michael@0: { michael@0: var attrs = { michael@0: "display": "", michael@0: "text-align": "", michael@0: "text-indent": "", michael@0: "margin-left": "", michael@0: "margin-right": "", michael@0: "margin-top": "", michael@0: "margin-bottom": "" michael@0: }; michael@0: testAbsentAttrs(aID, attrs); michael@0: } michael@0: michael@0: /** michael@0: * Test group object attributes (posinset, setsize and level) and michael@0: * nsIAccessible::groupPosition() method. michael@0: * michael@0: * @param aAccOrElmOrID [in] the ID, DOM node or accessible michael@0: * @param aPosInSet [in] the value of 'posinset' attribute michael@0: * @param aSetSize [in] the value of 'setsize' attribute michael@0: * @param aLevel [in, optional] the value of 'level' attribute michael@0: */ michael@0: function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel) michael@0: { michael@0: var acc = getAccessible(aAccOrElmOrID); michael@0: var levelObj = {}, posInSetObj = {}, setSizeObj = {}; michael@0: acc.groupPosition(levelObj, setSizeObj, posInSetObj); michael@0: michael@0: if (aPosInSet && aSetSize) { michael@0: is(posInSetObj.value, aPosInSet, michael@0: "Wrong group position (posinset) for " + prettyName(aAccOrElmOrID)); michael@0: is(setSizeObj.value, aSetSize, michael@0: "Wrong size of the group (setsize) for " + prettyName(aAccOrElmOrID)); michael@0: michael@0: var attrs = { michael@0: "posinset": String(aPosInSet), michael@0: "setsize": String(aSetSize) michael@0: }; michael@0: testAttrs(aAccOrElmOrID, attrs, true); michael@0: } michael@0: michael@0: if (aLevel) { michael@0: is(levelObj.value, aLevel, michael@0: "Wrong group level for " + prettyName(aAccOrElmOrID)); michael@0: michael@0: var attrs = { "level" : String(aLevel) }; michael@0: testAttrs(aAccOrElmOrID, attrs, true); michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Text attributes. michael@0: michael@0: /** michael@0: * Test text attributes. michael@0: * michael@0: * @param aID [in] the ID of DOM element having text michael@0: * accessible michael@0: * @param aOffset [in] the offset inside text accessible to fetch michael@0: * text attributes michael@0: * @param aAttrs [in] the map of expected text attributes michael@0: * (name/value pairs) exposed at the offset michael@0: * @param aDefAttrs [in] the map of expected text attributes michael@0: * (name/value pairs) exposed on hyper text michael@0: * accessible michael@0: * @param aStartOffset [in] expected start offset where text attributes michael@0: * are applied michael@0: * @param aEndOffset [in] expected end offset where text attribute michael@0: * are applied michael@0: * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if michael@0: * unexpected attribute is encountered michael@0: */ michael@0: function testTextAttrs(aID, aOffset, aAttrs, aDefAttrs, michael@0: aStartOffset, aEndOffset, aSkipUnexpectedAttrs) michael@0: { michael@0: var accessible = getAccessible(aID, [nsIAccessibleText]); michael@0: if (!accessible) michael@0: return; michael@0: michael@0: var startOffset = { value: -1 }; michael@0: var endOffset = { value: -1 }; michael@0: michael@0: // do not include attributes exposed on hyper text accessbile michael@0: var attrs = getTextAttributes(aID, accessible, false, aOffset, michael@0: startOffset, endOffset); michael@0: michael@0: if (!attrs) michael@0: return; michael@0: michael@0: var errorMsg = " for " + aID + " at offset " + aOffset; michael@0: michael@0: is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg); michael@0: is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg); michael@0: michael@0: compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs); michael@0: michael@0: // include attributes exposed on hyper text accessbile michael@0: var expectedAttrs = {}; michael@0: for (var name in aAttrs) michael@0: expectedAttrs[name] = aAttrs[name]; michael@0: michael@0: for (var name in aDefAttrs) { michael@0: if (!(name in expectedAttrs)) michael@0: expectedAttrs[name] = aDefAttrs[name]; michael@0: } michael@0: michael@0: attrs = getTextAttributes(aID, accessible, true, aOffset, michael@0: startOffset, endOffset); michael@0: michael@0: if (!attrs) michael@0: return; michael@0: michael@0: compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs); michael@0: } michael@0: michael@0: /** michael@0: * Test default text attributes. michael@0: * michael@0: * @param aID [in] the ID of DOM element having text michael@0: * accessible michael@0: * @param aDefAttrs [in] the map of expected text attributes michael@0: * (name/value pairs) michael@0: * @param aSkipUnexpectedAttrs [in] points the function doesn't fail if michael@0: * unexpected attribute is encountered michael@0: */ michael@0: function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs) michael@0: { michael@0: var accessible = getAccessible(aID, [nsIAccessibleText]); michael@0: if (!accessible) michael@0: return; michael@0: michael@0: var defAttrs = null; michael@0: try{ michael@0: defAttrs = accessible.defaultTextAttributes; michael@0: } catch (e) { michael@0: } michael@0: michael@0: if (!defAttrs) { michael@0: ok(false, "Can't get default text attributes for " + aID); michael@0: return; michael@0: } michael@0: michael@0: var errorMsg = ". Getting default text attributes for " + aID; michael@0: compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs); michael@0: } michael@0: michael@0: /** michael@0: * Test text attributes for wrong offset. michael@0: */ michael@0: function testTextAttrsWrongOffset(aID, aOffset) michael@0: { michael@0: var res = false; michael@0: try { michael@0: var s = {}, e = {}; michael@0: var acc = getAccessible(ID, [nsIAccessibleText]); michael@0: acc.getTextAttributes(false, 157, s, e); michael@0: } catch (e) { michael@0: res = true; michael@0: } michael@0: michael@0: ok(res, michael@0: "text attributes are calculated successfully at wrong offset " + aOffset + " for " + prettyName(aID)); michael@0: } michael@0: michael@0: const kNormalFontWeight = michael@0: function equalsToNormal(aWeight) { return aWeight <= 400 ; } michael@0: michael@0: const kBoldFontWeight = michael@0: function equalsToBold(aWeight) { return aWeight > 400; } michael@0: michael@0: // The pt font size of the input element can vary by Linux distro. michael@0: const kInputFontSize = WIN ? michael@0: "10pt" : (MAC ? "8pt" : function() { return true; }); michael@0: michael@0: const kAbsentFontFamily = michael@0: function(aFontFamily) { return aFontFamily != "sans-serif"; } michael@0: const kInputFontFamily = michael@0: function(aFontFamily) { return aFontFamily != "sans-serif"; } michael@0: michael@0: const kMonospaceFontFamily = michael@0: function(aFontFamily) { return aFontFamily != "monospace"; } michael@0: const kSansSerifFontFamily = michael@0: function(aFontFamily) { return aFontFamily != "sans-serif"; } michael@0: const kSerifFontFamily = michael@0: function(aFontFamily) { return aFontFamily != "serif"; } michael@0: michael@0: const kCursiveFontFamily = LINUX ? "DejaVu Serif" : "Comic Sans MS"; michael@0: michael@0: /** michael@0: * Return used font from the given computed style. michael@0: */ michael@0: function fontFamily(aComputedStyle) michael@0: { michael@0: var name = aComputedStyle.fontFamily; michael@0: switch (name) { michael@0: case "monospace": michael@0: return kMonospaceFontFamily; michael@0: case "sans-serif": michael@0: return kSansSerifFontFamily; michael@0: case "serif": michael@0: return kSerifFontFamily; michael@0: default: michael@0: return name; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Build an object of default text attributes expected for the given accessible. michael@0: * michael@0: * @param aID [in] identifier of accessible michael@0: * @param aFontSize [in] font size michael@0: * @param aFontWeight [in, optional] kBoldFontWeight or kNormalFontWeight, michael@0: * default value is kNormalFontWeight michael@0: */ michael@0: function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily) michael@0: { michael@0: var elm = getNode(aID); michael@0: var computedStyle = document.defaultView.getComputedStyle(elm, ""); michael@0: var bgColor = computedStyle.backgroundColor == "transparent" ? michael@0: "rgb(255, 255, 255)" : computedStyle.backgroundColor; michael@0: michael@0: var defAttrs = { michael@0: "font-style": computedStyle.fontStyle, michael@0: "font-size": aFontSize, michael@0: "background-color": bgColor, michael@0: "font-weight": aFontWeight ? aFontWeight : kNormalFontWeight, michael@0: "color": computedStyle.color, michael@0: "font-family": aFontFamily ? aFontFamily : fontFamily(computedStyle), michael@0: "text-position": computedStyle.verticalAlign michael@0: }; michael@0: michael@0: return defAttrs; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Private. michael@0: michael@0: function getTextAttributes(aID, aAccessible, aIncludeDefAttrs, aOffset, michael@0: aStartOffset, aEndOffset) michael@0: { michael@0: // This function expects the passed in accessible to already be queried for michael@0: // nsIAccessibleText. michael@0: var attrs = null; michael@0: try { michael@0: attrs = aAccessible.getTextAttributes(aIncludeDefAttrs, aOffset, michael@0: aStartOffset, aEndOffset); michael@0: } catch (e) { michael@0: } michael@0: michael@0: if (attrs) michael@0: return attrs; michael@0: michael@0: ok(false, "Can't get text attributes for " + aID); michael@0: return null; michael@0: } michael@0: michael@0: function testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs, michael@0: aAbsentAttrs) michael@0: { michael@0: var accessible = getAccessible(aAccOrElmOrID); michael@0: if (!accessible) michael@0: return; michael@0: michael@0: var attrs = null; michael@0: try { michael@0: attrs = accessible.attributes; michael@0: } catch (e) { } michael@0: michael@0: if (!attrs) { michael@0: ok(false, "Can't get object attributes for " + prettyName(aAccOrElmOrID)); michael@0: return; michael@0: } michael@0: michael@0: var errorMsg = " for " + prettyName(aAccOrElmOrID); michael@0: compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs, aAbsentAttrs); michael@0: } michael@0: michael@0: function compareAttrs(aErrorMsg, aAttrs, aExpectedAttrs, aSkipUnexpectedAttrs, michael@0: aAbsentAttrs) michael@0: { michael@0: // Check if all obtained attributes are expected and have expected value. michael@0: var enumerate = aAttrs.enumerate(); michael@0: while (enumerate.hasMoreElements()) { michael@0: var prop = enumerate.getNext().QueryInterface(nsIPropertyElement); michael@0: michael@0: if (!(prop.key in aExpectedAttrs)) { michael@0: if (!aSkipUnexpectedAttrs) michael@0: ok(false, "Unexpected attribute '" + prop.key + "' having '" + michael@0: prop.value + "'" + aErrorMsg); michael@0: } else { michael@0: var msg = "Attribute '" + prop.key + "' has wrong value" + aErrorMsg; michael@0: var expectedValue = aExpectedAttrs[prop.key]; michael@0: michael@0: if (typeof expectedValue == "function") michael@0: ok(expectedValue(prop.value), msg); michael@0: else michael@0: is(prop.value, expectedValue, msg); michael@0: } michael@0: } michael@0: michael@0: // Check if all expected attributes are presented. michael@0: for (var name in aExpectedAttrs) { michael@0: var value = ""; michael@0: try { michael@0: value = aAttrs.getStringProperty(name); michael@0: } catch(e) { } michael@0: michael@0: if (!value) michael@0: ok(false, michael@0: "There is no expected attribute '" + name + "' " + aErrorMsg); michael@0: } michael@0: michael@0: // Check if all unexpected attributes are absent. michael@0: if (aAbsentAttrs) { michael@0: for (var name in aAbsentAttrs) { michael@0: var wasFound = false; michael@0: michael@0: var enumerate = aAttrs.enumerate(); michael@0: while (enumerate.hasMoreElements()) { michael@0: var prop = enumerate.getNext().QueryInterface(nsIPropertyElement); michael@0: if (prop.key == name) michael@0: wasFound = true; michael@0: } michael@0: } michael@0: michael@0: ok(!wasFound, michael@0: "There is an unexpected attribute '" + name + "' " + aErrorMsg); michael@0: } michael@0: }