Skip to content

Commit 18c3efe

Browse files
authored
browser(firefox): instrument websockets (#4287)
1 parent 914f637 commit 18c3efe

File tree

5 files changed

+150
-11
lines changed

5 files changed

+150
-11
lines changed

browser_patches/firefox/BUILD_NUMBER

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
1197
2-
Changed: [email protected] Wed 28 Oct 2020 02:43:11 PM PDT
1+
1198
2+
Changed: [email protected] Thu 29 Oct 2020 04:23:02 PM PDT

browser_patches/firefox/juggler/content/FrameTree.js

Lines changed: 109 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class FrameTree {
2424
this._browsingContextGroup.__jugglerFrameTrees.add(this);
2525
this._scriptsToEvaluateOnNewDocument = new Map();
2626

27+
this._webSocketEventService = Cc[
28+
"@mozilla.org/websocketevent/service;1"
29+
].getService(Ci.nsIWebSocketEventService);
30+
2731
this._bindings = new Map();
2832
this._runtime = new Runtime(false /* isWorker */);
2933
this._workers = new Map();
@@ -200,7 +204,7 @@ class FrameTree {
200204

201205
if (isStart) {
202206
// Starting a new navigation.
203-
frame._pendingNavigationId = this._channelId(channel);
207+
frame._pendingNavigationId = channelId(channel);
204208
frame._pendingNavigationURL = channel.URI.spec;
205209
this.emit(FrameTree.Events.NavigationStarted, frame);
206210
} else if (isTransferring || (isStop && frame._pendingNavigationId && !status)) {
@@ -241,14 +245,6 @@ class FrameTree {
241245
}
242246
}
243247

244-
_channelId(channel) {
245-
if (channel instanceof Ci.nsIIdentChannel) {
246-
const identChannel = channel.QueryInterface(Ci.nsIIdentChannel);
247-
return String(identChannel.channelId);
248-
}
249-
return helper.generateId();
250-
}
251-
252248
_onDocShellCreated(docShell) {
253249
// Bug 1142752: sometimes, the docshell appears to be immediately
254250
// destroyed, bailout early to prevent random exceptions.
@@ -302,6 +298,11 @@ FrameTree.Events = {
302298
GlobalObjectCreated: 'globalobjectcreated',
303299
WorkerCreated: 'workercreated',
304300
WorkerDestroyed: 'workerdestroyed',
301+
WebSocketCreated: 'websocketcreated',
302+
WebSocketOpened: 'websocketopened',
303+
WebSocketClosed: 'websocketclosed',
304+
WebSocketFrameReceived: 'websocketframereceived',
305+
WebSocketFrameSent: 'websocketframesent',
305306
NavigationStarted: 'navigationstarted',
306307
NavigationCommitted: 'navigationcommitted',
307308
NavigationAborted: 'navigationaborted',
@@ -332,6 +333,90 @@ class Frame {
332333

333334
this._textInputProcessor = null;
334335
this._executionContext = null;
336+
337+
this._webSocketListenerInnerWindowId = 0;
338+
// WebSocketListener calls frameReceived event before webSocketOpened.
339+
// To avoid this, serialize event reporting.
340+
this._webSocketInfos = new Map();
341+
342+
const dispatchWebSocketFrameReceived = (webSocketSerialID, frame) => this._frameTree.emit(FrameTree.Events.WebSocketFrameReceived, {
343+
frameId: this._frameId,
344+
wsid: webSocketSerialID + '',
345+
opcode: frame.opCode,
346+
data: frame.opCode !== 1 ? btoa(frame.payload) : frame.payload,
347+
});
348+
this._webSocketListener = {
349+
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebSocketEventListener, ]),
350+
351+
webSocketCreated: (webSocketSerialID, uri, protocols) => {
352+
this._frameTree.emit(FrameTree.Events.WebSocketCreated, {
353+
frameId: this._frameId,
354+
wsid: webSocketSerialID + '',
355+
requestURL: uri,
356+
});
357+
this._webSocketInfos.set(webSocketSerialID, {
358+
opened: false,
359+
pendingIncomingFrames: [],
360+
});
361+
},
362+
363+
webSocketOpened: (webSocketSerialID, effectiveURI, protocols, extensions, httpChannelId) => {
364+
this._frameTree.emit(FrameTree.Events.WebSocketOpened, {
365+
frameId: this._frameId,
366+
requestId: httpChannelId + '',
367+
wsid: webSocketSerialID + '',
368+
effectiveURL: effectiveURI,
369+
});
370+
const info = this._webSocketInfos.get(webSocketSerialID);
371+
info.opened = true;
372+
for (const frame of info.pendingIncomingFrames)
373+
dispatchWebSocketFrameReceived(webSocketSerialID, frame);
374+
},
375+
376+
webSocketMessageAvailable: (webSocketSerialID, data, messageType) => {
377+
// We don't use this event.
378+
},
379+
380+
webSocketClosed: (webSocketSerialID, wasClean, code, reason) => {
381+
this._webSocketInfos.delete(webSocketSerialID);
382+
let error = '';
383+
if (!wasClean) {
384+
const keys = Object.keys(Ci.nsIWebSocketChannel);
385+
for (const key of keys) {
386+
if (Ci.nsIWebSocketChannel[key] === code)
387+
error = key;
388+
}
389+
}
390+
this._frameTree.emit(FrameTree.Events.WebSocketClosed, {
391+
frameId: this._frameId,
392+
wsid: webSocketSerialID + '',
393+
error,
394+
});
395+
},
396+
397+
frameReceived: (webSocketSerialID, frame) => {
398+
// Report only text and binary frames.
399+
if (frame.opCode !== 1 && frame.opCode !== 2)
400+
return;
401+
const info = this._webSocketInfos.get(webSocketSerialID);
402+
if (info.opened)
403+
dispatchWebSocketFrameReceived(webSocketSerialID, frame);
404+
else
405+
info.pendingIncomingFrames.push(frame);
406+
},
407+
408+
frameSent: (webSocketSerialID, frame) => {
409+
// Report only text and binary frames.
410+
if (frame.opCode !== 1 && frame.opCode !== 2)
411+
return;
412+
this._frameTree.emit(FrameTree.Events.WebSocketFrameSent, {
413+
frameId: this._frameId,
414+
wsid: webSocketSerialID + '',
415+
opcode: frame.opCode,
416+
data: frame.opCode !== 1 ? btoa(frame.payload) : frame.payload,
417+
});
418+
},
419+
};
335420
}
336421

337422
dispose() {
@@ -354,6 +439,12 @@ class Frame {
354439
}
355440

356441
_onGlobalObjectCleared() {
442+
const webSocketService = this._frameTree._webSocketEventService;
443+
if (this._webSocketListenerInnerWindowId)
444+
webSocketService.removeListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
445+
this._webSocketListenerInnerWindowId = this.domWindow().windowGlobalChild.innerWindowId;
446+
webSocketService.addListener(this._webSocketListenerInnerWindowId, this._webSocketListener);
447+
357448
if (this._executionContext)
358449
this._runtime.destroyExecutionContext(this._executionContext);
359450
this._executionContext = this._runtime.createExecutionContext(this.domWindow(), this.domWindow(), {
@@ -473,6 +564,15 @@ class Worker {
473564
}
474565
}
475566

567+
function channelId(channel) {
568+
if (channel instanceof Ci.nsIIdentChannel) {
569+
const identChannel = channel.QueryInterface(Ci.nsIIdentChannel);
570+
return String(identChannel.channelId);
571+
}
572+
return helper.generateId();
573+
}
574+
575+
476576
var EXPORTED_SYMBOLS = ['FrameTree'];
477577
this.FrameTree = FrameTree;
478578

browser_patches/firefox/juggler/content/PageAgent.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ class PageAgent {
177177
helper.on(this._frameTree, 'pageready', () => this._browserPage.emit('pageReady', {})),
178178
helper.on(this._frameTree, 'workercreated', this._onWorkerCreated.bind(this)),
179179
helper.on(this._frameTree, 'workerdestroyed', this._onWorkerDestroyed.bind(this)),
180+
helper.on(this._frameTree, 'websocketcreated', event => this._browserPage.emit('webSocketCreated', event)),
181+
helper.on(this._frameTree, 'websocketopened', event => this._browserPage.emit('webSocketOpened', event)),
182+
helper.on(this._frameTree, 'websocketframesent', event => this._browserPage.emit('webSocketFrameSent', event)),
183+
helper.on(this._frameTree, 'websocketframereceived', event => this._browserPage.emit('webSocketFrameReceived', event)),
184+
helper.on(this._frameTree, 'websocketclosed', event => this._browserPage.emit('webSocketClosed', event)),
180185
helper.addObserver(this._onWindowOpen.bind(this), 'webNavigation-createdNavigationTarget-from-js'),
181186
this._runtime.events.onErrorFromWorker((domWindow, message, stack) => {
182187
const frame = this._frameTree.frameForDocShell(domWindow.docShell);

browser_patches/firefox/juggler/protocol/PageHandler.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ class PageHandler {
110110
runtimeConsole: emitProtocolEvent('Runtime.console'),
111111
runtimeExecutionContextCreated: emitProtocolEvent('Runtime.executionContextCreated'),
112112
runtimeExecutionContextDestroyed: emitProtocolEvent('Runtime.executionContextDestroyed'),
113+
114+
webSocketCreated: emitProtocolEvent('Page.webSocketCreated'),
115+
webSocketOpened: emitProtocolEvent('Page.webSocketOpened'),
116+
webSocketClosed: emitProtocolEvent('Page.webSocketClosed'),
117+
webSocketFrameReceived: emitProtocolEvent('Page.webSocketFrameReceived'),
118+
webSocketFrameSent: emitProtocolEvent('Page.webSocketFrameSent'),
113119
}),
114120
];
115121
}

browser_patches/firefox/juggler/protocol/Protocol.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,34 @@ const Page = {
668668
screencastId: t.String,
669669
file: t.String,
670670
},
671+
'webSocketCreated': {
672+
frameId: t.String,
673+
wsid: t.String,
674+
requestURL: t.String,
675+
},
676+
'webSocketOpened': {
677+
frameId: t.String,
678+
requestId: t.String,
679+
wsid: t.String,
680+
effectiveURL: t.String,
681+
},
682+
'webSocketClosed': {
683+
frameId: t.String,
684+
wsid: t.String,
685+
error: t.String,
686+
},
687+
'webSocketFrameSent': {
688+
frameId: t.String,
689+
wsid: t.String,
690+
opcode: t.Number,
691+
data: t.String,
692+
},
693+
'webSocketFrameReceived': {
694+
frameId: t.String,
695+
wsid: t.String,
696+
opcode: t.Number,
697+
data: t.String,
698+
},
671699
},
672700

673701
methods: {

0 commit comments

Comments
 (0)