mouseover-utils.js (3901B)
1 let counter = 0; 2 const loadImage = size => { 3 return event => { 4 let zoom; 5 if (location.search.includes("replace")) { 6 zoom = document.getElementById("image"); 7 } else { 8 zoom = new Image(); 9 } 10 zoom.src=`/images/lcp-${size}.png`; 11 ++counter; 12 zoom.elementTiming = "zoom" + counter; 13 document.body.appendChild(zoom); 14 } 15 }; 16 const loadBackgroundImage = size => { 17 return event => { 18 const div = document.createElement("div"); 19 const [width, height] = size.split("x"); 20 ++counter; 21 div.style = `background-image: 22 url(/images/lcp-${size}.png?${counter}); width: ${width}px; height: ${height}px`; 23 div.elementTiming = "zoom" + counter; 24 document.body.appendChild(div); 25 } 26 }; 27 28 const registerMouseover = background => { 29 const image = document.getElementById("image"); 30 const span = document.getElementById("span"); 31 const func = background ? loadBackgroundImage : loadImage; 32 image.addEventListener("mouseover", func("100x50")); 33 span.addEventListener("mouseover", func("256x256")); 34 } 35 36 const dispatch_mouseover = () => { 37 span.dispatchEvent(new Event("mouseover")) 38 }; 39 40 const wait_for_lcp_entries = async entries_expected => { 41 await new Promise(resolve => { 42 let entries_seen = 0; 43 const PO = new PerformanceObserver(list => { 44 const entries = list.getEntries(); 45 for (let entry of entries) { 46 if (entry.url) { 47 entries_seen++; 48 } 49 } 50 if (entries_seen == entries_expected) { 51 PO.disconnect(); 52 resolve() 53 } else if (entries_seen > entries_expected) { 54 PO.disconnect(); 55 reject(); 56 } 57 }); 58 PO.observe({type: "largest-contentful-paint", buffered: true}); 59 }); 60 }; 61 const wait_for_element_timing_entry = async identifier => { 62 await new Promise(resolve => { 63 const PO = new PerformanceObserver(list => { 64 const entries = list.getEntries(); 65 for (let entry of entries) { 66 if (entry.identifier == identifier) { 67 PO.disconnect(); 68 resolve() 69 } 70 } 71 }); 72 PO.observe({type: "element", buffered: true}); 73 }); 74 }; 75 const wait_for_resource_timing_entry = async name => { 76 await new Promise(resolve => { 77 const PO = new PerformanceObserver(list => { 78 const entries = list.getEntries(); 79 for (let entry of entries) { 80 if (entry.name.includes(name)) { 81 PO.disconnect(); 82 resolve() 83 } 84 } 85 }); 86 PO.observe({type: "resource", buffered: true}); 87 }); 88 }; 89 90 const run_mouseover_test = background => { 91 promise_test(async t => { 92 // await the first LCP entry 93 await wait_for_lcp_entries(1); 94 // Hover over the image 95 registerMouseover(background); 96 if (test_driver) { 97 await new test_driver.Actions().pointerMove(0, 0, {origin: image}).send(); 98 } 99 if (!background) { 100 await wait_for_element_timing_entry("zoom1"); 101 } else { 102 await wait_for_resource_timing_entry("png?1"); 103 await new Promise(r => requestAnimationFrame(r)); 104 } 105 // There's only a single LCP entry, because the zoom was skipped. 106 await wait_for_lcp_entries(1); 107 108 // Wait 600 ms as the heuristic is 500 ms. 109 // This will no longer be necessary once the heuristic relies on Task 110 // Attribution. 111 await new Promise(r => step_timeout(r, 600)); 112 113 // Hover over the span. 114 if (test_driver) { 115 await new test_driver.Actions().pointerMove(0, 0, {origin: span}).send(); 116 } 117 if (!background) { 118 await wait_for_element_timing_entry("zoom2"); 119 } else { 120 await wait_for_resource_timing_entry("png?2"); 121 await new Promise(r => requestAnimationFrame(r)); 122 } 123 // There are 2 LCP entries, as the image loaded due to span hover is a 124 // valid LCP candidate. 125 await wait_for_lcp_entries(2); 126 }, `LCP mouseover heuristics ignore ${background ? 127 "background" : "element"}-based zoom widgets`); 128 }