browser_webconsole_stubs_network_event.js (6717B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { 7 createCommandsForTab, 8 STUBS_UPDATE_ENV, 9 getCleanedPacket, 10 getSerializedPacket, 11 getStubFile, 12 writeStubsToFile, 13 } = require(`${CHROME_URL_ROOT}stub-generator-helpers`); 14 15 const TEST_URI = 16 "https://example.com/browser/devtools/client/webconsole/test/browser/test-network-event.html"; 17 const STUB_FILE = "networkEvent.js"; 18 19 add_task(async function () { 20 const isStubsUpdate = Services.env.get(STUBS_UPDATE_ENV) == "true"; 21 info(`${isStubsUpdate ? "Update" : "Check"} ${STUB_FILE}`); 22 23 const generatedStubs = await generateNetworkEventStubs(); 24 25 if (isStubsUpdate) { 26 await writeStubsToFile(STUB_FILE, generatedStubs, true); 27 ok(true, `${STUB_FILE} was updated`); 28 return; 29 } 30 31 const existingStubs = getStubFile(STUB_FILE); 32 const FAILURE_MSG = 33 "The network event stubs file needs to be updated by running `" + 34 `mach test ${getCurrentTestFilePath()} --headless --setenv WEBCONSOLE_STUBS_UPDATE=true` + 35 "`"; 36 37 if (generatedStubs.size !== existingStubs.stubPackets.size) { 38 ok(false, FAILURE_MSG); 39 return; 40 } 41 42 let failed = false; 43 for (const [key, packet] of generatedStubs) { 44 // const existingPacket = existingStubs.stubPackets.get(key); 45 const packetStr = getSerializedPacket(packet, { 46 sortKeys: true, 47 replaceActorIds: true, 48 }); 49 const existingPacketStr = getSerializedPacket( 50 existingStubs.stubPackets.get(key), 51 { sortKeys: true, replaceActorIds: true } 52 ); 53 is(packetStr, existingPacketStr, `"${key}" packet has expected value`); 54 failed = failed || packetStr !== existingPacketStr; 55 } 56 57 if (failed) { 58 ok(false, FAILURE_MSG); 59 } else { 60 ok(true, "Stubs are up to date"); 61 } 62 }); 63 64 async function generateNetworkEventStubs() { 65 const stubs = new Map(); 66 const tab = await addTab(TEST_URI); 67 const commands = await createCommandsForTab(tab); 68 await commands.targetCommand.startListening(); 69 const resourceCommand = commands.resourceCommand; 70 71 const stacktraces = new Map(); 72 let addNetworkStub = function () {}; 73 let addNetworkUpdateStub = function () {}; 74 75 const onAvailable = resources => { 76 for (const resource of resources) { 77 if (resource.resourceType == resourceCommand.TYPES.NETWORK_EVENT) { 78 if (stacktraces.has(resource.channelId)) { 79 const { stacktraceAvailable, lastFrame } = stacktraces.get( 80 resource.channelId 81 ); 82 resource.cause.stacktraceAvailable = stacktraceAvailable; 83 resource.cause.lastFrame = lastFrame; 84 stacktraces.delete(resource.channelId); 85 } 86 addNetworkStub(resource); 87 continue; 88 } 89 if ( 90 resource.resourceType == resourceCommand.TYPES.NETWORK_EVENT_STACKTRACE 91 ) { 92 stacktraces.set(resource.channelId, resource); 93 } 94 } 95 }; 96 const onUpdated = updates => { 97 for (const { resource, update } of updates) { 98 addNetworkUpdateStub(resource, update); 99 } 100 }; 101 102 await resourceCommand.watchResources( 103 [ 104 resourceCommand.TYPES.NETWORK_EVENT_STACKTRACE, 105 resourceCommand.TYPES.NETWORK_EVENT, 106 ], 107 { 108 onAvailable, 109 onUpdated, 110 } 111 ); 112 113 for (const [key, code] of getCommands()) { 114 const networkEventDone = new Promise(resolve => { 115 addNetworkStub = resource => { 116 stubs.set(key, getCleanedPacket(key, getOrderedResource(resource))); 117 resolve(); 118 }; 119 }); 120 121 const networkEventUpdateDone = new Promise(resolve => { 122 addNetworkUpdateStub = (resource, update) => { 123 const updateKey = `${key} update`; 124 stubs.set(key, getCleanedPacket(key, getOrderedResource(resource))); 125 stubs.set( 126 updateKey, 127 // We cannot ensure the form of the resource, some properties 128 // might be in another order than in the original resource. 129 // Hand-picking only what we need should prevent this. 130 getCleanedPacket(updateKey, getOrderedResource(resource)) 131 ); 132 133 if (update.resourceUpdates.responseEndAvailable) { 134 resolve(); 135 } 136 }; 137 }); 138 139 await SpecialPowers.spawn( 140 gBrowser.selectedBrowser, 141 [code], 142 function (subCode) { 143 const script = content.document.createElement("script"); 144 script.append( 145 content.document.createTextNode( 146 `function triggerPacket() {${subCode}}` 147 ) 148 ); 149 content.document.body.append(script); 150 content.wrappedJSObject.triggerPacket(); 151 script.remove(); 152 } 153 ); 154 await Promise.all([networkEventDone, networkEventUpdateDone]); 155 } 156 resourceCommand.unwatchResources( 157 [ 158 resourceCommand.TYPES.NETWORK_EVENT_STACKTRACE, 159 resourceCommand.TYPES.NETWORK_EVENT, 160 ], 161 { 162 onAvailable, 163 onUpdated, 164 } 165 ); 166 167 await commands.destroy(); 168 169 return stubs; 170 } 171 // Ensures the order of the resource properties 172 function getOrderedResource(resource) { 173 return { 174 resourceType: resource.resourceType, 175 timeStamp: resource.timeStamp, 176 actor: resource.actor, 177 startedDateTime: resource.startedDateTime, 178 method: resource.method, 179 url: resource.url, 180 isXHR: resource.isXHR, 181 cause: resource.cause, 182 httpVersion: resource.httpVersion, 183 status: resource.status, 184 statusText: resource.statusText, 185 headersSize: resource.headersSize, 186 remoteAddress: resource.remoteAddress, 187 remotePort: resource.remotePort, 188 mimeType: resource.mimeType, 189 waitingTime: resource.waitingTime, 190 contentSize: resource.contentSize, 191 transferredSize: resource.transferredSize, 192 timings: resource.timings, 193 private: resource.private, 194 fromCache: resource.fromCache, 195 fromServiceWorker: resource.fromServiceWorker, 196 isThirdPartyTrackingResource: resource.isThirdPartyTrackingResource, 197 referrerPolicy: resource.referrerPolicy, 198 blockedReason: resource.blockedReason, 199 extension: resource.extension, 200 channelId: resource.channelId, 201 totalTime: resource.totalTime, 202 securityState: resource.securityState, 203 responseCache: resource.responseCache, 204 isRacing: resource.isRacing, 205 }; 206 } 207 208 function getCommands() { 209 const networkEvent = new Map(); 210 211 networkEvent.set( 212 "GET request", 213 ` 214 let i = document.createElement("img"); 215 i.src = "/inexistent.html"; 216 ` 217 ); 218 219 networkEvent.set( 220 "XHR GET request", 221 ` 222 const xhr = new XMLHttpRequest(); 223 xhr.open("GET", "/inexistent.html"); 224 xhr.send(); 225 ` 226 ); 227 228 networkEvent.set( 229 "XHR POST request", 230 ` 231 const xhr = new XMLHttpRequest(); 232 xhr.open("POST", "/inexistent.html"); 233 xhr.send(); 234 ` 235 ); 236 return networkEvent; 237 }