helper_relative_scroll_smoothness.html (5749B)
1 <!DOCTYPE html> 2 <html> 3 <meta charset="utf-8"> 4 <script src="/tests/SimpleTest/EventUtils.js"></script> 5 <script src="/tests/SimpleTest/NativeKeyCodes.js"></script> 6 <script src="/tests/SimpleTest/paint_listener.js"></script> 7 <script src="apz_test_utils.js"></script> 8 <script src="apz_test_native_event_utils.js"></script> 9 <title>What happens if main thread scrolls?</title> 10 <style> 11 html, body { margin: 0; } 12 13 html { 14 background: 15 repeating-linear-gradient(45deg, transparent 0, transparent 100px, rgba(0,0,0,0.1) 0, rgba(0,0,0,0.1) 200px), 16 repeating-linear-gradient(-45deg, transparent 0, transparent 100px, rgba(0,0,0,0.1) 0, rgba(0,0,0,0.1) 200px), 17 repeating-linear-gradient(to bottom, transparent 0, transparent 500px, rgba(0,0,0,0.4) 0, rgba(0,0,0,0.4) 1000px), 18 repeating-linear-gradient(to bottom, hsl(0, 60%, 80%), hsl(0, 60%, 80%) 200px, hsl(70, 60%, 80%) 0, hsl(70, 60%, 80%) 400px, hsl(140, 60%, 80%) 0, hsl(140, 60%, 80%) 600px, hsl(210, 60%, 80%) 0, hsl(210, 60%, 80%) 800px), 19 white; 20 background-size: 21 283px 283px, 22 283px 283px, 23 100px 1000px, 24 100px 800px; 25 } 26 27 body { 28 height: 10000px; 29 } 30 </style> 31 32 <script> 33 const searchParams = new URLSearchParams(location.search); 34 let strict = searchParams.get("strict") == "true"; 35 36 var intervalId; 37 // Start periodic content expansions after we get a scroll event triggered by 38 // a key press in test() function below, otherwise we may have same scroll 39 // offsets caused by this script before we start scrolling. 40 window.addEventListener("scroll", () => { 41 var offset = 0; 42 var initialBodyHeight = 10000; 43 intervalId = setInterval(() => { 44 // "Add content" at the top. We do this by making the body longer and adjusting the background position. 45 offset += 10; 46 document.documentElement.style.backgroundPosition = `0px ${offset}px`; 47 document.body.style.height = `${initialBodyHeight + offset}px`; 48 49 switch (searchParams.get("scroll-method")) { 50 case "scrollBy": 51 window.scrollBy(0, 10); 52 break; 53 case "scrollTop": 54 document.scrollingElement.scrollTop += 10; 55 break; 56 case "scrollTo": 57 window.scrollTo(0, window.scrollY + 10); 58 break; 59 default: 60 ok(false, "Unsupported scroll method: " + searchParams.get("scroll-method")); 61 break; 62 } 63 64 // Simulate some jank. 65 var freezeDurationInMilliseconds = 100; 66 var startTime = Date.now(); 67 while (Date.now() - startTime < freezeDurationInMilliseconds) {} // eslint-disable-line no-empty 68 }, 300); 69 }, { once: true }); 70 71 72 async function test() { 73 // Once this content starts scrolling, it triggers a 100ms jank every 300ms so 74 // sending arrow down keys for 1500ms will cause some jank. 75 const TEST_DURATION_MS = 1500; 76 const timeAtStart = performance.now(); 77 while (performance.now() - timeAtStart < TEST_DURATION_MS) { 78 switch (searchParams.get("input-type")) { 79 case "key": 80 synthesizeKey("KEY_ArrowDown"); 81 break; 82 case "native-key": { 83 const DownArrowKeyCode = nativeArrowDownKey(); 84 ok(synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, 85 DownArrowKeyCode, {} /* no modifier */, 86 "", ""), 87 "Dispatched an down arrow key event"); 88 break; 89 } 90 case "wheel": 91 await synthesizeNativeWheel(window, 50, 50, 0, -50); 92 break; 93 default: 94 ok(false, "Unsupported input type: " + searchParams.get("input-type")); 95 break; 96 } 97 await promiseFrame(window); 98 } 99 100 // Stop the periodic expansions. 101 clearInterval(intervalId); 102 103 const records = collectSampledScrollOffsets(document.scrollingElement); 104 105 let previousRecord = { scrollOffsetY: 0, sampledTimeStamp: 0 }; 106 let scrollStartTime = null; 107 for (const record of records) { 108 // Ignore offsets before scrolling. 109 if (record.scrollOffsetY == 0) { 110 continue; 111 } 112 // Ignore offsets after TEST_DURATION_MS has elapsed since the 113 // start of scrolling. Note that the sampled timestamps are 114 // in microseconds. 115 if (!scrollStartTime) { 116 scrollStartTime = record.sampledTimeStamp; 117 } else if (((record.sampledTimeStamp - scrollStartTime) / 1000) > TEST_DURATION_MS) { 118 break; 119 } 120 ok( 121 strict 122 ? (record.scrollOffsetY > previousRecord.scrollOffsetY) 123 : (record.scrollOffsetY >= previousRecord.scrollOffsetY), 124 "scroll offset should be " + 125 (strict ? "strictly monotonically increasing " : "nondecreasing ") + 126 "previous offset: " + previousRecord.scrollOffsetY + 127 ", offset: " + record.scrollOffsetY 128 ); 129 ok( 130 record.sampledTimeStamp > previousRecord.sampledTimeStamp, 131 "sampled time stamp should be strictly monotonically increasing " + 132 "previous timestamp: " + previousRecord.sampledTimeStamp + 133 ", timestamp: " + record.sampledTimeStamp 134 ); 135 previousRecord = record; 136 } 137 } 138 139 function isOnChaosMode() { 140 return SpecialPowers.Services.env.get("MOZ_CHAOSMODE"); 141 } 142 143 function startTest() { 144 if (searchParams.get("input-type") == "native-key") { 145 if (getPlatform() != "mac" && getPlatform() != "windows") { 146 ok(true, "Skipping test because native key events are not supported on " + 147 getPlatform()); 148 subtestDone(); 149 return; 150 } else if (getPlatform() == "mac" && isOnChaosMode()) { 151 ok(true, "Skipping native-key tests on verify runs on Mac"); 152 subtestDone(); 153 return; 154 } else if (getPlatform() == "windows") { 155 // Bug 1909721 156 ok(true, "Skipping native-key tests on Windows due to intermittent failures"); 157 subtestDone(); 158 return; 159 } 160 } 161 162 waitUntilApzStable() 163 .then(test) 164 .then(subtestDone, subtestFailed); 165 } 166 startTest(); 167 </script>