browser_layoutHelpers_getBoxQuads1.js (10685B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 // Tests getAdjustedQuads works properly in a variety of use cases including 5 // iframes, scroll and zoom 6 7 "use strict"; 8 9 const TEST_URI = TEST_URI_ROOT + "doc_layoutHelpers_getBoxQuads1.html"; 10 11 add_task(async function () { 12 const tab = await addTab(TEST_URI); 13 14 info("Running tests"); 15 16 await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { 17 // This function allows the Content Task to easily call `FullZoom` API in 18 // the parent process. 19 function sendCommand(cmd) { 20 return SpecialPowers.spawnChrome([cmd], async data => { 21 const window = this.browsingContext.topChromeWindow; 22 switch (data) { 23 case "zoom-enlarge": 24 window.FullZoom.enlarge(); 25 break; 26 case "zoom-reset": 27 await window.FullZoom.reset(); 28 break; 29 case "zoom-reduce": 30 window.FullZoom.reduce(); 31 break; 32 } 33 }); 34 } 35 36 const doc = content.document; 37 38 const { require } = ChromeUtils.importESModule( 39 "resource://devtools/shared/loader/Loader.sys.mjs" 40 ); 41 const { 42 getAdjustedQuads, 43 } = require("resource://devtools/shared/layout/utils.js"); 44 45 Assert.strictEqual( 46 typeof getAdjustedQuads, 47 "function", 48 "getAdjustedQuads is defined" 49 ); 50 51 returnsTheRightDataStructure(); 52 isEmptyForMissingNode(); 53 isEmptyForHiddenNodes(); 54 defaultsToBorderBoxIfNoneProvided(); 55 returnsLikeGetBoxQuadsInSimpleCase(); 56 takesIframesOffsetsIntoAccount(); 57 takesScrollingIntoAccount(); 58 await takesZoomIntoAccount(); 59 returnsMultipleItemsForWrappingInlineElements(); 60 61 function returnsTheRightDataStructure() { 62 info("Checks that the returned data contains bounds and 4 points"); 63 64 const node = doc.querySelector("body"); 65 const [res] = getAdjustedQuads(doc.defaultView, node, "content"); 66 67 ok("bounds" in res, "The returned data has a bounds property"); 68 ok("p1" in res, "The returned data has a p1 property"); 69 ok("p2" in res, "The returned data has a p2 property"); 70 ok("p3" in res, "The returned data has a p3 property"); 71 ok("p4" in res, "The returned data has a p4 property"); 72 73 for (const boundProp of [ 74 "bottom", 75 "top", 76 "right", 77 "left", 78 "width", 79 "height", 80 "x", 81 "y", 82 ]) { 83 ok( 84 boundProp in res.bounds, 85 "The bounds has a " + boundProp + " property" 86 ); 87 } 88 89 for (const point of ["p1", "p2", "p3", "p4"]) { 90 for (const pointProp of ["x", "y", "z", "w"]) { 91 ok( 92 pointProp in res[point], 93 point + " has a " + pointProp + " property" 94 ); 95 } 96 } 97 } 98 99 function isEmptyForMissingNode() { 100 info("Checks that null is returned for invalid nodes"); 101 102 for (const input of [null, undefined, "", 0]) { 103 is( 104 getAdjustedQuads(doc.defaultView, input).length, 105 0, 106 "A 0-length array is returned for input " + input 107 ); 108 } 109 } 110 111 function isEmptyForHiddenNodes() { 112 info("Checks that null is returned for nodes that aren't rendered"); 113 114 const style = doc.querySelector("#styles"); 115 is( 116 getAdjustedQuads(doc.defaultView, style).length, 117 0, 118 "null is returned for a <style> node" 119 ); 120 121 const hidden = doc.querySelector("#hidden-node"); 122 is( 123 getAdjustedQuads(doc.defaultView, hidden).length, 124 0, 125 "null is returned for a hidden node" 126 ); 127 } 128 129 function defaultsToBorderBoxIfNoneProvided() { 130 info( 131 "Checks that if no boxtype is passed, then border is the default one" 132 ); 133 134 const node = doc.querySelector("#simple-node-with-margin-padding-border"); 135 const [withBoxType] = getAdjustedQuads(doc.defaultView, node, "border"); 136 const [withoutBoxType] = getAdjustedQuads(doc.defaultView, node); 137 138 for (const boundProp of [ 139 "bottom", 140 "top", 141 "right", 142 "left", 143 "width", 144 "height", 145 "x", 146 "y", 147 ]) { 148 is( 149 withBoxType.bounds[boundProp], 150 withoutBoxType.bounds[boundProp], 151 boundProp + " bound is equal with or without the border box type" 152 ); 153 } 154 155 for (const point of ["p1", "p2", "p3", "p4"]) { 156 for (const pointProp of ["x", "y", "z", "w"]) { 157 is( 158 withBoxType[point][pointProp], 159 withoutBoxType[point][pointProp], 160 point + 161 "." + 162 pointProp + 163 " is equal with or without the border box type" 164 ); 165 } 166 } 167 } 168 169 function returnsLikeGetBoxQuadsInSimpleCase() { 170 info( 171 "Checks that for an element in the main frame, without scroll nor zoom" + 172 "that the returned value is similar to the returned value of getBoxQuads" 173 ); 174 175 const node = doc.querySelector("#simple-node-with-margin-padding-border"); 176 177 for (const region of ["content", "padding", "border", "margin"]) { 178 const expected = node.getBoxQuads({ 179 box: region, 180 })[0]; 181 const [actual] = getAdjustedQuads(doc.defaultView, node, region); 182 183 for (const boundProp of [ 184 "bottom", 185 "top", 186 "right", 187 "left", 188 "width", 189 "height", 190 "x", 191 "y", 192 ]) { 193 is( 194 actual.bounds[boundProp], 195 expected.getBounds()[boundProp], 196 boundProp + 197 " bound is equal to the one returned by getBoxQuads for " + 198 region + 199 " box" 200 ); 201 } 202 203 for (const point of ["p1", "p2", "p3", "p4"]) { 204 for (const pointProp of ["x", "y", "z", "w"]) { 205 is( 206 actual[point][pointProp], 207 expected[point][pointProp], 208 point + 209 "." + 210 pointProp + 211 " is equal to the one returned by getBoxQuads for " + 212 region + 213 " box" 214 ); 215 } 216 } 217 } 218 } 219 220 function takesIframesOffsetsIntoAccount() { 221 info( 222 "Checks that the quad returned for a node inside iframes that have " + 223 "margins takes those offsets into account" 224 ); 225 226 const rootIframe = doc.querySelector("iframe"); 227 const subIframe = rootIframe.contentDocument.querySelector("iframe"); 228 const innerNode = subIframe.contentDocument.querySelector("#inner-node"); 229 230 const [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content"); 231 232 // rootIframe margin + subIframe margin + node margin + node border + node padding 233 const p1x = 10 + 10 + 10 + 10 + 10; 234 is(quad.p1.x, p1x, "The inner node's p1 x position is correct"); 235 236 // Same as p1x + the inner node width 237 const p2x = p1x + 100; 238 is(quad.p2.x, p2x, "The inner node's p2 x position is correct"); 239 } 240 241 function takesScrollingIntoAccount() { 242 info( 243 "Checks that the quad returned for a node inside multiple scrolled " + 244 "containers takes the scroll values into account" 245 ); 246 247 // For info, the container being tested here is absolutely positioned at 0 0 248 // to simplify asserting the coordinates 249 250 info("Scroll the container nodes down"); 251 const scrolledNode = doc.querySelector("#scrolled-node"); 252 scrolledNode.scrollTop = 100; 253 const subScrolledNode = doc.querySelector("#sub-scrolled-node"); 254 subScrolledNode.scrollTop = 200; 255 const innerNode = doc.querySelector("#inner-scrolled-node"); 256 257 let [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content"); 258 is( 259 quad.p1.x, 260 0, 261 "p1.x of the scrolled node is correct after scrolling down" 262 ); 263 is( 264 quad.p1.y, 265 -300, 266 "p1.y of the scrolled node is correct after scrolling down" 267 ); 268 269 info("Scrolling back up"); 270 scrolledNode.scrollTop = 0; 271 subScrolledNode.scrollTop = 0; 272 273 [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content"); 274 is( 275 quad.p1.x, 276 0, 277 "p1.x of the scrolled node is correct after scrolling up" 278 ); 279 is( 280 quad.p1.y, 281 0, 282 "p1.y of the scrolled node is correct after scrolling up" 283 ); 284 } 285 286 async function takesZoomIntoAccount() { 287 info( 288 "Checks that if the page is zoomed in/out, the quad returned is correct" 289 ); 290 291 // Hard-coding coordinates in this zoom test is a bad idea as it can vary 292 // depending on the platform, so we simply test that zooming in produces a 293 // bigger quad and zooming out produces a smaller quad 294 295 const node = doc.querySelector("#simple-node-with-margin-padding-border"); 296 const [defaultQuad] = getAdjustedQuads(doc.defaultView, node); 297 298 info("Zoom in"); 299 await sendCommand("zoom-enlarge"); 300 const [zoomedInQuad] = getAdjustedQuads(doc.defaultView, node); 301 302 Assert.greater( 303 zoomedInQuad.bounds.width, 304 defaultQuad.bounds.width, 305 "The zoomed in quad is bigger than the default one" 306 ); 307 Assert.greater( 308 zoomedInQuad.bounds.height, 309 defaultQuad.bounds.height, 310 "The zoomed in quad is bigger than the default one" 311 ); 312 313 info("Zoom out"); 314 await sendCommand("zoom-reset"); 315 await sendCommand("zoom-reduce"); 316 317 const [zoomedOutQuad] = getAdjustedQuads(doc.defaultView, node); 318 319 Assert.less( 320 zoomedOutQuad.bounds.width, 321 defaultQuad.bounds.width, 322 "The zoomed out quad is smaller than the default one" 323 ); 324 Assert.less( 325 zoomedOutQuad.bounds.height, 326 defaultQuad.bounds.height, 327 "The zoomed out quad is smaller than the default one" 328 ); 329 330 await sendCommand("zoom-reset"); 331 } 332 333 function returnsMultipleItemsForWrappingInlineElements() { 334 info( 335 "Checks that several quads are returned " + 336 "for inline elements that span line-breaks" 337 ); 338 339 const node = doc.querySelector("#inline"); 340 const quads = getAdjustedQuads(doc.defaultView, node, "content"); 341 // At least 3 because of the 2 <br />, maybe more depending on the window size. 342 Assert.greaterOrEqual(quads.length, 3, "Multiple quads were returned"); 343 344 is( 345 quads.length, 346 node.getBoxQuads().length, 347 "The same number of boxes as getBoxQuads was returned" 348 ); 349 } 350 }); 351 352 gBrowser.removeCurrentTab(); 353 });