summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/layout.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--accessible/tests/mochitest/layout.js390
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),
+ ];
+}