diff options
Diffstat (limited to '')
-rw-r--r-- | accessible/tests/mochitest/layout.js | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/layout.js b/accessible/tests/mochitest/layout.js new file mode 100644 index 0000000000..1467d5fe7f --- /dev/null +++ b/accessible/tests/mochitest/layout.js @@ -0,0 +1,390 @@ +/* import-globals-from common.js */ + +/** + * Tests if the given child and grand child accessibles at the given point are + * expected. + * + * @param aID [in] accessible identifier + * @param aX [in] x coordinate of the point relative accessible + * @param aY [in] y coordinate of the point relative accessible + * @param aChildID [in] expected child accessible + * @param aGrandChildID [in] expected child accessible + */ +function testChildAtPoint(aID, aX, aY, aChildID, aGrandChildID) { + var child = getChildAtPoint(aID, aX, aY, false); + var expectedChild = getAccessible(aChildID); + + var msg = + "Wrong direct child accessible at the point (" + + aX + + ", " + + aY + + ") of " + + prettyName(aID); + isObject(child, expectedChild, msg); + + var grandChild = getChildAtPoint(aID, aX, aY, true); + var expectedGrandChild = getAccessible(aGrandChildID); + + msg = + "Wrong deepest child accessible at the point (" + + aX + + ", " + + aY + + ") of " + + prettyName(aID); + isObject(grandChild, expectedGrandChild, msg); +} + +/** + * Test if getChildAtPoint returns the given child and grand child accessibles + * at coordinates of child accessible (direct and deep hit test). + */ +function hitTest(aContainerID, aChildID, aGrandChildID) { + var container = getAccessible(aContainerID); + var child = getAccessible(aChildID); + var grandChild = getAccessible(aGrandChildID); + + var [x, y] = getBoundsForDOMElm(child); + + var actualChild = container.getChildAtPoint(x + 1, y + 1); + isObject( + actualChild, + child, + "Wrong direct child of " + prettyName(aContainerID) + ); + + var actualGrandChild = container.getDeepestChildAtPoint(x + 1, y + 1); + isObject( + actualGrandChild, + grandChild, + "Wrong deepest child of " + prettyName(aContainerID) + ); +} + +/** + * Test if getOffsetAtPoint returns the given text offset at given coordinates. + */ +function testOffsetAtPoint(aHyperTextID, aX, aY, aCoordType, aExpectedOffset) { + var hyperText = getAccessible(aHyperTextID, [nsIAccessibleText]); + var offset = hyperText.getOffsetAtPoint(aX, aY, aCoordType); + is( + offset, + aExpectedOffset, + "Wrong offset at given point (" + + aX + + ", " + + aY + + ") for " + + prettyName(aHyperTextID) + ); +} + +/** + * Zoom the given document. + */ +function zoomDocument(aDocument, aZoom) { + SpecialPowers.setFullZoom(aDocument.defaultView, aZoom); +} + +/** + * Set the relative resolution of this document. This is what apz does. + * On non-mobile platforms you won't see a visible change. + */ +function setResolution(aDocument, aZoom) { + var windowUtils = aDocument.defaultView.windowUtils; + + windowUtils.setResolutionAndScaleTo(aZoom); +} + +/** + * Return child accessible at the given point. + * + * @param aIdentifier [in] accessible identifier + * @param aX [in] x coordinate of the point relative accessible + * @param aY [in] y coordinate of the point relative accessible + * @param aFindDeepestChild [in] points whether deepest or nearest child should + * be returned + * @return the child accessible at the given point + */ +function getChildAtPoint(aIdentifier, aX, aY, aFindDeepestChild) { + var acc = getAccessible(aIdentifier); + if (!acc) { + return null; + } + + var [screenX, screenY] = getBoundsForDOMElm(acc.DOMNode); + + var x = screenX + aX; + var y = screenY + aY; + + try { + if (aFindDeepestChild) { + return acc.getDeepestChildAtPoint(x, y); + } + return acc.getChildAtPoint(x, y); + } catch (e) {} + + return null; +} + +/** + * Test the accessible position. + */ +function testPos(aID, aPoint) { + var [expectedX, expectedY] = + aPoint != undefined ? aPoint : getBoundsForDOMElm(aID); + + var [x, y] = getBounds(aID); + is(x, expectedX, "Wrong x coordinate of " + prettyName(aID)); + is(y, expectedY, "Wrong y coordinate of " + prettyName(aID)); +} + +/** + * Test the accessible boundaries. + */ +function testBounds(aID, aRect) { + var [expectedX, expectedY, expectedWidth, expectedHeight] = + aRect != undefined ? aRect : getBoundsForDOMElm(aID); + + var [x, y, width, height] = getBounds(aID); + is(x, expectedX, "Wrong x coordinate of " + prettyName(aID)); + is(y, expectedY, "Wrong y coordinate of " + prettyName(aID)); + is(width, expectedWidth, "Wrong width of " + prettyName(aID)); + is(height, expectedHeight, "Wrong height of " + prettyName(aID)); +} + +/** + * Test text position at the given offset. + */ +function testTextPos(aID, aOffset, aPoint, aCoordOrigin) { + var [expectedX, expectedY] = aPoint; + + var xObj = {}, + yObj = {}; + var hyperText = getAccessible(aID, [nsIAccessibleText]); + hyperText.getCharacterExtents(aOffset, xObj, yObj, {}, {}, aCoordOrigin); + is( + xObj.value, + expectedX, + "Wrong x coordinate at offset " + aOffset + " for " + prettyName(aID) + ); + ok( + yObj.value - expectedY <= 2 && expectedY - yObj.value <= 2, + "Wrong y coordinate at offset " + + aOffset + + " for " + + prettyName(aID) + + " - got " + + yObj.value + + ", expected " + + expectedY + + "The difference doesn't exceed 1." + ); +} + +/** + * Test text bounds that is enclosed betwene the given offsets. + */ +function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin) { + var [expectedX, expectedY, expectedWidth, expectedHeight] = aRect; + + var xObj = {}, + yObj = {}, + widthObj = {}, + heightObj = {}; + var hyperText = getAccessible(aID, [nsIAccessibleText]); + hyperText.getRangeExtents( + aStartOffset, + aEndOffset, + xObj, + yObj, + widthObj, + heightObj, + aCoordOrigin + ); + + // x + isWithin( + expectedX, + xObj.value, + 1, + "Wrong x coordinate of text between offsets (" + + aStartOffset + + ", " + + aEndOffset + + ") for " + + prettyName(aID) + ); + + // y + isWithin( + expectedY, + yObj.value, + 1, + `y coord of text between offsets (${aStartOffset}, ${aEndOffset}) ` + + `for ${prettyName(aID)}` + ); + + // Width + var msg = + "Wrong width of text between offsets (" + + aStartOffset + + ", " + + aEndOffset + + ") for " + + prettyName(aID) + + " - Got " + + widthObj.value + + " Expected " + + expectedWidth; + if (!WIN) { + isWithin(expectedWidth, widthObj.value, 1, msg); + } else { + // fails on some windows machines + todo(false, msg); + } + + // Height + isWithin( + expectedHeight, + heightObj.value, + 1, + `height of text between offsets (${aStartOffset}, ${aEndOffset}) ` + + `for ${prettyName(aID)}` + ); +} + +/** + * Return the accessible coordinates relative to the screen in device pixels. + */ +function getPos(aID) { + var accessible = getAccessible(aID); + var x = {}, + y = {}; + accessible.getBounds(x, y, {}, {}); + return [x.value, y.value]; +} + +/** + * Return the accessible coordinates and size relative to the screen in device + * pixels. This methods also retrieves coordinates in CSS pixels and ensures that they + * match Dev pixels with a given device pixel ratio. + */ +function getBounds(aID, aDPR = window.devicePixelRatio) { + const accessible = getAccessible(aID); + let x = {}, + y = {}, + width = {}, + height = {}; + let xInCSS = {}, + yInCSS = {}, + widthInCSS = {}, + heightInCSS = {}; + accessible.getBounds(x, y, width, height); + accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS); + + info(`DPR is: ${aDPR}`); + isWithin( + xInCSS.value, + x.value / aDPR, + 1, + "X in CSS pixels is calculated correctly" + ); + isWithin( + yInCSS.value, + y.value / aDPR, + 1, + "Y in CSS pixels is calculated correctly" + ); + isWithin( + widthInCSS.value, + width.value / aDPR, + 1, + "Width in CSS pixels is calculated correctly" + ); + isWithin( + heightInCSS.value, + height.value / aDPR, + 1, + "Height in CSS pixels is calculated correctly" + ); + + return [x.value, y.value, width.value, height.value]; +} + +function getRangeExtents(aID, aStartOffset, aEndOffset, aCoordOrigin) { + var hyperText = getAccessible(aID, [nsIAccessibleText]); + var x = {}, + y = {}, + width = {}, + height = {}; + hyperText.getRangeExtents( + aStartOffset, + aEndOffset, + x, + y, + width, + height, + aCoordOrigin + ); + return [x.value, y.value, width.value, height.value]; +} + +/** + * Return DOM node coordinates relative the screen and its size in device + * pixels. + */ +function getBoundsForDOMElm(aID) { + var x = 0, + y = 0, + width = 0, + height = 0; + + var elm = getNode(aID); + if (elm.localName == "area") { + var mapName = elm.parentNode.getAttribute("name"); + var selector = "[usemap='#" + mapName + "']"; + var img = elm.ownerDocument.querySelector(selector); + + var areaCoords = elm.coords.split(","); + var areaX = parseInt(areaCoords[0]); + var areaY = parseInt(areaCoords[1]); + var areaWidth = parseInt(areaCoords[2]) - areaX; + var areaHeight = parseInt(areaCoords[3]) - areaY; + + let rect = img.getBoundingClientRect(); + x = rect.left + areaX; + y = rect.top + areaY; + width = areaWidth; + height = areaHeight; + } else { + let rect = elm.getBoundingClientRect(); + x = rect.left; + y = rect.top; + width = rect.width; + height = rect.height; + } + + var elmWindow = elm.ownerGlobal; + return CSSToDevicePixels( + elmWindow, + x + elmWindow.mozInnerScreenX, + y + elmWindow.mozInnerScreenY, + width, + height + ); +} + +function CSSToDevicePixels(aWindow, aX, aY, aWidth, aHeight) { + var ratio = aWindow.devicePixelRatio; + + // CSS pixels and ratio can be not integer. Device pixels are always integer. + // Do our best and hope it works. + return [ + Math.round(aX * ratio), + Math.round(aY * ratio), + Math.round(aWidth * ratio), + Math.round(aHeight * ratio), + ]; +} |