layout.js (9788B)
1 /* import-globals-from common.js */ 2 3 /** 4 * Tests if the given child and grand child accessibles at the given point are 5 * expected. 6 * 7 * @param aID [in] accessible identifier 8 * @param aX [in] x coordinate of the point relative accessible 9 * @param aY [in] y coordinate of the point relative accessible 10 * @param aChildID [in] expected child accessible 11 * @param aGrandChildID [in] expected child accessible 12 */ 13 function testChildAtPoint(aID, aX, aY, aChildID, aGrandChildID) { 14 var child = getChildAtPoint(aID, aX, aY, false); 15 var expectedChild = getAccessible(aChildID); 16 17 var msg = 18 "Wrong direct child accessible at the point (" + 19 aX + 20 ", " + 21 aY + 22 ") of " + 23 prettyName(aID); 24 isObject(child, expectedChild, msg); 25 26 var grandChild = getChildAtPoint(aID, aX, aY, true); 27 var expectedGrandChild = getAccessible(aGrandChildID); 28 29 msg = 30 "Wrong deepest child accessible at the point (" + 31 aX + 32 ", " + 33 aY + 34 ") of " + 35 prettyName(aID); 36 isObject(grandChild, expectedGrandChild, msg); 37 } 38 39 /** 40 * Test if getChildAtPoint returns the given child and grand child accessibles 41 * at coordinates of child accessible (direct and deep hit test). 42 */ 43 function hitTest(aContainerID, aChildID, aGrandChildID) { 44 var container = getAccessible(aContainerID); 45 var child = getAccessible(aChildID); 46 var grandChild = getAccessible(aGrandChildID); 47 48 var [x, y] = getBoundsForDOMElm(child); 49 50 var actualChild = container.getChildAtPoint(x + 1, y + 1); 51 isObject( 52 actualChild, 53 child, 54 "Wrong direct child of " + prettyName(aContainerID) 55 ); 56 57 var actualGrandChild = container.getDeepestChildAtPoint(x + 1, y + 1); 58 isObject( 59 actualGrandChild, 60 grandChild, 61 "Wrong deepest child of " + prettyName(aContainerID) 62 ); 63 } 64 65 /** 66 * Test if getOffsetAtPoint returns the given text offset at given coordinates. 67 */ 68 function testOffsetAtPoint(aHyperTextID, aX, aY, aCoordType, aExpectedOffset) { 69 var hyperText = getAccessible(aHyperTextID, [nsIAccessibleText]); 70 var offset = hyperText.getOffsetAtPoint(aX, aY, aCoordType); 71 is( 72 offset, 73 aExpectedOffset, 74 "Wrong offset at given point (" + 75 aX + 76 ", " + 77 aY + 78 ") for " + 79 prettyName(aHyperTextID) 80 ); 81 } 82 83 /** 84 * Zoom the given document. 85 */ 86 function zoomDocument(aDocument, aZoom) { 87 SpecialPowers.setFullZoom(aDocument.defaultView, aZoom); 88 } 89 90 /** 91 * Set the relative resolution of this document. This is what apz does. 92 * On non-mobile platforms you won't see a visible change. 93 */ 94 function setResolution(aDocument, aZoom) { 95 var windowUtils = aDocument.defaultView.windowUtils; 96 97 windowUtils.setResolutionAndScaleTo(aZoom); 98 } 99 100 /** 101 * Return child accessible at the given point. 102 * 103 * @param aIdentifier [in] accessible identifier 104 * @param aX [in] x coordinate of the point relative accessible 105 * @param aY [in] y coordinate of the point relative accessible 106 * @param aFindDeepestChild [in] points whether deepest or nearest child should 107 * be returned 108 * @return the child accessible at the given point 109 */ 110 function getChildAtPoint(aIdentifier, aX, aY, aFindDeepestChild) { 111 var acc = getAccessible(aIdentifier); 112 if (!acc) { 113 return null; 114 } 115 116 var [screenX, screenY] = getBoundsForDOMElm(acc.DOMNode); 117 118 var x = screenX + aX; 119 var y = screenY + aY; 120 121 try { 122 if (aFindDeepestChild) { 123 return acc.getDeepestChildAtPoint(x, y); 124 } 125 return acc.getChildAtPoint(x, y); 126 } catch (e) {} 127 128 return null; 129 } 130 131 /** 132 * Test the accessible position. 133 */ 134 function testPos(aID, aPoint) { 135 var [expectedX, expectedY] = 136 aPoint != undefined ? aPoint : getBoundsForDOMElm(aID); 137 138 var [x, y] = getBounds(aID); 139 is(x, expectedX, "Wrong x coordinate of " + prettyName(aID)); 140 is(y, expectedY, "Wrong y coordinate of " + prettyName(aID)); 141 } 142 143 /** 144 * Test the accessible boundaries. 145 */ 146 function testBounds(aID, aRect) { 147 var [expectedX, expectedY, expectedWidth, expectedHeight] = 148 aRect != undefined ? aRect : getBoundsForDOMElm(aID); 149 150 var [x, y, width, height] = getBounds(aID); 151 is(x, expectedX, "Wrong x coordinate of " + prettyName(aID)); 152 is(y, expectedY, "Wrong y coordinate of " + prettyName(aID)); 153 is(width, expectedWidth, "Wrong width of " + prettyName(aID)); 154 is(height, expectedHeight, "Wrong height of " + prettyName(aID)); 155 } 156 157 /** 158 * Test text position at the given offset. 159 */ 160 function testTextPos(aID, aOffset, aPoint, aCoordOrigin) { 161 var [expectedX, expectedY] = aPoint; 162 163 var xObj = {}, 164 yObj = {}; 165 var hyperText = getAccessible(aID, [nsIAccessibleText]); 166 hyperText.getCharacterExtents(aOffset, xObj, yObj, {}, {}, aCoordOrigin); 167 is( 168 xObj.value, 169 expectedX, 170 "Wrong x coordinate at offset " + aOffset + " for " + prettyName(aID) 171 ); 172 ok( 173 yObj.value - expectedY <= 2 && expectedY - yObj.value <= 2, 174 "Wrong y coordinate at offset " + 175 aOffset + 176 " for " + 177 prettyName(aID) + 178 " - got " + 179 yObj.value + 180 ", expected " + 181 expectedY + 182 "The difference doesn't exceed 1." 183 ); 184 } 185 186 /** 187 * Test text bounds that is enclosed betwene the given offsets. 188 */ 189 function testTextBounds(aID, aStartOffset, aEndOffset, aRect, aCoordOrigin) { 190 var [expectedX, expectedY, expectedWidth, expectedHeight] = aRect; 191 192 var xObj = {}, 193 yObj = {}, 194 widthObj = {}, 195 heightObj = {}; 196 var hyperText = getAccessible(aID, [nsIAccessibleText]); 197 hyperText.getRangeExtents( 198 aStartOffset, 199 aEndOffset, 200 xObj, 201 yObj, 202 widthObj, 203 heightObj, 204 aCoordOrigin 205 ); 206 207 // x 208 isWithin( 209 expectedX, 210 xObj.value, 211 1, 212 "Wrong x coordinate of text between offsets (" + 213 aStartOffset + 214 ", " + 215 aEndOffset + 216 ") for " + 217 prettyName(aID) 218 ); 219 220 // y 221 isWithin( 222 expectedY, 223 yObj.value, 224 1, 225 `y coord of text between offsets (${aStartOffset}, ${aEndOffset}) ` + 226 `for ${prettyName(aID)}` 227 ); 228 229 // Width 230 var msg = 231 "Wrong width of text between offsets (" + 232 aStartOffset + 233 ", " + 234 aEndOffset + 235 ") for " + 236 prettyName(aID) + 237 " - Got " + 238 widthObj.value + 239 " Expected " + 240 expectedWidth; 241 if (!WIN) { 242 isWithin(expectedWidth, widthObj.value, 1, msg); 243 } else { 244 // fails on some windows machines 245 todo(false, msg); 246 } 247 248 // Height 249 isWithin( 250 expectedHeight, 251 heightObj.value, 252 1, 253 `height of text between offsets (${aStartOffset}, ${aEndOffset}) ` + 254 `for ${prettyName(aID)}` 255 ); 256 } 257 258 /** 259 * Return the accessible coordinates relative to the screen in device pixels. 260 */ 261 function getPos(aID) { 262 var accessible = getAccessible(aID); 263 var x = {}, 264 y = {}; 265 accessible.getBounds(x, y, {}, {}); 266 return [x.value, y.value]; 267 } 268 269 /** 270 * Return the accessible coordinates and size relative to the screen in device 271 * pixels. This methods also retrieves coordinates in CSS pixels and ensures that they 272 * match Dev pixels with a given device pixel ratio. 273 */ 274 function getBounds(aID, aDPR = window.devicePixelRatio) { 275 const accessible = getAccessible(aID); 276 let x = {}, 277 y = {}, 278 width = {}, 279 height = {}; 280 let xInCSS = {}, 281 yInCSS = {}, 282 widthInCSS = {}, 283 heightInCSS = {}; 284 accessible.getBounds(x, y, width, height); 285 accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS); 286 287 info(`DPR is: ${aDPR}`); 288 isWithin( 289 xInCSS.value, 290 x.value / aDPR, 291 1, 292 "X in CSS pixels is calculated correctly" 293 ); 294 isWithin( 295 yInCSS.value, 296 y.value / aDPR, 297 1, 298 "Y in CSS pixels is calculated correctly" 299 ); 300 isWithin( 301 widthInCSS.value, 302 width.value / aDPR, 303 1, 304 "Width in CSS pixels is calculated correctly" 305 ); 306 isWithin( 307 heightInCSS.value, 308 height.value / aDPR, 309 1, 310 "Height in CSS pixels is calculated correctly" 311 ); 312 313 return [x.value, y.value, width.value, height.value]; 314 } 315 316 function getRangeExtents(aID, aStartOffset, aEndOffset, aCoordOrigin) { 317 var hyperText = getAccessible(aID, [nsIAccessibleText]); 318 var x = {}, 319 y = {}, 320 width = {}, 321 height = {}; 322 hyperText.getRangeExtents( 323 aStartOffset, 324 aEndOffset, 325 x, 326 y, 327 width, 328 height, 329 aCoordOrigin 330 ); 331 return [x.value, y.value, width.value, height.value]; 332 } 333 334 /** 335 * Return DOM node coordinates relative the screen and its size in device 336 * pixels. 337 */ 338 function getBoundsForDOMElm(aID) { 339 var x = 0, 340 y = 0, 341 width = 0, 342 height = 0; 343 344 var elm = getNode(aID); 345 if (elm.localName == "area") { 346 var mapName = elm.parentNode.getAttribute("name"); 347 var selector = "[usemap='#" + mapName + "']"; 348 var img = elm.ownerDocument.querySelector(selector); 349 350 var areaCoords = elm.coords.split(","); 351 var areaX = parseInt(areaCoords[0]); 352 var areaY = parseInt(areaCoords[1]); 353 var areaWidth = parseInt(areaCoords[2]) - areaX; 354 var areaHeight = parseInt(areaCoords[3]) - areaY; 355 356 let rect = img.getBoundingClientRect(); 357 x = rect.left + areaX; 358 y = rect.top + areaY; 359 width = areaWidth; 360 height = areaHeight; 361 } else { 362 let rect = elm.getBoundingClientRect(); 363 x = rect.left; 364 y = rect.top; 365 width = rect.width; 366 height = rect.height; 367 } 368 369 var elmWindow = elm.ownerGlobal; 370 return CSSToDevicePixels( 371 elmWindow, 372 x + elmWindow.mozInnerScreenX, 373 y + elmWindow.mozInnerScreenY, 374 width, 375 height 376 ); 377 } 378 379 function CSSToDevicePixels(aWindow, aX, aY, aWidth, aHeight) { 380 var ratio = aWindow.devicePixelRatio; 381 382 // CSS pixels and ratio can be not integer. Device pixels are always integer. 383 // Do our best and hope it works. 384 return [ 385 Math.round(aX * ratio), 386 Math.round(aY * ratio), 387 Math.round(aWidth * ratio), 388 Math.round(aHeight * ratio), 389 ]; 390 }