browser_fetch_after_suspending_request.js (6913B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 add_task(async function () { 7 info("Run test with network.http.rcwn.enabled set to true"); 8 await test_fetch_after_suspend(true); 9 info("Run test with network.http.rcwn.enabled set to false"); 10 await test_fetch_after_suspend(false); 11 }); 12 13 let wrappedChannel; 14 15 async function test_fetch_after_suspend(rcwnEnabled) { 16 info("Set network.http.rcwn.enabled to " + rcwnEnabled); 17 await SpecialPowers.pushPrefEnv({ 18 set: [ 19 ["network.http.rcwn.enabled", rcwnEnabled], 20 ["network.cache.suspended_writer_delay_ms", 300], 21 ], 22 }); 23 24 info("Add a new test tab"); 25 const tab = BrowserTestUtils.addTab( 26 gBrowser, 27 "https://example.com/document-builder.sjs?html=tab" 28 ); 29 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 30 31 const testBlockedUrl = "https://example.com/?test-blocked"; 32 33 info(`Add an observer to suspend the next channel to ${testBlockedUrl}`); 34 const { promise, resolve } = Promise.withResolvers(); 35 const onExamineResponse = subject => { 36 if (!(subject instanceof Ci.nsIHttpChannel)) { 37 return; 38 } 39 40 const channel = subject.QueryInterface(Ci.nsIHttpChannel); 41 if (channel.URI.displaySpec !== testBlockedUrl) { 42 return; 43 } 44 45 wrappedChannel = ChannelWrapper.get(channel); 46 wrappedChannel.suspend("test-blocked-suspend"); 47 Services.obs.removeObserver(onExamineResponse, "http-on-examine-response"); 48 resolve(); 49 }; 50 Services.obs.addObserver(onExamineResponse, "http-on-examine-response"); 51 52 info(`Send fetch call for ${testBlockedUrl}`); 53 let first = fetch(tab.linkedBrowser, testBlockedUrl); 54 55 info( 56 "Wait for the fetch request to be suspended in http-on-examine-response" 57 ); 58 await promise; 59 60 info("Fetch the same URL again"); 61 let secondCompleted = false; 62 let second = fetch(tab.linkedBrowser, testBlockedUrl).then(() => { 63 secondCompleted = true; 64 }); 65 66 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 67 let timer = new Promise(resolve => setTimeout(resolve, 6000)); 68 await Promise.race([second, timer]); 69 70 Assert.equal( 71 secondCompleted, 72 true, 73 "The second fetch should resolve successfully" 74 ); 75 76 // Resume the first channel and await its completion so we don't leak anything. 77 wrappedChannel.resume(); 78 await first; 79 80 info("Cleanup"); 81 gBrowser.removeTab(tab); 82 } 83 84 function fetch(browser, url) { 85 return SpecialPowers.spawn(browser, [url], async _url => { 86 const response = await content.fetch(_url); 87 await response.text(); 88 }); 89 } 90 91 add_task(async function test_fetch_after_suspended_timer_fires() { 92 await SpecialPowers.pushPrefEnv({ 93 set: [ 94 ["network.http.rcwn.enabled", false], 95 ["network.cache.suspended_writer_delay_ms", 300], 96 ], 97 }); 98 99 info("Add a new test tab"); 100 const tab = BrowserTestUtils.addTab( 101 gBrowser, 102 "https://example.com/document-builder.sjs?html=tab" 103 ); 104 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 105 106 const testBlockedUrl = "https://example.com/?test-blocked"; 107 108 info(`Add an observer to suspend the next channel to ${testBlockedUrl}`); 109 const { promise, resolve } = Promise.withResolvers(); 110 const onExamineResponse = subject => { 111 if (!(subject instanceof Ci.nsIHttpChannel)) { 112 return; 113 } 114 115 const channel = subject.QueryInterface(Ci.nsIHttpChannel); 116 if (channel.URI.displaySpec !== testBlockedUrl) { 117 return; 118 } 119 120 wrappedChannel = ChannelWrapper.get(channel); 121 wrappedChannel.suspend("test-blocked-suspend"); 122 Services.obs.removeObserver(onExamineResponse, "http-on-examine-response"); 123 resolve(); 124 }; 125 Services.obs.addObserver(onExamineResponse, "http-on-examine-response"); 126 127 info(`Send fetch call for ${testBlockedUrl}`); 128 let first = fetch(tab.linkedBrowser, testBlockedUrl); 129 130 info( 131 "Wait for the fetch request to be suspended in http-on-examine-response" 132 ); 133 await promise; 134 135 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 136 await new Promise(resolve => setTimeout(resolve, 500)); 137 138 info("Fetch the same URL again"); 139 let secondCompleted = false; 140 let second = fetch(tab.linkedBrowser, testBlockedUrl).then(() => { 141 secondCompleted = true; 142 }); 143 144 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 145 let timer = new Promise(resolve => setTimeout(resolve, 2000)); 146 await Promise.race([second, timer]); 147 148 Assert.equal( 149 secondCompleted, 150 true, 151 "The second fetch should resolve successfully" 152 ); 153 154 // Resume the first channel and await its completion so we don't leak anything. 155 wrappedChannel.resume(); 156 await first; 157 158 info("Cleanup"); 159 gBrowser.removeTab(tab); 160 }); 161 162 add_task(async function test_fetch_after_suspended_and_resumed() { 163 await SpecialPowers.pushPrefEnv({ 164 set: [ 165 ["network.http.rcwn.enabled", false], 166 ["network.cache.suspended_writer_delay_ms", 1000], 167 ], 168 }); 169 170 info("Add a new test tab"); 171 const tab = BrowserTestUtils.addTab( 172 gBrowser, 173 "https://example.com/document-builder.sjs?html=tab" 174 ); 175 await BrowserTestUtils.browserLoaded(tab.linkedBrowser); 176 177 const testBlockedUrl = "https://example.com/?test-blocked"; 178 179 info(`Add an observer to suspend the next channel to ${testBlockedUrl}`); 180 const { promise, resolve } = Promise.withResolvers(); 181 const onExamineResponse = subject => { 182 if (!(subject instanceof Ci.nsIHttpChannel)) { 183 return; 184 } 185 186 const channel = subject.QueryInterface(Ci.nsIHttpChannel); 187 if (channel.URI.displaySpec !== testBlockedUrl) { 188 return; 189 } 190 191 wrappedChannel = ChannelWrapper.get(channel); 192 wrappedChannel.suspend("test-blocked-suspend"); 193 Services.obs.removeObserver(onExamineResponse, "http-on-examine-response"); 194 resolve(); 195 }; 196 Services.obs.addObserver(onExamineResponse, "http-on-examine-response"); 197 198 info(`Send fetch call for ${testBlockedUrl}`); 199 let first = fetch(tab.linkedBrowser, testBlockedUrl); 200 201 info( 202 "Wait for the fetch request to be suspended in http-on-examine-response" 203 ); 204 await promise; 205 206 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 207 await new Promise(resolve => setTimeout(resolve, 100)); 208 209 // Resume the channel to make sure we cancel timer 210 wrappedChannel.resume(); 211 212 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 213 await new Promise(resolve => setTimeout(resolve, 1500)); 214 215 info("Fetch the same URL again"); 216 let secondCompleted = false; 217 let second = fetch(tab.linkedBrowser, testBlockedUrl).then(() => { 218 secondCompleted = true; 219 }); 220 221 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout 222 let timer = new Promise(resolve => setTimeout(resolve, 2000)); 223 await Promise.race([second, timer]); 224 225 Assert.equal( 226 secondCompleted, 227 true, 228 "The second fetch should resolve successfully" 229 ); 230 await first; 231 232 info("Cleanup"); 233 gBrowser.removeTab(tab); 234 });