test_urgent_start.html (8818B)
1 <!DOCTYPE HTML> 2 <!-- 3 https://bugzilla.mozilla.org/show_bug.cgi?id=1348050 4 Test for fetch and xhr to guarantee we only mark channel as urgent-start when 5 it is triggered by user input events. 6 7 For { Fetch, SRC-*, XHR }, do the test as following: 8 Step 1: Verify them not mark the channel when there is no any input event. 9 Step 2: Verify them mark the channel there is a user input event. 10 Step 3: Verify them not mark the channel when there is a non input event. 11 12 In each steps, it shows that we only mark channel on direct triggering task. 13 We won't mark the channel for additional task(setTimeout) or 14 micro-task(promise). 15 --> 16 <html> 17 <head> 18 <title>Test for urgent-start on Fetch and XHR</title> 19 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 20 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 21 <link rel="stylesheet" 22 type="text/css" 23 href="chrome://mochikit/content/tests/SimpleTest/test.css"> 24 <script type="text/javascript" src="manifest.js"></script> 25 </head> 26 <body> 27 <img id="image"></body> 28 <audio autoplay id="audio"></audio> 29 <iframe id="iframe"></iframe> 30 <input type="image" id="input"></input> 31 <embed id="embed"></embed> 32 <pre id="test"> 33 <script class="testbody" type="text/javascript"> 34 35 SimpleTest.waitForExplicitFinish(); 36 SimpleTest.waitForFocus(runTest); 37 38 const topic_request = "http-on-opening-request"; 39 const topic_response = "http-on-examine-response"; 40 const topic_cachedResponse = "http-on-examine-cached-response"; 41 const scope = "http://mochi.test:8888/chrome/dom/base/test/" 42 const url = scope + "file_empty.html"; 43 44 let expectedResults = []; 45 let testcases = [ 46 "fetch", 47 "src-embed", 48 "src-img", 49 "src-input", 50 "src-media", 51 "xhr", 52 ]; 53 let testcase; 54 55 function isUrgentStart(aClassFlags) { 56 if (!aClassFlags) { 57 return false; 58 } 59 60 const urgentStartFlag = 1 << 6; 61 return !!(urgentStartFlag & aClassFlags); 62 } 63 64 // Test for setTimeout (task) 65 function testSetTimeout() { 66 return new Promise(aResolve => 67 setTimeout(function() { 68 testSimple().then(aResolve); 69 }, 0)); 70 } 71 72 // Test for promise chain (micro-task) 73 function testPromise() { 74 return Promise.resolve().then(testSimple); 75 } 76 77 function testSimple() { 78 let testUrl = url + "?" + expectedResults.length; 79 80 if (testcase == "fetch") { 81 return fetch(testUrl); 82 } else if (testcase == "src-embed") { 83 document.getElementById('embed').src = testUrl; 84 return Promise.resolve(); 85 } else if (testcase == "src-img") { 86 document.getElementById('image').src = testUrl; 87 return Promise.resolve(); 88 } else if (testcase == "src-input") { 89 document.getElementById('input').src = testUrl; 90 return Promise.resolve(); 91 } else if (testcase == "src-media") { 92 document.getElementById('audio').src = testUrl; 93 return Promise.resolve(); 94 } else if (testcase == "xhr") { 95 let xhr = new XMLHttpRequest(); 96 xhr.open("GET", testUrl, true); 97 xhr.send(null); 98 return Promise.resolve(); 99 } 100 101 ok(false, "Shouldn't go here."); 102 return Promise.reject(); 103 } 104 105 function sendRequsetAndCheckUrgentStart(aEventToTest) { 106 info("SendRequsetAndCheckUrgentStart"); 107 108 let promise1, promise2; 109 let promise1_resolve, promise2_resolve; 110 111 function checkUrgentStart(aSubject) { 112 var channel = aSubject.QueryInterface(Ci.nsIChannel); 113 if (!channel.URI.spec.includes(scope) ) { 114 return; 115 } 116 117 info("CheckUrgentStart"); 118 119 let cos = channel.QueryInterface(Ci.nsIClassOfService); 120 121 let expectedResult = expectedResults.shift(); 122 is(isUrgentStart(cos.classFlags), expectedResult, 123 "Expect get: " + expectedResult + ", get: " + 124 isUrgentStart(cos.classFlags) + " in the " + 125 (9 - expectedResults.length) + " test of " + testcase); 126 127 // Make sure we've run the check. 128 promise1_resolve(); 129 } 130 131 // Resolve this after we've gotten response to prevent from sending too many 132 // requests to Necko in a short time. 133 function getResponse(aSubject) { 134 var channel = aSubject.QueryInterface(Ci.nsIChannel); 135 if (!channel.URI.spec.includes(scope) ) { 136 return; 137 } 138 info("GetResponse"); 139 140 promise2_resolve(); 141 } 142 143 SpecialPowers.addObserver(checkUrgentStart, topic_request); 144 SpecialPowers.addObserver(getResponse, topic_response); 145 SpecialPowers.addObserver(getResponse, topic_cachedResponse); 146 147 return Promise.resolve() 148 .then(() => { 149 promise1 = new Promise(aResolve => { promise1_resolve = aResolve; }); 150 promise2 = new Promise(aResolve => { promise2_resolve = aResolve; }); 151 return Promise.all([addListenerAndSendEvent(testSimple, aEventToTest), 152 promise1, 153 promise2]); 154 }) 155 .then(() => { 156 promise1 = new Promise(aResolve => { promise1_resolve = aResolve; }); 157 promise2 = new Promise(aResolve => { promise2_resolve = aResolve; }); 158 return Promise.all([addListenerAndSendEvent(testSetTimeout, aEventToTest), 159 promise1, 160 promise2]); 161 }) 162 .then(() => { 163 promise1 = new Promise(aResolve => { promise1_resolve = aResolve; }); 164 promise2 = new Promise(aResolve => { promise2_resolve = aResolve; }); 165 return Promise.all([addListenerAndSendEvent(testPromise, aEventToTest), 166 promise1, 167 promise2]); 168 }) 169 .then(() => { 170 // remove obs if we've tested each three conditions 171 // (simple, promise, setTimeout). 172 SpecialPowers.removeObserver(checkUrgentStart, topic_request); 173 SpecialPowers.removeObserver(getResponse,topic_response); 174 SpecialPowers.removeObserver(getResponse, topic_cachedResponse); 175 return Promise.resolve(); 176 }); 177 } 178 179 function addListenerAndSendEvent(aFunction, aEventToTest) { 180 info("AddListenerAndSendEvent:" + aEventToTest); 181 182 let eventHandle = function () { 183 return aFunction(); 184 }; 185 186 if (aEventToTest === TestEvent.USER_INPUT_EVENT) { 187 // User Input Event 188 window.addEventListener("mousedown", eventHandle, {once: true}); 189 } else if (aEventToTest === TestEvent.NONUSER_INPUT_EVENT) { 190 window.addEventListener("message", eventHandle, {once: true}); 191 } 192 193 if (aEventToTest === TestEvent.USER_INPUT_EVENT) { 194 // User Input Event 195 synthesizeMouseAtPoint(1, 1, { type: "mousedown" }); 196 } else if (aEventToTest === TestEvent.NONUSER_INPUT_EVENT) { 197 window.postMessage("hello", "*"); 198 } else if (aEventToTest === TestEvent.NOEVENT) { 199 eventHandle(); 200 } 201 } 202 203 const TestEvent = { 204 NOEVENT: 0, 205 USER_INPUT_EVENT: 1, 206 NONUSER_INPUT_EVENT: 2, 207 }; 208 209 function executeTest() { 210 is(expectedResults.length, 0, "expectedResults should be 0 be executeTest."); 211 212 // We will test fetch first and then xhr. 213 testcase = testcases.shift(); 214 info("Verify " + testcase); 215 216 expectedResults = [ 217 /* SimpleTest without any events */ false, 218 /* PromiseTest without any events */ false, 219 /* SetTimeoutTest without any events */ false, 220 /* SimpleTest with a user input event */ true, 221 /* PromiseTest with a user input event */ false, 222 /* SetTimeoutTest with user input event */ false, 223 /* SimpleTest with a non user input event */ false, 224 /* PromiseTest with a non user input event */ false, 225 /* SetTimeoutTest with a non user input event */ false, 226 ]; 227 228 return Promise.resolve() 229 // Verify urgent-start is not set when the request is not triggered by any 230 // events. 231 .then(() => sendRequsetAndCheckUrgentStart(TestEvent.NOEVENT)) 232 233 // Verify urgent-start is set only when the request is triggered by a user 234 // input event. (not for another microtask (e.g. promise-chain) and 235 // task (e.g. setTimeout)). 236 .then(() => sendRequsetAndCheckUrgentStart(TestEvent.USER_INPUT_EVENT)) 237 238 // Verify urgent-start is not set when the request is triggered by a non user 239 // input event. 240 .then(() => sendRequsetAndCheckUrgentStart(TestEvent.NONUSER_INPUT_EVENT)) 241 .then(_ => { 242 if (testcases.length !== 0) { 243 // Run the other test if we still have tests needed to be run. 244 return executeTest(); 245 } 246 247 return Promise.resolve(); 248 }); 249 } 250 251 function endCheck() { 252 info("End Check: make sure that we've done all the tests."); 253 254 is(testcases.length, 0, "All the tests should be executed."); 255 is(expectedResults.length, 0, "All the tests should be executed."); 256 257 return Promise.resolve(); 258 } 259 260 function runTest() {// Disable mixed-content upgrading as this test is expecting an HTTP load 261 return SpecialPowers.pushPrefEnv( 262 {"set": [ 263 ["security.mixed_content.upgrade_display_content", false], 264 ["network.http.rcwn.enabled", false], 265 ]} 266 ) 267 .then(executeTest) 268 .then(endCheck) 269 .catch(aError => ok(false, "Some test failed with error " + aError)) 270 .then(SimpleTest.finish); 271 } 272 </script> 273 </pre> 274 </body> 275 </html>