tor-browser

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

pointerevent_pointercapture_in_frame.html (15251B)


      1 <meta name="variant" content="?mouse">
      2 <meta name="variant" content="?touch">
      3 <meta name="variant" content="?pen">
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 <script src="/resources/testdriver.js"></script>
      7 <script src="/resources/testdriver-vendor.js"></script>
      8 <script src="/resources/testdriver-actions.js"></script>
      9 <script type="text/javascript" src="pointerevent_support.js"></script>
     10 <style>
     11 iframe {
     12  width: 300px;
     13  height: 300px;
     14  top: 100px;
     15  left: 100px;
     16  border: 0;
     17  position: absolute;
     18  background: green;
     19 }
     20 #outerFrame {
     21  width: 500px;
     22  height: 500px;
     23  background: blue;
     24 }
     25 body {
     26  touch-action:none;
     27 }
     28 </style>
     29 <body id="outerFrame body" onload="run()">
     30 <div id='outerFrame'>
     31 <iframe src="resources/pointerevent_pointercapture-iframe.html"></iframe>
     32 </div>
     33 </body>
     34 <script>
     35 var receivedEventList = [];
     36 // |start_logging| is set to true in the pointerdown event handler, and as such
     37 // also tracks if pointerdown happened already in the test.
     38 var start_logging = false;
     39 var first_pointermove_happened = false;
     40 
     41 // Add a timeout promise when waiting for lostpointercapture and pointerup.
     42 // We add this to make it easy to know which event wasn't fired on the right
     43 // element.
     44 var eventTimeout = ()=>(()=>new Promise((resolve, reject)=>{
     45    const msToWait = 4000;
     46    let start;
     47    function wait(timestamp){
     48        if(start === undefined)
     49            start = timestamp;
     50        if(timestamp - start < msToWait)
     51            requestAnimationFrame(wait);
     52        else
     53            resolve();
     54    }
     55    requestAnimationFrame(wait);
     56 }));
     57 
     58 
     59 function handleEvent(event) {
     60  if (event.type == 'pointerdown') {
     61    start_logging = true;
     62    const capture_target = document.captureTargetOverride || event.target;
     63    try {
     64        capture_target.setPointerCapture(event.pointerId);
     65    } catch (error) {
     66        // The promise_tests below look into logged events to assert capture
     67        // failures without logging the exception here.
     68        //
     69        // Logging the exception here is not interoperable because the pointer
     70        // capture algorithm [1] may fail silently at Step 5 instead of throwing
     71        // at Step 1 in certain cases related to optional requirements for
     72        // `event.pointerId`.  Below are two such cases:
     73        // - The user agent has a reserved constant pointerId for pointerType
     74        //   "mouse".
     75        // - The user agent exposes the same pointerId for a pointer dragged
     76        //   across a same-origin iframe boundary.
     77        //
     78        // [1] https://w3c.github.io/pointerevents/#setting-pointer-capture
     79    }
     80  }
     81 
     82  // Only log the first pointermove event after pointerdown. We need to account
     83  // for coalesced pointermove events and for the pointermove events that
     84  // happen after lostpointercapture.
     85  if (start_logging && (event.type !== "pointermove" || !first_pointermove_happened))
     86    receivedEventList.push(event.target.id + ' received ' + event.type);
     87 
     88  if (event.type == "pointermove"){
     89    if (document.releasePointerCaptureOnFirstMove && event.target.hasPointerCapture(event.pointerId))
     90        event.target.releasePointerCapture(event.pointerId);
     91    if(start_logging)
     92        first_pointermove_happened = true;
     93  }
     94 };
     95 
     96 document.testEventList = ['pointerup', 'pointerdown', 'pointermove', 'gotpointercapture', 'lostpointercapture'];
     97 document.testEventList.forEach(function(eventName) {
     98  document.getElementById('outerFrame').addEventListener(eventName, handleEvent);
     99 });
    100 
    101 function Reset() {
    102  document.captureTargetOverride = null;
    103  document.releasePointerCaptureOnFirstMove = false;
    104  receivedEventList = [];
    105  start_logging = false;
    106  first_pointermove_happened = false;
    107 }
    108 
    109 function run() {
    110    Reset();
    111    var pointerType = location.search.substring(1);
    112 
    113    promise_test (async(t) => {
    114        t.add_cleanup(Reset);
    115        expectedEventList = ["innerFrame received pointerdown",
    116                             "innerFrame received gotpointercapture",
    117                             "innerFrame received pointermove",
    118                             "innerFrame received pointerup",
    119                             "innerFrame received lostpointercapture"];
    120        var pointerId = pointerType + "Pointer1";
    121 
    122        var innerFrameDocument =  frames[0].document;
    123        // We are interested in tracking events only after pointerdown
    124        var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);});
    125        var watcher_promise = pointerdown_happened.then(()=>{
    126            var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["lostpointercapture"], eventTimeout());
    127            return watch_inner_frame.wait_for(["lostpointercapture"]);
    128        });
    129 
    130        await new test_driver.Actions()
    131                             .addPointer(pointerId, pointerType)
    132                             .pointerMove(200, 200)
    133                             .pointerDown()
    134                             .pointerMove(150, 150)
    135                             .pointerMove(50, 50)
    136                             .pointerUp()
    137                             .send();
    138        // Wait for lostpointercapture to fire.
    139        await watcher_promise;
    140        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    141    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at inner frame and set pointer capture.");
    142 
    143    promise_test (async(t) => {
    144        t.add_cleanup(Reset);
    145        document.captureTargetOverride = document.getElementById('outerFrame');
    146        expectedEventList = ["innerFrame received pointerdown",
    147                             "innerFrame received pointermove",
    148                             "innerFrame received pointerup"];
    149        var pointerId = pointerType + "Pointer1";
    150 
    151        var innerFrameDocument =  frames[0].document;
    152        // We are interested in tracking events only after pointerdown
    153        var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);});
    154 
    155        var watcher_promise = pointerdown_happened.then(()=>{
    156            var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout());
    157            return watch_inner_frame.wait_for(["pointerup"]);
    158        });
    159 
    160        await new test_driver.Actions()
    161                             .addPointer(pointerId, pointerType)
    162                             .pointerMove(200, 200)
    163                             .pointerDown()
    164                             .pointerMove(150, 150)
    165                             .pointerUp()
    166                             .send();
    167        // Interestingly, a drag out of the iframe behaves consitently between
    168        // Chrome, Firefox and Safari even though this is not spec-ed: the
    169        // `pointerup` always goes to innerFrameDocument.
    170 
    171        // Wait for pointerup to fire.
    172        await watcher_promise;
    173        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    174    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at inner frame and set pointer capture to outer frame should not capture.");
    175 
    176    promise_test (async(t) => {
    177        t.add_cleanup(Reset);
    178        expectedEventList = ["outerFrame received pointerdown",
    179                             "outerFrame received gotpointercapture",
    180                             "outerFrame received pointermove",
    181                             "outerFrame received pointerup",
    182                             "outerFrame received lostpointercapture"];
    183        var pointerId = pointerType + "Pointer1";
    184 
    185        // We are interested in tracking events only after pointerdown
    186        var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);});
    187        var watcher_promise = pointerdown_happened.then(()=>{
    188            // For this test we're going to wait for both pointerup and lostpointercapture,
    189            // as pointerup fires on innerFrame on Windows (see crbug.com/1186788)
    190            var watch_outer_frame_for_pointerup_and_lostpointercapture =
    191              new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup", "lostpointercapture"], eventTimeout());
    192            return watch_outer_frame_for_pointerup_and_lostpointercapture.wait_for(["pointerup", "lostpointercapture"]);
    193        });
    194 
    195        await new test_driver.Actions()
    196                             .addPointer(pointerId, pointerType)
    197                             .pointerMove(25, 25)
    198                             .pointerDown()
    199                             .pointerMove(200, 200)
    200                             .pointerUp()
    201                             .send();
    202        // Wait for pointerup or lostpointercapture to fire.
    203        await watcher_promise;
    204        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    205    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at outer frame body and set pointer capture.");
    206 
    207    promise_test (async(t) => {
    208        t.add_cleanup(Reset);
    209        document.captureTargetOverride = frames[0].document.body;
    210        expectedEventList = ["outerFrame received pointerdown",
    211                             "outerFrame received pointermove",
    212                             "outerFrame received pointerup"];
    213        var pointerId = pointerType + "Pointer1";
    214 
    215        // We are interested in tracking events only after pointerdown
    216        var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);});
    217        var watcher_promise = pointerdown_happened.then(()=>{
    218            var watch_outer_frame = new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup"], eventTimeout());
    219            return watch_outer_frame.wait_for("pointerup");
    220        });
    221 
    222        await new test_driver.Actions()
    223                             .addPointer(pointerId, pointerType)
    224                             .pointerMove(25, 25)
    225                             .pointerDown()
    226                             .pointerMove(75, 75)
    227                             .pointerUp()
    228                             .send();
    229        // Wait for pointerup to fire.
    230        await watcher_promise;
    231        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    232    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointer down at outer frame body and set pointer capture in inner frame should not capture.");
    233 
    234    promise_test (async(t) => {
    235        t.add_cleanup(Reset);
    236        document.releasePointerCaptureOnFirstMove = true;
    237        // Mouse event has the frame capture, so after pointer capture released, events are
    238        // dispatched to innerFrameDocument.
    239        expectedEventList = ["innerFrame received pointerdown",
    240                             "innerFrame received gotpointercapture",
    241                             "innerFrame received pointermove",
    242                             "innerFrame received lostpointercapture",
    243                             (pointerType == "touch" ? "outerFrame": "innerFrameDocument") + " received pointerup",];
    244        var pointerId = pointerType + "Pointer1";
    245 
    246        var innerFrameDocument =  frames[0].document;
    247        // We are interested in tracking events only after pointerdown
    248        var pointerdown_happened = new Promise((resolve, reject)=>{innerFrameDocument.addEventListener("pointerdown",resolve);});
    249        var watcher_promise = pointerdown_happened.then(()=>{
    250            if(pointerType === "touch"){
    251                var watch_outer_frame = new EventWatcher(t, document.getElementById('outerFrame'), ["pointerup"], eventTimeout());
    252                return watch_outer_frame.wait_for("pointerup");
    253            }else{
    254                var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout());
    255                return watch_inner_frame.wait_for("pointerup");
    256            }
    257        });
    258 
    259        await new test_driver.Actions()
    260                             .addPointer(pointerId, pointerType)
    261                             .pointerMove(200, 200)
    262                             .pointerDown()
    263                             .pointerMove(150, 150)
    264                             .pointerMove(50, 50)
    265                             .pointerUp()
    266                             .send();
    267        // Wait for pointerup to fire.
    268        await watcher_promise;
    269        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    270        document.releasePointerCaptureOnFirstMove = false;
    271    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at inner frame, then release on next pointermove.");
    272 
    273    promise_test (async(t) => {
    274        t.add_cleanup(Reset);
    275        document.releasePointerCaptureOnFirstMove = true;
    276        expectedEventList = ["outerFrame received pointerdown",
    277                             "outerFrame received gotpointercapture",
    278                             "outerFrame received pointermove",
    279                             "outerFrame received lostpointercapture",
    280                             "innerFrame received pointerup"];
    281        var pointerId = pointerType + "Pointer1";
    282 
    283        var innerFrameDocument =  frames[0].document;
    284        // We are interested in tracking events only after pointerdown
    285        var pointerdown_happened = new Promise((resolve, reject)=>{document.getElementById('outerFrame').addEventListener("pointerdown",resolve);});
    286        var watcher_promise = pointerdown_happened.then(()=>{
    287            var watch_inner_frame = new EventWatcher(t, innerFrameDocument, ["pointerup"], eventTimeout());
    288            return watch_inner_frame.wait_for(["pointerup"]);
    289        });
    290 
    291        await new test_driver.Actions()
    292                             .addPointer(pointerId, pointerType)
    293                             .pointerMove(50, 50)
    294                             .pointerDown()
    295                             .pointerMove(200, 200)
    296                             // Pause here to make sure that the previous and following pointer moves
    297                             // are not coalesced. If they are coalesced, we will not see the second
    298                             // move event which is when the pending lostpointercapture should be fired
    299                             // (Since the pointerup event is targeted at a different frame, it won't dispatch
    300                             // the pending lostpointercapture event).
    301                             .pause(300)
    302                             .pointerMove(250, 250)
    303                             .pointerUp()
    304                             .send();
    305        // Wait for pointerup to fire.
    306        await watcher_promise;
    307        assert_array_equals(receivedEventList, expectedEventList, "Received events: " + receivedEventList);
    308        document.releasePointerCaptureOnFirstMove = false;
    309    }, "Test " + pointerType + "pointer capture in same-origin frame: Pointerdown with set capture at outer frame, then release on next pointermove.");
    310 }
    311 </script>
    312 
    313 </body>