tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

helper_mainthread_scroll_bug1662379.html (5394B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <meta name="viewport" content="width=device-width, minimum-scale=1.0">
      4 <script src="apz_test_utils.js"></script>
      5 <script src="apz_test_native_event_utils.js"></script>
      6 <script src="/tests/SimpleTest/paint_listener.js"></script>
      7 <div id="content">
      8  <div id="lhs">
      9    <ul>
     10      <li>Test item 1</li>
     11      <li>Test item 2</li>
     12      <li>Test item 3</li>
     13      <li>Test item 4</li>
     14      <li>Test item 5</li>
     15      <li>Test item 6</li>
     16      <li>Test item 7</li>
     17      <li>Test item 8</li>
     18      <li>Test item 9</li>
     19      <li>Test item 10</li>
     20      <li>Test item 11</li>
     21      <li>Test item 13</li>
     22      <li>Test item 14</li>
     23      <li>Test item 15</li>
     24      <li>Test item 16</li>
     25      <li>Test item 17</li>
     26      <li>Test item 18</li>
     27      <li>Test item 19</li>
     28    </ul>
     29  </div>
     30  <div id="center">
     31    <div>
     32     Steps to reproduce:
     33     <ol>
     34      <li>Scroll the list of "test items" all the way to the bottom
     35      <li>Click on the reparent button below
     36      <li>Click on one of the test items
     37      <li>The `clickTarget` JS variable should match the thing you clicked on
     38     </ol>
     39    </div>
     40    <button onclick="reparent()"> Click here to reparent </button>
     41  </div>
     42 </div>
     43 <style>
     44 #content {
     45  display: flex;
     46  height: 300px;
     47  background-color: pink;
     48  border: 3px solid green;
     49 }
     50 
     51 #lhs, #rhs {
     52  width: 250px;
     53  overflow: scroll;
     54  flex: 0 0 250px
     55 }
     56 
     57 #center {
     58  padding: 20px;
     59 }
     60 
     61 ul {
     62  margin: 16px 0px;
     63 }
     64 
     65 /* Each element has a border-height of 20 + (2 * 5) + (2 * 1) = 32px */
     66 ul li {
     67  background-color: aqua;
     68  border: 1px solid blue;
     69  padding: 5px;
     70  cursor: pointer;
     71  height: 20px;
     72 }
     73 </style>
     74 <script>
     75 var clickTarget = null;
     76 
     77 for (var el of document.querySelectorAll("ul li")) {
     78  el.addEventListener("click", function(e) {
     79    clickTarget = e.target;
     80  });
     81 }
     82 
     83 function reparent() {
     84  var content = document.getElementById("content");
     85  var lhs = document.getElementById("lhs");
     86  content.appendChild(lhs);
     87  lhs.id = "rhs";
     88 }
     89 
     90 function getAsyncScrollOffsetForUniqueRcdChild() {
     91  let apzcTree = getLastApzcTree();
     92  let rcd = findRcdNode(apzcTree);
     93  // We assume a unique child of the RCD. If this is not the case, bail out.
     94  if (rcd == null || rcd.children.length != 1) {
     95    info("Did not find unique child on the RCD: rcd=" + JSON.stringify(rcd));
     96    return {x: -1, y: -1};
     97  }
     98  let child = rcd.children[0];
     99  return parsePoint(child.asyncScrollOffset);
    100 }
    101 
    102 async function test() {
    103  if (getPlatform() == "android") {
    104    ok(true, "Mousewheel is not supported on android, skipping test...");
    105    return;
    106  }
    107 
    108  // Simulate user mouse-wheel scrolling the lhs pane down to the bottom.
    109  let lhs = document.getElementById("lhs");
    110  let scrollendPromise = promiseScrollend(lhs);
    111  while (lhs.scrollTop < lhs.scrollTopMax) {
    112    await promiseNativeWheelAndWaitForScrollEvent(
    113      lhs,
    114      50, 50,
    115      0, -50);
    116    info("Did scroll, pos is now " + lhs.scrollTop + "/" + lhs.scrollTopMax);
    117  }
    118  // Wait for the animation to be completely done. (scrollTop is rounded to
    119  // the nearest integer value, so at the time scrollTop reaches scrollTopMax,
    120  // the compositor animation may still be a sub-pixel amount away from the
    121  // destination).
    122  await scrollendPromise;
    123  await promiseApzFlushedRepaints();
    124 
    125  // Click at 50,50 from the top-left corner of the lhs pane. If lhs were
    126  // not scrolled, this would hit "Test item 2" but since lhs is scrolled
    127  // it should hit something else. So let's check that it doesn't hit
    128  // "Test item 2".
    129  await promiseNativeMouseEventWithAPZAndWaitForEvent({
    130    type: "click",
    131    target: lhs,
    132    offsetX: 50,
    133    offsetY: 50,
    134  });
    135  isnot(clickTarget, null, "Click target got set");
    136  info("Hit " + clickTarget.textContent);
    137  isnot(clickTarget.textContent, "Test item 2", "Item two didn't get hit");
    138  clickTarget = null;
    139 
    140  // Do the reparenting
    141  reparent();
    142  await promiseApzFlushedRepaints();
    143  info("Done reparent + wait, about to fire click...");
    144 
    145  // Now fire the click on the reparented element (which is now called "rhs")
    146  // at the same 50,50 offset from the top-left. This time it *should* hit
    147  // "Test item 2" because the reparenting should reset the scroll offset
    148  // back to zero.
    149  await promiseNativeMouseEventWithAPZAndWaitForEvent({
    150    type: "click",
    151    target: document.getElementById("rhs"),
    152    offsetX: 50,
    153    offsetY: 50,
    154  });
    155 
    156  // Check that the visual scroll position (as determined by the compositor
    157  // scroll offset) is consistent with the main-thread scroll position (as
    158  // determined by the click target). In both cases the scroll position
    159  // should have gotten reset back to zero with the reparenting step. In
    160  // bug 1662379 the visual scroll position was *not* getting reset, and
    161  // so even though the main-thread click target was "Test item 2", the
    162  // compositor scroll offset was non-zero, so to the user the click
    163  // appeared to trigger something different from what they clicked on.
    164  isnot(clickTarget, null, "Click target got set");
    165  is(clickTarget.textContent, "Test item 2", "Item two got hit correctly");
    166  let rhsCompositorScrollOffset = getAsyncScrollOffsetForUniqueRcdChild();
    167  is(rhsCompositorScrollOffset.x, 0, "rhs compositor x-offset is zero");
    168  is(rhsCompositorScrollOffset.y, 0, "rhs compositor y-offset is zero");
    169 }
    170 
    171 waitUntilApzStable()
    172 .then(test)
    173 .then(subtestDone, subtestFailed);
    174 </script>