helper_interrupted_reflow.html (22194B)
1 <!DOCTYPE html> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=1292781 5 --> 6 <head> 7 <title>Test for bug 1292781</title> 8 <script src="/tests/SimpleTest/EventUtils.js"></script> 9 <script src="/tests/SimpleTest/paint_listener.js"></script> 10 <script type="application/javascript" src="apz_test_utils.js"></script> 11 <script type="application/javascript" src="apz_test_native_event_utils.js"></script> 12 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 13 <style> 14 .outer { 15 height: 400px; 16 width: 415px; 17 overflow: hidden; 18 position: relative; 19 } 20 .inner { 21 height: 100%; 22 outline: none; 23 overflow-x: hidden; 24 overflow-y: scroll; 25 position: relative; 26 } 27 .inner div:nth-child(even) { 28 background-color: lightblue; 29 } 30 .inner div:nth-child(odd) { 31 background-color: lightgreen; 32 } 33 .outer.contentBefore::before { 34 top: 0; 35 content: ''; 36 display: block; 37 height: 2px; 38 position: absolute; 39 width: 100%; 40 z-index: 99; 41 } 42 </style> 43 </head> 44 <body> 45 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292781">Mozilla Bug 1292781</a> 46 <p id="display"></p> 47 <div id="content"> 48 <p>The frame reconstruction should not leave this scrollframe in a bad state</p> 49 <div class="outer"> 50 <div class="inner"> 51 this is the top of the scrollframe. 52 <div>this is a box</div> 53 <div>this is a box</div> 54 <div>this is a box</div> 55 <div>this is a box</div> 56 this is near the top of the scrollframe. 57 <div>this is a box</div> 58 <div>this is a box</div> 59 <div>this is a box</div> 60 <div>this is a box</div> 61 <div>this is a box</div> 62 <div>this is a box</div> 63 <div>this is a box</div> 64 <div>this is a box</div> 65 <div>this is a box</div> 66 <div>this is a box</div> 67 <div>this is a box</div> 68 <div>this is a box</div> 69 <div>this is a box</div> 70 <div>this is a box</div> 71 <div>this is a box</div> 72 <div>this is a box</div> 73 <div>this is a box</div> 74 <div>this is a box</div> 75 <div>this is a box</div> 76 <div>this is a box</div> 77 <div>this is a box</div> 78 <div>this is a box</div> 79 <div>this is a box</div> 80 <div>this is a box</div> 81 <div>this is a box</div> 82 <div>this is a box</div> 83 <div>this is a box</div> 84 <div>this is a box</div> 85 <div>this is a box</div> 86 <div>this is a box</div> 87 <div>this is a box</div> 88 <div>this is a box</div> 89 <div>this is a box</div> 90 <div>this is a box</div> 91 <div>this is a box</div> 92 <div>this is a box</div> 93 <div>this is a box</div> 94 <div>this is a box</div> 95 <div>this is a box</div> 96 <div>this is a box</div> 97 <div>this is a box</div> 98 <div>this is a box</div> 99 <div>this is a box</div> 100 <div>this is a box</div> 101 <div>this is a box</div> 102 <div>this is a box</div> 103 <div>this is a box</div> 104 <div>this is a box</div> 105 <div>this is a box</div> 106 <div>this is a box</div> 107 <div>this is a box</div> 108 <div>this is a box</div> 109 <div>this is a box</div> 110 <div>this is a box</div> 111 <div>this is a box</div> 112 <div>this is a box</div> 113 <div>this is a box</div> 114 <div>this is a box</div> 115 <div>this is a box</div> 116 <div>this is a box</div> 117 <div>this is a box</div> 118 <div>this is a box</div> 119 <div>this is a box</div> 120 <div>this is a box</div> 121 <div>this is a box</div> 122 <div>this is a box</div> 123 <div>this is a box</div> 124 <div>this is a box</div> 125 <div>this is a box</div> 126 <div>this is a box</div> 127 <div>this is a box</div> 128 <div>this is a box</div> 129 <div>this is a box</div> 130 <div>this is a box</div> 131 <div>this is a box</div> 132 <div>this is a box</div> 133 <div>this is a box</div> 134 <div>this is a box</div> 135 <div>this is a box</div> 136 <div>this is a box</div> 137 <div>this is a box</div> 138 <div>this is a box</div> 139 <div>this is a box</div> 140 <div>this is a box</div> 141 <div>this is a box</div> 142 <div>this is a box</div> 143 <div>this is a box</div> 144 <div>this is a box</div> 145 <div>this is a box</div> 146 <div>this is a box</div> 147 <div>this is a box</div> 148 <div>this is a box</div> 149 <div>this is a box</div> 150 <div>this is a box</div> 151 <div>this is a box</div> 152 <div>this is a box</div> 153 <div>this is a box</div> 154 <div>this is a box</div> 155 <div>this is a box</div> 156 <div>this is a box</div> 157 <div>this is a box</div> 158 <div>this is a box</div> 159 <div>this is a box</div> 160 <div>this is a box</div> 161 <div>this is a box</div> 162 <div>this is a box</div> 163 <div>this is a box</div> 164 <div>this is a box</div> 165 <div>this is a box</div> 166 <div>this is a box</div> 167 <div>this is a box</div> 168 <div>this is a box</div> 169 <div>this is a box</div> 170 <div>this is a box</div> 171 <div>this is a box</div> 172 <div>this is a box</div> 173 <div>this is a box</div> 174 <div>this is a box</div> 175 <div>this is a box</div> 176 <div>this is a box</div> 177 <div>this is a box</div> 178 <div>this is a box</div> 179 <div>this is a box</div> 180 <div>this is a box</div> 181 <div>this is a box</div> 182 <div>this is a box</div> 183 <div>this is a box</div> 184 <div>this is a box</div> 185 <div>this is a box</div> 186 <div>this is a box</div> 187 <div>this is a box</div> 188 <div>this is a box</div> 189 <div>this is a box</div> 190 <div>this is a box</div> 191 <div>this is a box</div> 192 <div>this is a box</div> 193 <div>this is a box</div> 194 <div>this is a box</div> 195 <div>this is a box</div> 196 <div>this is a box</div> 197 <div>this is a box</div> 198 <div>this is a box</div> 199 <div>this is a box</div> 200 <div>this is a box</div> 201 <div>this is a box</div> 202 <div>this is a box</div> 203 <div>this is a box</div> 204 <div>this is a box</div> 205 <div>this is a box</div> 206 <div>this is a box</div> 207 <div>this is a box</div> 208 <div>this is a box</div> 209 <div>this is a box</div> 210 <div>this is a box</div> 211 <div>this is a box</div> 212 <div>this is a box</div> 213 <div>this is a box</div> 214 <div>this is a box</div> 215 <div>this is a box</div> 216 <div>this is a box</div> 217 <div>this is a box</div> 218 <div>this is a box</div> 219 <div>this is a box</div> 220 <div>this is a box</div> 221 <div>this is a box</div> 222 <div>this is a box</div> 223 <div>this is a box</div> 224 <div>this is a box</div> 225 <div>this is a box</div> 226 <div>this is a box</div> 227 <div>this is a box</div> 228 <div>this is a box</div> 229 <div>this is a box</div> 230 <div>this is a box</div> 231 <div>this is a box</div> 232 <div>this is a box</div> 233 <div>this is a box</div> 234 <div>this is a box</div> 235 <div>this is a box</div> 236 <div>this is a box</div> 237 <div>this is a box</div> 238 <div>this is a box</div> 239 <div>this is a box</div> 240 <div>this is a box</div> 241 <div>this is a box</div> 242 <div>this is a box</div> 243 <div>this is a box</div> 244 <div>this is a box</div> 245 <div>this is a box</div> 246 <div>this is a box</div> 247 <div>this is a box</div> 248 <div>this is a box</div> 249 <div>this is a box</div> 250 <div>this is a box</div> 251 <div>this is a box</div> 252 <div>this is a box</div> 253 <div>this is a box</div> 254 <div>this is a box</div> 255 <div>this is a box</div> 256 <div>this is a box</div> 257 <div>this is a box</div> 258 <div>this is a box</div> 259 <div>this is a box</div> 260 <div>this is a box</div> 261 <div>this is a box</div> 262 <div>this is a box</div> 263 <div>this is a box</div> 264 <div>this is a box</div> 265 <div>this is a box</div> 266 <div>this is a box</div> 267 <div>this is a box</div> 268 <div>this is a box</div> 269 <div>this is a box</div> 270 <div>this is a box</div> 271 <div>this is a box</div> 272 <div>this is a box</div> 273 <div>this is a box</div> 274 <div>this is a box</div> 275 <div>this is a box</div> 276 <div>this is a box</div> 277 <div>this is a box</div> 278 <div>this is a box</div> 279 <div>this is a box</div> 280 <div>this is a box</div> 281 <div>this is a box</div> 282 <div>this is a box</div> 283 <div>this is a box</div> 284 <div>this is a box</div> 285 <div>this is a box</div> 286 <div>this is a box</div> 287 <div>this is a box</div> 288 <div>this is a box</div> 289 <div>this is a box</div> 290 <div>this is a box</div> 291 <div>this is a box</div> 292 <div>this is a box</div> 293 <div>this is a box</div> 294 <div>this is a box</div> 295 <div>this is a box</div> 296 <div>this is a box</div> 297 <div>this is a box</div> 298 <div>this is a box</div> 299 <div>this is a box</div> 300 <div>this is a box</div> 301 <div>this is a box</div> 302 <div>this is a box</div> 303 <div>this is a box</div> 304 <div>this is a box</div> 305 <div>this is a box</div> 306 <div>this is a box</div> 307 <div>this is a box</div> 308 <div>this is a box</div> 309 <div>this is a box</div> 310 <div>this is a box</div> 311 <div>this is a box</div> 312 <div>this is a box</div> 313 <div>this is a box</div> 314 <div>this is a box</div> 315 <div>this is a box</div> 316 <div>this is a box</div> 317 <div>this is a box</div> 318 <div>this is a box</div> 319 <div>this is a box</div> 320 <div>this is a box</div> 321 <div>this is a box</div> 322 <div>this is a box</div> 323 <div>this is a box</div> 324 <div>this is a box</div> 325 <div>this is a box</div> 326 <div>this is a box</div> 327 <div>this is a box</div> 328 <div>this is a box</div> 329 <div>this is a box</div> 330 <div>this is a box</div> 331 <div>this is a box</div> 332 <div>this is a box</div> 333 <div>this is a box</div> 334 <div>this is a box</div> 335 <div>this is a box</div> 336 <div>this is a box</div> 337 <div>this is a box</div> 338 <div>this is a box</div> 339 <div>this is a box</div> 340 <div>this is a box</div> 341 <div>this is a box</div> 342 <div>this is a box</div> 343 <div>this is a box</div> 344 <div>this is a box</div> 345 <div>this is a box</div> 346 <div>this is a box</div> 347 <div>this is a box</div> 348 <div>this is a box</div> 349 <div>this is a box</div> 350 <div>this is a box</div> 351 <div>this is a box</div> 352 <div>this is a box</div> 353 <div>this is a box</div> 354 <div>this is a box</div> 355 <div>this is a box</div> 356 <div>this is a box</div> 357 <div>this is a box</div> 358 <div>this is a box</div> 359 <div>this is a box</div> 360 <div>this is a box</div> 361 <div>this is a box</div> 362 <div>this is a box</div> 363 <div>this is a box</div> 364 <div>this is a box</div> 365 <div>this is a box</div> 366 <div>this is a box</div> 367 <div>this is a box</div> 368 <div>this is a box</div> 369 <div>this is a box</div> 370 <div>this is a box</div> 371 <div>this is a box</div> 372 <div>this is a box</div> 373 <div>this is a box</div> 374 <div>this is a box</div> 375 <div>this is a box</div> 376 <div>this is a box</div> 377 <div>this is a box</div> 378 <div>this is a box</div> 379 <div>this is a box</div> 380 <div>this is a box</div> 381 <div>this is a box</div> 382 <div>this is a box</div> 383 <div>this is a box</div> 384 <div>this is a box</div> 385 <div>this is a box</div> 386 <div>this is a box</div> 387 <div>this is a box</div> 388 <div>this is a box</div> 389 <div>this is a box</div> 390 <div>this is a box</div> 391 <div>this is a box</div> 392 <div>this is a box</div> 393 <div>this is a box</div> 394 <div>this is a box</div> 395 <div>this is a box</div> 396 <div>this is a box</div> 397 <div>this is a box</div> 398 <div>this is a box</div> 399 <div>this is a box</div> 400 <div>this is a box</div> 401 <div>this is a box</div> 402 <div>this is a box</div> 403 <div>this is a box</div> 404 <div>this is a box</div> 405 <div>this is a box</div> 406 <div>this is a box</div> 407 <div>this is a box</div> 408 <div>this is a box</div> 409 <div>this is a box</div> 410 <div>this is a box</div> 411 <div>this is a box</div> 412 <div>this is a box</div> 413 <div>this is a box</div> 414 <div>this is a box</div> 415 <div>this is a box</div> 416 <div>this is a box</div> 417 <div>this is a box</div> 418 <div>this is a box</div> 419 <div>this is a box</div> 420 <div>this is a box</div> 421 <div>this is a box</div> 422 <div>this is a box</div> 423 <div>this is a box</div> 424 <div>this is a box</div> 425 <div>this is a box</div> 426 <div>this is a box</div> 427 <div>this is a box</div> 428 <div>this is a box</div> 429 <div>this is a box</div> 430 <div>this is a box</div> 431 <div>this is a box</div> 432 <div>this is a box</div> 433 <div>this is a box</div> 434 <div>this is a box</div> 435 <div>this is a box</div> 436 <div>this is a box</div> 437 <div>this is a box</div> 438 <div>this is a box</div> 439 <div>this is a box</div> 440 <div>this is a box</div> 441 <div>this is a box</div> 442 <div>this is a box</div> 443 <div>this is a box</div> 444 <div>this is a box</div> 445 <div>this is a box</div> 446 <div>this is a box</div> 447 <div>this is a box</div> 448 <div>this is a box</div> 449 <div>this is a box</div> 450 <div>this is a box</div> 451 <div>this is a box</div> 452 <div>this is a box</div> 453 <div>this is a box</div> 454 <div>this is a box</div> 455 <div>this is a box</div> 456 <div>this is a box</div> 457 <div>this is a box</div> 458 <div>this is a box</div> 459 <div>this is a box</div> 460 <div>this is a box</div> 461 <div>this is a box</div> 462 <div>this is a box</div> 463 <div>this is a box</div> 464 <div>this is a box</div> 465 <div>this is a box</div> 466 <div>this is a box</div> 467 <div>this is a box</div> 468 <div>this is a box</div> 469 <div>this is a box</div> 470 <div>this is a box</div> 471 <div>this is a box</div> 472 <div>this is a box</div> 473 <div>this is a box</div> 474 <div>this is a box</div> 475 <div>this is a box</div> 476 <div>this is a box</div> 477 <div>this is a box</div> 478 <div>this is a box</div> 479 <div>this is a box</div> 480 <div>this is a box</div> 481 <div>this is a box</div> 482 <div>this is a box</div> 483 <div>this is a box</div> 484 <div>this is a box</div> 485 <div>this is a box</div> 486 <div>this is a box</div> 487 <div>this is a box</div> 488 <div>this is a box</div> 489 <div>this is a box</div> 490 <div>this is a box</div> 491 <div>this is a box</div> 492 <div>this is a box</div> 493 <div>this is a box</div> 494 <div>this is a box</div> 495 <div>this is a box</div> 496 <div>this is a box</div> 497 <div>this is a box</div> 498 <div>this is a box</div> 499 <div>this is a box</div> 500 <div>this is a box</div> 501 <div>this is a box</div> 502 <div>this is a box</div> 503 <div>this is a box</div> 504 <div>this is a box</div> 505 <div>this is a box</div> 506 <div>this is a box</div> 507 <div>this is a box</div> 508 <div>this is a box</div> 509 <div>this is a box</div> 510 <div>this is a box</div> 511 <div>this is a box</div> 512 <div>this is a box</div> 513 <div>this is a box</div> 514 <div>this is a box</div> 515 <div>this is a box</div> 516 <div>this is a box</div> 517 <div>this is a box</div> 518 <div>this is a box</div> 519 <div>this is a box</div> 520 <div>this is a box</div> 521 <div>this is a box</div> 522 <div>this is a box</div> 523 <div>this is a box</div> 524 <div>this is a box</div> 525 <div>this is a box</div> 526 <div>this is a box</div> 527 <div>this is a box</div> 528 <div>this is a box</div> 529 <div>this is a box</div> 530 <div>this is a box</div> 531 <div>this is a box</div> 532 <div>this is a box</div> 533 <div>this is a box</div> 534 <div>this is a box</div> 535 <div>this is a box</div> 536 <div>this is a box</div> 537 <div>this is a box</div> 538 <div>this is a box</div> 539 <div>this is a box</div> 540 <div>this is a box</div> 541 <div>this is a box</div> 542 <div>this is a box</div> 543 <div>this is a box</div> 544 <div>this is a box</div> 545 <div>this is a box</div> 546 <div>this is a box</div> 547 <div>this is a box</div> 548 <div>this is a box</div> 549 this is near the bottom of the scrollframe. 550 <div>this is a box</div> 551 <div>this is a box</div> 552 <div>this is a box</div> 553 <div>this is a box</div> 554 <div>this is a box</div> 555 this is the bottom of the scrollframe. 556 </div> 557 </div> 558 </div> 559 560 <pre id="test"> 561 <script type="text/javascript"> 562 563 const is = window.opener.is; 564 const ok = window.opener.ok; 565 const SimpleTest = window.opener.SimpleTest; 566 567 // Returns a list of async scroll offsets that the |inner| element had, one for 568 // each paint. 569 function getAsyncScrollOffsets(aPaintsToIgnore) { 570 var offsets = []; 571 var compositorTestData = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData(); 572 var buckets = compositorTestData.paints.slice(aPaintsToIgnore); 573 ok(buckets.length >= 3, "Expected at least three paints in the compositor test data"); 574 var childIsLayerized = false; 575 for (var i = 0; i < buckets.length; ++i) { 576 var apzcTree = buildApzcTree(convertScrollFrameData(buckets[i].scrollFrames)); 577 var rcd = findRcdNode(apzcTree); 578 if (rcd == null) { 579 continue; 580 } 581 if (rcd.children.length) { 582 // The child may not be layerized in the first few paints, but once it is 583 // layerized, it should stay layerized. 584 childIsLayerized = true; 585 } 586 if (!childIsLayerized) { 587 continue; 588 } 589 590 ok(rcd.children.length == 1, "Root content APZC has exactly one child"); 591 offsets.push(parsePoint(rcd.children[0].asyncScrollOffset)); 592 } 593 return offsets; 594 } 595 596 async function test() { 597 var utils = SpecialPowers.DOMWindowUtils; 598 599 // The APZ test data accumulates whenever a test turns it on. We just want 600 // the data for this test, so we check how many frames are already recorded 601 // and discard those later. 602 var framesToSkip = SpecialPowers.getDOMWindowUtils(window).getCompositorAPZTestData().paints.length; 603 604 var elm = document.getElementsByClassName("inner")[0]; 605 // Set a zero-margin displayport to ensure that the element is async-scrollable 606 // otherwise on Fennec it is not 607 utils.setDisplayPortMarginsForElement(0, 0, 0, 0, elm, 0); 608 609 var maxScroll = elm.scrollTopMax; 610 elm.scrollTop = maxScroll; 611 await promiseAllPaintsDone(); 612 await promiseOnlyApzControllerFlushed(); 613 614 // Take control of the refresh driver 615 utils.advanceTimeAndRefresh(0); 616 617 // Force the next reflow to get interrupted 618 utils.forceReflowInterrupt(); 619 620 // Make a change that triggers frame reconstruction, and then tick the refresh 621 // driver so that layout processes the pending restyles and then runs an 622 // interruptible reflow. That reflow *will* be interrupted (because of the flag 623 // we set above), and we should end up with a transient 0,0 scroll offset 624 // being sent to the compositor. 625 elm.parentNode.classList.add("contentBefore"); 626 utils.advanceTimeAndRefresh(0); 627 // On android, and maybe non-e10s platforms generally, we need to manually 628 // kick the paint to send the layer transaction to the compositor. 629 await promiseAllPaintsDone(); 630 631 // Read the main-thread scroll offset; although this is temporarily 0,0 that 632 // temporary value is never exposed to content - instead reading this value 633 // will finish doing the interrupted reflow from above and then report the 634 // correct scroll offset. 635 is(elm.scrollTop, maxScroll, "Main-thread scroll position was restored"); 636 637 // .. and now flush everything to make sure the state gets pushed over to the 638 // compositor and APZ as well. 639 utils.restoreNormalRefresh(); 640 await promiseApzFlushedRepaints(); 641 642 // Now we pull the compositor data and check it. What we expect to see is that 643 // the scroll position goes to maxScroll, then drops to 0 and then goes back 644 // to maxScroll. This test is specifically testing that last bit - that it 645 // properly gets restored from 0 to maxScroll. 646 // The one hitch is that on Android this page is loaded with some amount of 647 // zoom, and the async scroll is in ParentLayerPixel coordinates, so it will 648 // not match maxScroll exactly. Since we can't reliably compute what that 649 // ParentLayer scroll will be, we just make sure the async scroll is nonzero 650 // and use the first value we encounter to verify that it got restored properly. 651 // The other alternative is to spawn this test into a new window with 1.0 zoom 652 // but I'm tired of doing that for pretty much every test. 653 var state = 0; 654 var asyncScrollOffsets = getAsyncScrollOffsets(framesToSkip); 655 dump("Got scroll offsets: " + JSON.stringify(asyncScrollOffsets) + "\n"); 656 var maxScrollParentLayerPixels = maxScroll; 657 while (asyncScrollOffsets.length) { 658 let offset = asyncScrollOffsets.shift(); 659 switch (state) { 660 // 0 is the initial state, the scroll offset might be zero but should 661 // become non-zero from when we set scrollTop to scrollTopMax 662 case 0: 663 if (offset.y == 0) { 664 break; 665 } 666 if (getPlatform() == "android") { 667 ok(offset.y > 0, "Async scroll y of scrollframe is " + offset.y); 668 maxScrollParentLayerPixels = offset.y; 669 } else { 670 is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe is " + offset.y); 671 } 672 state = 1; 673 break; 674 675 // state 1 starts out at maxScrollParentLayerPixels, should drop to 0 676 // because of the interrupted reflow putting the scroll into a transient 677 // zero state 678 case 1: 679 if (offset.y == maxScrollParentLayerPixels) { 680 break; 681 } 682 is(offset.y, 0, "Async scroll position was temporarily 0"); 683 state = 2; 684 break; 685 686 // state 2 starts out the transient 0 scroll offset, and we expect the 687 // scroll position to get restored back to maxScrollParentLayerPixels 688 case 2: 689 if (offset.y == 0) { 690 break; 691 } 692 is(offset.y, maxScrollParentLayerPixels, "Async scroll y of scrollframe restored to " + offset.y); 693 state = 3; 694 break; 695 696 // Terminal state. The scroll position should stay at maxScrollParentLayerPixels 697 case 3: 698 is(offset.y, maxScrollParentLayerPixels, "Scroll position maintained"); 699 break; 700 } 701 } 702 is(state, 3, "The scroll position did drop to 0 and then get restored properly"); 703 704 window.opener.finishTest(); 705 } 706 707 waitUntilApzStable() 708 .then(async () => test()); 709 710 </script> 711 </body> 712 </html>