task-attribution.html (5850B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>Soft Navigation Detection: Task Attribution.</title> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <script src="/resources/testdriver.js"></script> 9 <script src="/resources/testdriver-vendor.js"></script> 10 <script> 11 // Appends a new div with a greeting to the document body. 12 function updateUI() { 13 const greeting = document.createElement("div"); 14 greeting.textContent = "Hello, World."; 15 document.body.appendChild(greeting); 16 } 17 18 // Basic soft navigation (see basic.html) which directly does its work 19 // in the click handler. This is our baseline - the methods below 20 // differ in that they require propagating the soft navigation attribution 21 // to a different task. 22 function noSchedulingHop() { 23 updateUI(); 24 history.pushState({}, "", "/no-scheduling-hop"); 25 } 26 27 // A soft navigation where the click handler schedules its work with 28 // setTimeout, 0ms delay. 29 // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout 30 function withSetTimeout0() { 31 setTimeout(() => { 32 updateUI(); 33 history.pushState({}, "", "/with-set-timeout-0"); 34 }, 0); 35 } 36 37 // A soft navigation where the click handler schedules its work with 38 // setTimeout, 10ms delay. 39 // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout 40 function withSetTimeout10() { 41 setTimeout(() => { 42 updateUI(); 43 history.pushState({}, "", "/with-set-timeout-10"); 44 }, 10); 45 } 46 47 // A soft navigation where the click handler schedules its work with 48 // two setTimeouts. 49 // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout 50 function twoSetTimeouts() { 51 setTimeout(updateUI, 20); 52 setTimeout(() => { 53 history.pushState({}, "", "/two-set-timeouts"); 54 }, 10); 55 } 56 57 // A soft navigation where the click handler schedules its UI work with 58 // setTimeout and the URL update with a sync history.pushState call. 59 // https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout 60 function setTimeoutAndSyncUrlUpdate() { 61 setTimeout(updateUI, 10); 62 history.pushState({}, "", "/set-timeout-and-sync-url-update"); 63 } 64 65 // A soft navigation where the click handler schedules its work with 66 // scheduler.postTask. 67 // https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/postTask 68 function withSchedulerPostTask() { 69 scheduler.postTask(() => { 70 updateUI(); 71 history.pushState({}, "", "/with-scheduler-post-task"); 72 }); 73 } 74 75 // A soft navigation where the click handler yields 76 // (using scheduler.yield) between URL and UI update. 77 async function withSchedulerYield() { 78 history.pushState({}, "", "/with-scheduler-yield"); 79 await scheduler.yield(); 80 updateUI(); 81 } 82 83 // A soft navigation where the click handler schedules its work with 84 // requestAnimationFrame. 85 // https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame 86 function withRequestAnimationFrame() { 87 requestAnimationFrame(() => { 88 updateUI(); 89 history.pushState({}, "", "/with-request-animation-frame"); 90 }); 91 } 92 </script> 93 </head> 94 <body> 95 <div id="no-scheduling-hop" onclick="noSchedulingHop()">Click here!</div> 96 <div id="with-set-timeout-0" onclick="withSetTimeout0()">Click here!</div> 97 <div id="with-set-timeout-10" onclick="withSetTimeout10()">Click here!</div> 98 <div id="two-set-timeouts" onclick="twoSetTimeouts()">Click here!</div> 99 <div id="set-timeout-and-sync-url-update" onclick="setTimeoutAndSyncUrlUpdate()"> 100 Click here! 101 </div> 102 <div id="with-scheduler-post-task" onclick="withSchedulerPostTask()">Click here!</div> 103 <div id="with-scheduler-yield" onclick="withSchedulerYield()">Click here!</div> 104 <div id="with-request-animation-frame" onclick="withRequestAnimationFrame()">Click here!</div> 105 106 <script> 107 function test_template(test_id, description) { 108 promise_test(async (t) => { 109 let entries; 110 new PerformanceObserver((list, observer) => { 111 entries = list.getEntries(); 112 observer.disconnect(); 113 }).observe({ type: "soft-navigation" }); 114 if (test_driver) { 115 test_driver.click(document.getElementById(test_id)); 116 } 117 await t.step_wait(() => entries !== undefined, "Soft navigation event not fired."); 118 119 assert_equals(entries.length, 1, "Expected exactly one soft navigation."); 120 assert_equals( 121 entries[0].name.replace(/.*\//, ""), 122 test_id, 123 "URL should end with the test ID.", 124 ); 125 }, description); 126 } 127 128 test_template("no-scheduling-hop", "No scheduling hop."); 129 test_template("with-set-timeout-0", "With set_timeout, 0ms delay."); 130 test_template("with-set-timeout-10", "With set_timeout, 10ms delay."); 131 test_template("two-set-timeouts", "With two set_timeouts."); 132 test_template("set-timeout-and-sync-url-update", "With set_timeout and sync URL update."); 133 if (scheduler.postTask) { 134 // Skip test if scheduler.postTask is not supported. 135 test_template("with-scheduler-post-task", "With scheduler.postTask."); 136 } 137 if (scheduler.yield) { 138 // Skip test if scheduler.yield is not supported. 139 test_template("with-scheduler-yield", "With scheduler.yield."); 140 } 141 test_template("with-request-animation-frame", "With requestAnimationFrame."); 142 </script> 143 </body> 144 </html>