tor-browser

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

helper_hittest_overscroll.html (8655B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <title>Test APZ hit-testing while overscrolled</title>
      5  <script type="application/javascript" src="apz_test_utils.js"></script>
      6  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
      7  <script src="/tests/SimpleTest/paint_listener.js"></script>
      8  <meta name="viewport" content="width=device-width"/>
      9  <style>
     10    html, body {
     11      margin: 0;
     12      padding: 0;
     13    }
     14    .spacer {
     15      height: 5000px;
     16    }
     17    #target {
     18      margin-left: 100px;
     19      margin-top: 2px;
     20      height: 4px;
     21      width: 4px;
     22      background: red;
     23    }
     24    #fixedtarget {
     25      left: 300px;
     26      height: 20px;
     27      width: 5px;
     28      top: 2px;
     29      background: green;
     30      position: fixed;
     31    }
     32    #fixedtargetbutton {
     33      height: 10px;
     34      width: 5px;
     35      padding: 0;
     36      margin-top: 10px;
     37      margin-left: 0;
     38      border: 0;
     39    }
     40  </style>
     41 </head>
     42 <body>
     43 <div id="target"></div>
     44 <div id="fixedtarget">
     45   <div id="fixedtargetbutton"></div>
     46 </div>
     47 <div id="spacer" class="spacer"></div>
     48 </body>
     49 <script type="application/javascript">
     50 
     51 // Some helper functions for listening for click events in the browser chrome.
     52 
     53 // A handle used to interact with the chrome script used to implement
     54 // [start|stop]ListeningFOrClickEventsInChrome().
     55 let chromeScriptHandle = null;
     56 
     57 function startListeningForClickEventsInChrome() {
     58  function chromeScript() {
     59    /* eslint-env mozilla/chrome-script */
     60    let topWin = Services.wm.getMostRecentWindow("navigator:browser");
     61    if (!topWin) {
     62      topWin = Services.wm.getMostRecentWindow("navigator:geckoview");
     63    }
     64    let chromeReceivedClick = false;
     65    function chromeListener() {
     66      chromeReceivedClick = true;
     67    }
     68    topWin.addEventListener("click", chromeListener);
     69    function queryClicked() {
     70      sendAsyncMessage("query-clicked-response", { chromeReceivedClick });
     71    }
     72    function cleanup() {
     73      topWin.removeEventListener("click", chromeListener);
     74      removeMessageListener("query-clicked", queryClicked);
     75      removeMessageListener("cleanup", cleanup);
     76    }
     77    addMessageListener("query-clicked", queryClicked);
     78    addMessageListener("cleanup", cleanup);
     79  }
     80  chromeScriptHandle = SpecialPowers.loadChromeScript(chromeScript);
     81 }
     82 
     83 async function didChromeReceiveClick() {
     84  chromeScriptHandle.sendAsyncMessage("query-clicked", null);
     85  let response = await chromeScriptHandle.promiseOneMessage("query-clicked-response");
     86  ok(response && ("chromeReceivedClick" in response),
     87     "Received a well-formed response from chrome script");
     88  return response.chromeReceivedClick;
     89 }
     90 
     91 function stopListeningForClickEventsInChrome() {
     92  chromeScriptHandle.sendAsyncMessage("cleanup", null);
     93  chromeScriptHandle.destroy();
     94 }
     95 
     96 async function test() {
     97  var config = getHitTestConfig();
     98  var utils = config.utils;
     99 
    100  // Overscroll the root scroll frame at the top, creating a gutter.
    101  // Note that the size of the gutter will only be 8px, because
    102  // setAsyncScrollOffset() applies the overscroll as a single delta,
    103  // and current APZ logic that transforms a delta into an overscroll
    104  // amount limits each delta to at most 8px.
    105  utils.setAsyncScrollOffset(document.documentElement, 0, -200);
    106 
    107  // Check that the event hits the root scroll frame in APZ.
    108  // This is important because additional pan-gesture events in the gutter
    109  // should continue to be handled and cause further overscroll (or
    110  // relieving overscroll, depending on their direction).
    111  let hitResult = hitTest({x: 100, y: 4});
    112  let rootViewId = utils.getViewId(document.documentElement);
    113  checkHitResult(hitResult,
    114                 APZHitResultFlags.VISIBLE,
    115                 rootViewId,
    116                 utils.getLayersId(),
    117                 "APZ hit-test in the root gutter");
    118 
    119  // Now, perform a click in the gutter and check that APZ prevents
    120  // the event from reaching Gecko.
    121  // To be sure that no event was dispatched to Gecko, install listeners
    122  // on both the browser chrome window and the content window.
    123  // This makes sure we catch the case where the overscroll transform causes
    124  // the event to incorrectly target the browser chrome.
    125 
    126  //// Util function to perform mouse events in the gutter. Used to ensure these
    127  //// events are not dispatched to the content.
    128  async function clickInGutter(xOffset, yOffset) {
    129    startListeningForClickEventsInChrome();
    130    let contentReceivedClick = false;
    131    let contentListener = function(e) {
    132      info("event clientX = " + e.clientX);
    133      info("event clientY = " + e.clientY);
    134      info("event target id: " + e.target.id);
    135      contentReceivedClick = true;
    136    };
    137    document.addEventListener("click", contentListener);
    138    await synthesizeNativeMouseEventWithAPZ({
    139      type: "click",
    140      target: window,
    141      offsetX: xOffset,
    142      offsetY: yOffset,
    143    });
    144    // Wait 10 frames for the event to maybe arrive, and if it
    145    // hasn't, assume it won't.
    146    for (let i = 0; i < 10; i++) {
    147      await promiseFrame();
    148    }
    149    info("Finished waiting around for click event");
    150    let chromeReceivedClick = await didChromeReceiveClick();
    151    ok(!chromeReceivedClick,
    152       "Gecko received click event in browser chrome when it shouldn't have");
    153    ok(!contentReceivedClick,
    154       "Gecko received click event targeting web content when it shouldn't have");
    155    stopListeningForClickEventsInChrome();
    156    document.removeEventListener("click", contentListener);
    157  }
    158 
    159  // Perform the test
    160  await clickInGutter(100, 4);
    161 
    162  // Finally, while still overscrolled, perform a click not in the gutter.
    163  // This event should successfully go through to the web content, and
    164  // be untransformed by the overscroll transform (such that it hits the
    165  // content that is visually under the cursor).
    166  let clickPromise = new Promise(resolve => {
    167    document.addEventListener("click", function(e) {
    168      info("event clientX = " + e.clientX);
    169      info("event clientY = " + e.clientY);
    170      is(e.target, target, "Click while overscrolled hit intended target");
    171      resolve();
    172    }, { once: true });
    173  });
    174  await synthesizeNativeMouseEventWithAPZ({
    175    type: "click",
    176    target: window,
    177    offsetX: 102,
    178    offsetY: 12,
    179  });
    180  await clickPromise;
    181 
    182  // Test that mouse events targetting the fixed content are dispatched
    183  // to the content.
    184  async function clickFixed(yOffset, expectedTarget) {
    185    let clickFixedPromise = new Promise(resolve => {
    186      document.addEventListener("click", function(e) {
    187        info("event clientX = " + e.clientX);
    188        info("event clientY = " + e.clientY);
    189        info("event target id: " + e.target.id);
    190        is(e.target, expectedTarget, "Click while overscrolled hit intended target");
    191        resolve();
    192      }, { once: true });
    193    });
    194    await synthesizeNativeMouseEventWithAPZ({
    195      type: "click",
    196      target: window,
    197      offsetX: 302,
    198      offsetY: yOffset,
    199    });
    200    await clickFixedPromise;
    201  }
    202 
    203  // This hit is technically in the gutter created by the overscroll, but we
    204  // should still dispatch to gecko due to the fixed element extending into
    205  // the gutter.
    206  await clickFixed(4, fixedtarget, false);
    207  // Perform various mouse events to ensure the fixed element has not moved
    208  await clickFixed(10, fixedtarget, false);
    209  await clickFixed(14, fixedtargetbutton, false);
    210  await clickFixed(18, fixedtargetbutton, false);
    211 
    212  let clickFixedPromise = new Promise(resolve => {
    213    document.addEventListener("click", function(e) {
    214      info("event clientX = " + e.clientX);
    215      info("event clientY = " + e.clientY);
    216      info("event target id: " + e.target.id);
    217      // TODO(dlrobertson): What exists directly below the fixed element?
    218      // Should there be a gutter below the fixed element? Or should events
    219      // directed below the fixed element be dispatched normally. In the
    220      // transform of the mouse event, the hit will not have any side bits
    221      // set and we  will transform the hit result. As a result, 25 will be
    222      // transformed to ~17, and the event will be dispatched to the fixed
    223      // element.
    224      todo(false,
    225           "Click while overscrolled hit intended target below fixed content");
    226      resolve();
    227    }, { once: true });
    228  });
    229  await synthesizeNativeMouseEventWithAPZ({
    230    type: "click",
    231    target: window,
    232    offsetX: 302,
    233    offsetY: 25,
    234  });
    235  await clickFixedPromise;
    236 
    237  // Click above the fixed element, but in the gutter.
    238  await clickInGutter(302, 1);
    239  // Click left of the the fixed element, but in the gutter.
    240  await clickInGutter(298, 4);
    241 }
    242 
    243 waitUntilApzStable()
    244 .then(test)
    245 .then(subtestDone, subtestFailed);
    246 
    247 </script>
    248 </html>