helper_bug1695598.html (4572B)
1 <html> 2 <head> 3 <title>Test for bug 1695598</title> 4 <script src="/tests/SimpleTest/EventUtils.js"></script> 5 <script src="/tests/SimpleTest/paint_listener.js"></script> 6 <script type="application/javascript" src="apz_test_utils.js"></script> 7 <script type="application/javascript" src="apz_test_native_event_utils.js"></script> 8 <script type="text/javascript"> 9 let scrollEvents = 100; 10 let i = 0; 11 12 // Scroll points 13 let numscrolls = 0; 14 let last_numscrolls = 0; 15 let start_scrolly = 0; 16 let missed_events = 0; 17 let missed_scroll_updates = 0; 18 19 let utils = SpecialPowers.getDOMWindowUtils(window); 20 21 let timeStamp = document.timeline.currentTime; 22 async function sendScrollEvent() { 23 if (i < scrollEvents) { 24 if (timeStamp == document.timeline.currentTime) { 25 // If we are in a rAF callback at the same time stamp we've already 26 // sent a key event, skip it, otherwise we will not get the 27 // corresponding scroll event for the key event since it will be 28 // coalesced into a single scroll event. 29 window.requestAnimationFrame(sendScrollEvent); 30 return; 31 } 32 timeStamp = document.timeline.currentTime; 33 // Sent a key event in a setTimeout callback so that it will be 34 // processed in between nsRefreshDriver::Tick calls, thus the async 35 // scroll triggered by the key event is going to be processed on the 36 // main-thread before RepaintRequests are processed inside 37 // nsRefreshDriver::Tick, which results there's an async scroll when 38 // RepaintRequests are processed. 39 setTimeout(async () => { 40 window.synthesizeKey("KEY_ArrowDown"); 41 i++; 42 // "apz-repaints-flush" is notified in an early runner of 43 // nsRefreshDriver, which means it will be delivered inside 44 // a nsRefreshDriver::Tick call before rAF callbacks. 45 await promiseOnlyApzControllerFlushedWithoutSetTimeout(); 46 47 // Wait an animationiteration event since animation events are fired 48 // before rAF callbacks and after scroll events so that it's a good 49 // place to tell whether the expected scroll event got fired or not. 50 await promiseOneEvent(document.getElementById("animation"), 51 "animationiteration", null); 52 if (numscrolls == last_numscrolls) { 53 missed_events++; 54 } 55 if (window.scrollY <= start_scrolly) { 56 missed_scroll_updates++; 57 } 58 last_numscrolls = numscrolls; 59 start_scrolly = window.scrollY; 60 window.requestAnimationFrame(sendScrollEvent); 61 }, 0); 62 } else { 63 // There's a race condition even if we got an "apz-repaints-flush" 64 // notification but any scroll event isn't fired and scroll position 65 // isn't updated since the notification was corresponding to a layers 66 // update triggered by the key event above, which means there was no 67 // repaint request corresponding to APZ animation sample in the time 68 // frame. We allow the case here in the half of key events. 69 ok(missed_events < scrollEvents / 2, `missed event firing ${missed_events} times`); 70 ok(missed_scroll_updates < scrollEvents / 2, `missed scroll update ${missed_scroll_updates} times`); 71 endTest(); 72 } 73 } 74 75 async function endTest() { 76 document.removeEventListener("scroll", gotScroll); 77 subtestDone(); 78 } 79 80 function gotScroll() { 81 numscrolls++; 82 } 83 84 function startTest() { 85 document.addEventListener("scroll", gotScroll); 86 window.requestAnimationFrame(sendScrollEvent); 87 } 88 89 if (!isApzEnabled()) { 90 ok(true, "APZ not enabled, skipping test"); 91 subtestDone(); 92 } 93 94 waitUntilApzStable() 95 .then(forceLayerTreeToCompositor) 96 .then(startTest); 97 </script> 98 <style> 99 #content { 100 height: 10000vh; 101 background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px); 102 } 103 @keyframes anim { 104 from { opacity: 0; }; /* To avoid churning this scroll test */ 105 to { opacity: 0; }; 106 } 107 #animation { 108 position: absolute; 109 width: 100px; 110 height: 100px; 111 visibility: hidden; /* for skipping restyles on the main-thread */ 112 animation-name: anim; 113 animation-iteration-count: infinite; 114 animation-duration: 1ms; /* to get an animationiteration event in each tick */ 115 } 116 </style> 117 </head> 118 <body> 119 <div id="animation"></div> 120 <div id="content"> 121 </div> 122 </body> 123 </html>