513 lines
13 KiB
JavaScript
513 lines
13 KiB
JavaScript
/* import-globals-from common.js */
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// Object attributes.
|
|
|
|
/**
|
|
* Test object attributes.
|
|
*
|
|
* @param aAccOrElmOrID [in] the accessible identifier
|
|
* @param aAttrs [in] the map of expected object attributes
|
|
* (name/value pairs)
|
|
* @param aSkipUnexpectedAttrs [in] points this function doesn't fail if
|
|
* unexpected attribute is encountered
|
|
* @param aTodo [in] true if this is a 'todo'
|
|
*/
|
|
function testAttrs(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs, aTodo) {
|
|
testAttrsInternal(aAccOrElmOrID, aAttrs, aSkipUnexpectedAttrs, null, aTodo);
|
|
}
|
|
|
|
/**
|
|
* Test object attributes that must not be present.
|
|
*
|
|
* @param aAccOrElmOrID [in] the accessible identifier
|
|
* @param aAbsentAttrs [in] map of attributes that should not be
|
|
* present (name/value pairs)
|
|
* @param aTodo [in] true if this is a 'todo'
|
|
*/
|
|
function testAbsentAttrs(aAccOrElmOrID, aAbsentAttrs, aTodo) {
|
|
testAttrsInternal(aAccOrElmOrID, {}, true, aAbsentAttrs, aTodo);
|
|
}
|
|
|
|
/**
|
|
* Test object attributes that aren't right, but should be (todo)
|
|
*
|
|
* @param aAccOrElmOrID [in] the accessible identifier
|
|
* @param aKey [in] attribute name
|
|
* @param aExpectedValue [in] expected attribute value
|
|
*/
|
|
function todoAttr(aAccOrElmOrID, aKey, aExpectedValue) {
|
|
testAttrs(
|
|
aAccOrElmOrID,
|
|
Object.fromEntries([[aKey, aExpectedValue]]),
|
|
true,
|
|
true
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Test CSS based object attributes.
|
|
*/
|
|
function testCSSAttrs(aID) {
|
|
var node = document.getElementById(aID);
|
|
var computedStyle = document.defaultView.getComputedStyle(node);
|
|
|
|
var attrs = {
|
|
display: computedStyle.display,
|
|
"text-align": computedStyle.textAlign,
|
|
"text-indent": computedStyle.textIndent,
|
|
"margin-left": computedStyle.marginLeft,
|
|
"margin-right": computedStyle.marginRight,
|
|
"margin-top": computedStyle.marginTop,
|
|
"margin-bottom": computedStyle.marginBottom,
|
|
};
|
|
testAttrs(aID, attrs, true);
|
|
}
|
|
|
|
/**
|
|
* Test the accessible that it doesn't have CSS-based object attributes.
|
|
*/
|
|
function testAbsentCSSAttrs(aID) {
|
|
var attrs = {
|
|
display: "",
|
|
"text-align": "",
|
|
"text-indent": "",
|
|
"margin-left": "",
|
|
"margin-right": "",
|
|
"margin-top": "",
|
|
"margin-bottom": "",
|
|
};
|
|
testAbsentAttrs(aID, attrs);
|
|
}
|
|
|
|
/**
|
|
* Test group object attributes (posinset, setsize and level) and
|
|
* nsIAccessible::groupPosition() method.
|
|
*
|
|
* @param aAccOrElmOrID [in] the ID, DOM node or accessible
|
|
* @param aPosInSet [in] the value of 'posinset' attribute
|
|
* @param aSetSize [in] the value of 'setsize' attribute
|
|
* @param aLevel [in, optional] the value of 'level' attribute
|
|
*/
|
|
function testGroupAttrs(aAccOrElmOrID, aPosInSet, aSetSize, aLevel, aTodo) {
|
|
var acc = getAccessible(aAccOrElmOrID);
|
|
var levelObj = {},
|
|
posInSetObj = {},
|
|
setSizeObj = {};
|
|
acc.groupPosition(levelObj, setSizeObj, posInSetObj);
|
|
|
|
let groupPos = {},
|
|
expectedGroupPos = {};
|
|
|
|
if (aPosInSet && aSetSize) {
|
|
groupPos.setsize = String(setSizeObj.value);
|
|
groupPos.posinset = String(posInSetObj.value);
|
|
|
|
expectedGroupPos.setsize = String(aSetSize);
|
|
expectedGroupPos.posinset = String(aPosInSet);
|
|
}
|
|
|
|
if (aLevel) {
|
|
groupPos.level = String(levelObj.value);
|
|
|
|
expectedGroupPos.level = String(aLevel);
|
|
}
|
|
|
|
compareSimpleObjects(
|
|
groupPos,
|
|
expectedGroupPos,
|
|
false,
|
|
"wrong groupPos",
|
|
aTodo
|
|
);
|
|
|
|
testAttrs(aAccOrElmOrID, expectedGroupPos, true, aTodo);
|
|
}
|
|
|
|
function testGroupParentAttrs(
|
|
aAccOrElmOrID,
|
|
aChildItemCount,
|
|
aIsHierarchical,
|
|
aTodo
|
|
) {
|
|
testAttrs(
|
|
aAccOrElmOrID,
|
|
{ "child-item-count": String(aChildItemCount) },
|
|
true,
|
|
aTodo
|
|
);
|
|
|
|
if (aIsHierarchical) {
|
|
testAttrs(aAccOrElmOrID, { tree: "true" }, true, aTodo);
|
|
} else {
|
|
testAbsentAttrs(aAccOrElmOrID, { tree: "true" });
|
|
}
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// Text attributes.
|
|
|
|
/**
|
|
* Test text attributes.
|
|
*
|
|
* @param aID [in] the ID of DOM element having text
|
|
* accessible
|
|
* @param aOffset [in] the offset inside text accessible to fetch
|
|
* text attributes
|
|
* @param aAttrs [in] the map of expected text attributes
|
|
* (name/value pairs) exposed at the offset
|
|
* @param aDefAttrs [in] the map of expected text attributes
|
|
* (name/value pairs) exposed on hyper text
|
|
* accessible
|
|
* @param aStartOffset [in] expected start offset where text attributes
|
|
* are applied
|
|
* @param aEndOffset [in] expected end offset where text attribute
|
|
* are applied
|
|
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
|
|
* unexpected attribute is encountered
|
|
*/
|
|
function testTextAttrs(
|
|
aID,
|
|
aOffset,
|
|
aAttrs,
|
|
aDefAttrs,
|
|
aStartOffset,
|
|
aEndOffset,
|
|
aSkipUnexpectedAttrs
|
|
) {
|
|
var accessible = getAccessible(aID, [nsIAccessibleText]);
|
|
if (!accessible) {
|
|
return;
|
|
}
|
|
|
|
var startOffset = { value: -1 };
|
|
var endOffset = { value: -1 };
|
|
|
|
// do not include attributes exposed on hyper text accessible
|
|
var attrs = getTextAttributes(
|
|
aID,
|
|
accessible,
|
|
false,
|
|
aOffset,
|
|
startOffset,
|
|
endOffset
|
|
);
|
|
|
|
if (!attrs) {
|
|
return;
|
|
}
|
|
|
|
var errorMsg = " for " + aID + " at offset " + aOffset;
|
|
|
|
is(startOffset.value, aStartOffset, "Wrong start offset" + errorMsg);
|
|
is(endOffset.value, aEndOffset, "Wrong end offset" + errorMsg);
|
|
|
|
compareAttrs(errorMsg, attrs, aAttrs, aSkipUnexpectedAttrs);
|
|
|
|
// include attributes exposed on hyper text accessible
|
|
var expectedAttrs = {};
|
|
for (let name in aAttrs) {
|
|
expectedAttrs[name] = aAttrs[name];
|
|
}
|
|
|
|
for (let name in aDefAttrs) {
|
|
if (!(name in expectedAttrs)) {
|
|
expectedAttrs[name] = aDefAttrs[name];
|
|
}
|
|
}
|
|
|
|
attrs = getTextAttributes(
|
|
aID,
|
|
accessible,
|
|
true,
|
|
aOffset,
|
|
startOffset,
|
|
endOffset
|
|
);
|
|
|
|
if (!attrs) {
|
|
return;
|
|
}
|
|
|
|
compareAttrs(errorMsg, attrs, expectedAttrs, aSkipUnexpectedAttrs);
|
|
}
|
|
|
|
/**
|
|
* Test default text attributes.
|
|
*
|
|
* @param aID [in] the ID of DOM element having text
|
|
* accessible
|
|
* @param aDefAttrs [in] the map of expected text attributes
|
|
* (name/value pairs)
|
|
* @param aSkipUnexpectedAttrs [in] points the function doesn't fail if
|
|
* unexpected attribute is encountered
|
|
*/
|
|
function testDefaultTextAttrs(aID, aDefAttrs, aSkipUnexpectedAttrs) {
|
|
var accessible = getAccessible(aID, [nsIAccessibleText]);
|
|
if (!accessible) {
|
|
return;
|
|
}
|
|
|
|
var defAttrs = null;
|
|
try {
|
|
defAttrs = accessible.defaultTextAttributes;
|
|
} catch (e) {}
|
|
|
|
if (!defAttrs) {
|
|
ok(false, "Can't get default text attributes for " + aID);
|
|
return;
|
|
}
|
|
|
|
var errorMsg = ". Getting default text attributes for " + aID;
|
|
compareAttrs(errorMsg, defAttrs, aDefAttrs, aSkipUnexpectedAttrs);
|
|
}
|
|
|
|
/**
|
|
* Test text attributes for wrong offset.
|
|
*/
|
|
function testTextAttrsWrongOffset(aID, aOffset) {
|
|
var res = false;
|
|
try {
|
|
var s = {},
|
|
e = {};
|
|
// Bug 1602031
|
|
// eslint-disable-next-line no-undef
|
|
var acc = getAccessible(ID, [nsIAccessibleText]);
|
|
acc.getTextAttributes(false, 157, s, e);
|
|
} catch (ex) {
|
|
res = true;
|
|
}
|
|
|
|
ok(
|
|
res,
|
|
"text attributes are calculated successfully at wrong offset " +
|
|
aOffset +
|
|
" for " +
|
|
prettyName(aID)
|
|
);
|
|
}
|
|
|
|
const kNormalFontWeight = function equalsToNormal(aWeight) {
|
|
return aWeight <= 400;
|
|
};
|
|
|
|
const kBoldFontWeight = function equalsToBold(aWeight) {
|
|
return aWeight > 400;
|
|
};
|
|
|
|
// The pt font size of the input element can vary by Linux distro.
|
|
const kInputFontSize =
|
|
WIN || MAC
|
|
? "10pt"
|
|
: function () {
|
|
return true;
|
|
};
|
|
|
|
const kAbsentFontFamily = function (aFontFamily) {
|
|
return aFontFamily != "sans-serif";
|
|
};
|
|
const kInputFontFamily = function (aFontFamily) {
|
|
return aFontFamily != "sans-serif";
|
|
};
|
|
|
|
const kMonospaceFontFamily = function (aFontFamily) {
|
|
return aFontFamily != "monospace";
|
|
};
|
|
const kSansSerifFontFamily = function (aFontFamily) {
|
|
return aFontFamily != "sans-serif";
|
|
};
|
|
const kSerifFontFamily = function (aFontFamily) {
|
|
return aFontFamily != "serif";
|
|
};
|
|
|
|
const kCursiveFontFamily = LINUX ? "DejaVu Serif" : "Comic Sans MS";
|
|
|
|
/**
|
|
* Return used font from the given computed style.
|
|
*/
|
|
function fontFamily(aComputedStyle) {
|
|
var name = aComputedStyle.fontFamily;
|
|
switch (name) {
|
|
case "monospace":
|
|
return kMonospaceFontFamily;
|
|
case "sans-serif":
|
|
return kSansSerifFontFamily;
|
|
case "serif":
|
|
return kSerifFontFamily;
|
|
default:
|
|
return name;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a computed system color for this document.
|
|
*/
|
|
function getSystemColor(aColor) {
|
|
let { r, g, b, a } = InspectorUtils.colorToRGBA(aColor, document);
|
|
return a == 1 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
}
|
|
|
|
/**
|
|
* Build an object of default text attributes expected for the given accessible.
|
|
*
|
|
* @param aID [in] identifier of accessible
|
|
* @param aFontSize [in] font size
|
|
* @param aFontWeight [in, optional] kBoldFontWeight or kNormalFontWeight,
|
|
* default value is kNormalFontWeight
|
|
*/
|
|
function buildDefaultTextAttrs(aID, aFontSize, aFontWeight, aFontFamily) {
|
|
var elm = getNode(aID);
|
|
var computedStyle = document.defaultView.getComputedStyle(elm);
|
|
var bgColor =
|
|
computedStyle.backgroundColor == "rgba(0, 0, 0, 0)"
|
|
? getSystemColor("Canvas")
|
|
: computedStyle.backgroundColor;
|
|
|
|
var defAttrs = {
|
|
"font-style": computedStyle.fontStyle,
|
|
"font-size": aFontSize,
|
|
"background-color": bgColor,
|
|
"font-weight": aFontWeight ? aFontWeight : kNormalFontWeight,
|
|
color: computedStyle.color,
|
|
"font-family": aFontFamily ? aFontFamily : fontFamily(computedStyle),
|
|
"text-position": computedStyle.verticalAlign,
|
|
};
|
|
|
|
return defAttrs;
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////////////////////////
|
|
// Private.
|
|
|
|
function getTextAttributes(
|
|
aID,
|
|
aAccessible,
|
|
aIncludeDefAttrs,
|
|
aOffset,
|
|
aStartOffset,
|
|
aEndOffset
|
|
) {
|
|
// This function expects the passed in accessible to already be queried for
|
|
// nsIAccessibleText.
|
|
var attrs = null;
|
|
try {
|
|
attrs = aAccessible.getTextAttributes(
|
|
aIncludeDefAttrs,
|
|
aOffset,
|
|
aStartOffset,
|
|
aEndOffset
|
|
);
|
|
} catch (e) {}
|
|
|
|
if (attrs) {
|
|
return attrs;
|
|
}
|
|
|
|
ok(false, "Can't get text attributes for " + aID);
|
|
return null;
|
|
}
|
|
|
|
function testAttrsInternal(
|
|
aAccOrElmOrID,
|
|
aAttrs,
|
|
aSkipUnexpectedAttrs,
|
|
aAbsentAttrs,
|
|
aTodo
|
|
) {
|
|
var accessible = getAccessible(aAccOrElmOrID);
|
|
if (!accessible) {
|
|
return;
|
|
}
|
|
|
|
var attrs = null;
|
|
try {
|
|
attrs = accessible.attributes;
|
|
} catch (e) {}
|
|
|
|
if (!attrs) {
|
|
ok(false, "Can't get object attributes for " + prettyName(aAccOrElmOrID));
|
|
return;
|
|
}
|
|
|
|
var errorMsg = " for " + prettyName(aAccOrElmOrID);
|
|
compareAttrs(
|
|
errorMsg,
|
|
attrs,
|
|
aAttrs,
|
|
aSkipUnexpectedAttrs,
|
|
aAbsentAttrs,
|
|
aTodo
|
|
);
|
|
}
|
|
|
|
function compareAttrs(
|
|
aErrorMsg,
|
|
aAttrs,
|
|
aExpectedAttrs,
|
|
aSkipUnexpectedAttrs,
|
|
aAbsentAttrs,
|
|
aTodo
|
|
) {
|
|
// Check if all obtained attributes are expected and have expected value.
|
|
let attrObject = {};
|
|
for (let prop of aAttrs.enumerate()) {
|
|
attrObject[prop.key] = prop.value;
|
|
}
|
|
|
|
// Create expected attributes set by using the return values from
|
|
// embedded functions to determine the entry's value.
|
|
let expectedObj = Object.fromEntries(
|
|
Object.entries(aExpectedAttrs).map(([k, v]) => {
|
|
if (v instanceof Function) {
|
|
// If value is a function that returns true given the received
|
|
// attribute value, assign the attribute value to the entry.
|
|
// If it is false, stringify the function for good error reporting.
|
|
let value = v(attrObject[k]) ? attrObject[k] : v.toString();
|
|
return [k, value];
|
|
}
|
|
|
|
return [k, v];
|
|
})
|
|
);
|
|
|
|
compareSimpleObjects(
|
|
attrObject,
|
|
expectedObj,
|
|
aSkipUnexpectedAttrs,
|
|
aErrorMsg,
|
|
aTodo
|
|
);
|
|
|
|
// Check if all unexpected attributes are absent.
|
|
if (aAbsentAttrs) {
|
|
let presentAttrs = Object.keys(attrObject).filter(
|
|
k => aAbsentAttrs[k] !== undefined
|
|
);
|
|
if (presentAttrs.length) {
|
|
(aTodo ? todo : ok)(
|
|
false,
|
|
`There were unexpected attributes: ${presentAttrs}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function compareSimpleObjects(
|
|
aObj,
|
|
aExpectedObj,
|
|
aSkipUnexpectedAttrs,
|
|
aMessage,
|
|
aTodo
|
|
) {
|
|
let keys = aSkipUnexpectedAttrs
|
|
? Object.keys(aExpectedObj).sort()
|
|
: Object.keys(aObj).sort();
|
|
let o1 = JSON.stringify(aObj, keys);
|
|
let o2 = JSON.stringify(aExpectedObj, keys);
|
|
|
|
if (aTodo) {
|
|
todo_is(o1, o2, `${aMessage} - Got ${o1}, expected ${o2}`);
|
|
} else {
|
|
is(o1, o2, aMessage);
|
|
}
|
|
}
|