raf-loop.html (4117B)
1 <!doctype html> 2 <meta charset="utf-8" /> 3 <title> 4 Largest Contentful Paint after soft navigation: requestAnimationFrame can add additional LCP 5 entry. 6 </title> 7 <script src="/resources/testharness.js"></script> 8 <script src="/resources/testharnessreport.js"></script> 9 <script src="/resources/testdriver.js"></script> 10 <script src="/resources/testdriver-vendor.js"></script> 11 <script src="/soft-navigation-heuristics/resources/soft-navigation-test-helper.js"></script> 12 <body> 13 <button id="click-target" onclick="clickHandler()">Click!</button> 14 </body> 15 <script> 16 // The click handler uses a RAF (requestAnimationFrame) loop to defer work, which 17 // ultimately causes as second (and larger) soft LCP entry to be added. 18 // The termination condition for the RAF loop is that the first soft LCP entry 19 // (from clickHandler) has been observed. So, this example guarantees that 20 // there are two soft LCP entries. 21 // 22 // Note: This test could become brittle if performance observer tasks were 23 // deprioritized by the scheduler, while RAF loop iterations were to run 24 // back-to-back; in that case, adding a setTimeout or scheduler.yield into the 25 // RAF loop may become necessary. 26 // See shaseley's comment on crrev.com/c/6658297. 27 function clickHandler() { 28 let smallLcpIssued = false; 29 30 new PerformanceObserver((list, observer) => { 31 if (list.getEntries().some((e) => e.id === "small-lcp")) { 32 smallLcpIssued = true; 33 observer.disconnect(); 34 } 35 }).observe({ type: "interaction-contentful-paint" }); 36 37 document.body.innerHTML = ` 38 <div id='small-lcp'>Hello, world.</div> 39 `; 40 history.pushState({}, "", "/test"); 41 42 function rafLoop() { 43 if (smallLcpIssued) { 44 const div = document.createElement("div"); 45 div.innerHTML = "The quick brown fox jumps over the lazy dog."; 46 div.id = "large-lcp"; 47 document.body.appendChild(div); 48 } else { 49 requestAnimationFrame(rafLoop); 50 } 51 } 52 rafLoop(); 53 } 54 55 promise_test(async (t) => { 56 assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented"); 57 const helper = new SoftNavigationTestHelper(t); 58 const lcpEntries = 59 await helper.getBufferedPerformanceEntriesWithTimeout("largest-contentful-paint"); 60 assert_equals(lcpEntries.length, 1, "There should be only one LCP entry"); 61 assert_equals(lcpEntries[0].id, "click-target", "The first entry should be the button"); 62 63 const promise = Promise.all([ 64 SoftNavigationTestHelper.getPerformanceEntries("soft-navigation"), 65 SoftNavigationTestHelper.getPerformanceEntries( 66 "interaction-contentful-paint", 67 /*minNumEntries=*/ 2, 68 ), 69 ]); 70 if (test_driver) { 71 test_driver.click(document.getElementById("click-target")); 72 } 73 const [softNavigationEntries, softLcpEntries] = await promise; 74 assert_equals( 75 softNavigationEntries.length, 76 1, 77 "There should be only one soft navigation entry", 78 ); 79 assert_equals(softLcpEntries.length, 2, "There should be two soft LCP entries"); 80 assert_equals( 81 softLcpEntries[0].id, 82 "small-lcp", 83 "The first soft LCP entry should be the small text", 84 ); 85 assert_equals( 86 softLcpEntries[1].id, 87 "large-lcp", 88 "The second soft LCP entry should be the large text", 89 ); 90 assert_equals( 91 softNavigationEntries[0].navigationId, 92 softLcpEntries[0].navigationId, 93 "The soft navigation entry should have the same navigation ID as the first soft LCP entry", 94 ); 95 assert_equals( 96 softNavigationEntries[0].navigationId, 97 softLcpEntries[1].navigationId, 98 "The soft navigation entry should have the same navigation ID as the second soft LCP entry", 99 ); 100 assert_not_equals( 101 lcpEntries[0].navigationId, 102 softNavigationEntries[0].navigationId, 103 "The soft navigation entry should have a different navigation ID than the initial (hard) LCP entry", 104 ); 105 }, "Largest Contentful Paint after soft navigation: requestAnimationFrame can add additional LCP entry."); 106 </script>