browser_resources_websocket.js (6884B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test the ResourceCommand API around WEBSOCKET. 7 8 const ResourceCommand = require("resource://devtools/shared/commands/resource/resource-command.js"); 9 10 const IS_NUMBER = "IS_NUMBER"; 11 const SHOULD_EXIST = "SHOULD_EXIST"; 12 13 const targets = { 14 TOP_LEVEL_DOCUMENT: "top-level-document", 15 IN_PROCESS_IFRAME: "in-process-frame", 16 OUT_PROCESS_IFRAME: "out-process-frame", 17 }; 18 19 add_task(async function () { 20 info("Testing the top-level document"); 21 await testWebsocketResources(targets.TOP_LEVEL_DOCUMENT); 22 info("Testing the in-process iframe"); 23 await testWebsocketResources(targets.IN_PROCESS_IFRAME); 24 info("Testing the out-of-process iframe"); 25 await testWebsocketResources(targets.OUT_PROCESS_IFRAME); 26 }); 27 28 async function testWebsocketResources(target) { 29 const tab = await addTab(URL_ROOT_SSL + "websocket_frontend.html"); 30 const { client, resourceCommand, targetCommand } = 31 await initResourceCommand(tab); 32 33 const availableResources = []; 34 function onResourceAvailable(resources) { 35 availableResources.push(...resources); 36 } 37 38 await resourceCommand.watchResources([resourceCommand.TYPES.WEBSOCKET], { 39 onAvailable: onResourceAvailable, 40 }); 41 42 info("Check available resources at initial"); 43 is( 44 availableResources.length, 45 0, 46 "Length of existing resources is correct at initial" 47 ); 48 49 info("Check resource of opening websocket"); 50 await executeFunctionInContext(tab, target, "openConnection"); 51 52 await waitUntil(() => availableResources.length === 1); 53 54 const httpChannelId = availableResources[0].httpChannelId; 55 56 ok(httpChannelId, "httpChannelId is present in the resource"); 57 58 assertResource(availableResources[0], { 59 wsMessageType: "webSocketOpened", 60 effectiveURI: 61 "wss://example.com/browser/devtools/shared/commands/resource/tests/websocket_backend", 62 extensions: "permessage-deflate", 63 protocols: "", 64 }); 65 66 info("Check resource of sending/receiving the data via websocket"); 67 await executeFunctionInContext(tab, target, "sendData", "test"); 68 69 await waitUntil(() => availableResources.length === 3); 70 71 assertResource(availableResources[1], { 72 wsMessageType: "frameSent", 73 httpChannelId, 74 data: { 75 type: "sent", 76 payload: "test", 77 timeStamp: SHOULD_EXIST, 78 finBit: SHOULD_EXIST, 79 rsvBit1: SHOULD_EXIST, 80 rsvBit2: SHOULD_EXIST, 81 rsvBit3: SHOULD_EXIST, 82 opCode: SHOULD_EXIST, 83 mask: SHOULD_EXIST, 84 maskBit: SHOULD_EXIST, 85 }, 86 }); 87 assertResource(availableResources[2], { 88 wsMessageType: "frameReceived", 89 httpChannelId, 90 data: { 91 type: "received", 92 payload: "test", 93 timeStamp: SHOULD_EXIST, 94 finBit: SHOULD_EXIST, 95 rsvBit1: SHOULD_EXIST, 96 rsvBit2: SHOULD_EXIST, 97 rsvBit3: SHOULD_EXIST, 98 opCode: SHOULD_EXIST, 99 mask: SHOULD_EXIST, 100 maskBit: SHOULD_EXIST, 101 }, 102 }); 103 104 info("Check resource of closing websocket"); 105 await executeFunctionInContext(tab, target, "closeConnection"); 106 107 await waitUntil(() => availableResources.length === 6); 108 assertResource(availableResources[3], { 109 wsMessageType: "frameSent", 110 httpChannelId, 111 data: { 112 type: "sent", 113 payload: "", 114 timeStamp: SHOULD_EXIST, 115 finBit: SHOULD_EXIST, 116 rsvBit1: SHOULD_EXIST, 117 rsvBit2: SHOULD_EXIST, 118 rsvBit3: SHOULD_EXIST, 119 opCode: SHOULD_EXIST, 120 mask: SHOULD_EXIST, 121 maskBit: SHOULD_EXIST, 122 }, 123 }); 124 assertResource(availableResources[4], { 125 wsMessageType: "frameReceived", 126 httpChannelId, 127 data: { 128 type: "received", 129 payload: "", 130 timeStamp: SHOULD_EXIST, 131 finBit: SHOULD_EXIST, 132 rsvBit1: SHOULD_EXIST, 133 rsvBit2: SHOULD_EXIST, 134 rsvBit3: SHOULD_EXIST, 135 opCode: SHOULD_EXIST, 136 mask: SHOULD_EXIST, 137 maskBit: SHOULD_EXIST, 138 }, 139 }); 140 assertResource(availableResources[5], { 141 wsMessageType: "webSocketClosed", 142 httpChannelId, 143 code: IS_NUMBER, 144 reason: "", 145 wasClean: true, 146 }); 147 148 info("Check existing resources"); 149 const existingResources = []; 150 151 function onExsistingResourceAvailable(resources) { 152 existingResources.push(...resources); 153 } 154 155 await resourceCommand.watchResources([resourceCommand.TYPES.WEBSOCKET], { 156 onAvailable: onExsistingResourceAvailable, 157 }); 158 159 is( 160 availableResources.length, 161 existingResources.length, 162 "Length of existing resources is correct" 163 ); 164 165 for (let i = 0; i < availableResources.length; i++) { 166 Assert.strictEqual( 167 availableResources[i], 168 existingResources[i], 169 `The ${i}th resource is correct` 170 ); 171 } 172 173 await resourceCommand.unwatchResources([resourceCommand.TYPES.WEBSOCKET], { 174 onAvailable: onResourceAvailable, 175 }); 176 177 await resourceCommand.unwatchResources([resourceCommand.TYPES.WEBSOCKET], { 178 onAvailable: onExsistingResourceAvailable, 179 }); 180 181 targetCommand.destroy(); 182 await client.close(); 183 BrowserTestUtils.removeTab(tab); 184 } 185 186 /** 187 * Execute global functions defined in the correct 188 * target (top-level-window or frames) contexts. 189 * 190 * @param {object} tab The current window tab 191 * @param {string} target A string identify if we want to test the top level document or iframes 192 * @param {string} funcName The name of the global function which needs to be called. 193 * @param {*} funcArgs The arguments to pass to the global function 194 */ 195 async function executeFunctionInContext(tab, target, funcName, ...funcArgs) { 196 let browsingContext = tab.linkedBrowser.browsingContext; 197 // If the target is an iframe get its window global 198 if (target !== targets.TOP_LEVEL_DOCUMENT) { 199 browsingContext = await SpecialPowers.spawn( 200 tab.linkedBrowser, 201 [target], 202 async _target => { 203 const iframe = content.document.getElementById(_target); 204 return iframe.browsingContext; 205 } 206 ); 207 } 208 209 return SpecialPowers.spawn( 210 browsingContext, 211 [funcName, funcArgs], 212 async (_funcName, _funcArgs) => { 213 await content.wrappedJSObject[_funcName](..._funcArgs); 214 } 215 ); 216 } 217 218 function assertResource(resource, expected) { 219 is( 220 resource.resourceType, 221 ResourceCommand.TYPES.WEBSOCKET, 222 "Resource type is correct" 223 ); 224 225 assertObject(resource, expected); 226 } 227 228 function assertObject(object, expected) { 229 for (const field in expected) { 230 if (typeof expected[field] === "object") { 231 assertObject(object[field], expected[field]); 232 } else if (expected[field] === SHOULD_EXIST) { 233 Assert.notStrictEqual( 234 object[field], 235 undefined, 236 `The value of ${field} exists` 237 ); 238 } else if (expected[field] === IS_NUMBER) { 239 ok(!isNaN(object[field]), `The value of ${field} is number`); 240 } else { 241 is(object[field], expected[field], `The value of ${field} is correct`); 242 } 243 } 244 }