Skip to content

Commit

Permalink
feat(firefox): support WebSockets on Firefox (#4289)
Browse files Browse the repository at this point in the history
  • Loading branch information
aslushnikov authored Oct 30, 2020
1 parent 333916a commit 7fbbd18
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 17 deletions.
2 changes: 1 addition & 1 deletion browsers.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
{
"name": "firefox",
"revision": "1197",
"revision": "1198",
"download": true
},
{
Expand Down
28 changes: 28 additions & 0 deletions src/server/firefox/ffPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ export class FFPage implements PageDelegate {
helper.addEventListener(this._session, 'Page.dispatchMessageFromWorker', this._onDispatchMessageFromWorker.bind(this)),
helper.addEventListener(this._session, 'Page.crashed', this._onCrashed.bind(this)),
helper.addEventListener(this._session, 'Page.screencastStarted', this._onScreencastStarted.bind(this)),

helper.addEventListener(this._session, 'Page.webSocketCreated', this._onWebSocketCreated.bind(this)),
helper.addEventListener(this._session, 'Page.webSocketClosed', this._onWebSocketClosed.bind(this)),
helper.addEventListener(this._session, 'Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)),
helper.addEventListener(this._session, 'Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)),
];
this._pagePromise = new Promise(f => this._pageCallback = f);
session.once(FFSessionEvents.Disconnected, () => this._page._didDisconnect());
Expand All @@ -100,6 +105,25 @@ export class FFPage implements PageDelegate {
return this._pagePromise;
}

_onWebSocketCreated(event: Protocol.Page.webSocketCreatedPayload) {
this._page._frameManager.onWebSocketCreated(webSocketId(event.frameId, event.wsid), event.requestURL);
this._page._frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid));
}

_onWebSocketClosed(event: Protocol.Page.webSocketClosedPayload) {
if (event.error)
this._page._frameManager.webSocketError(webSocketId(event.frameId, event.wsid), event.error);
this._page._frameManager.webSocketClosed(webSocketId(event.frameId, event.wsid));
}

_onWebSocketFrameReceived(event: Protocol.Page.webSocketFrameReceivedPayload) {
this._page._frameManager.webSocketFrameReceived(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
}

_onWebSocketFrameSent(event: Protocol.Page.webSocketFrameSentPayload) {
this._page._frameManager.onWebSocketFrameSent(webSocketId(event.frameId, event.wsid), event.opcode, event.data);
}

_onExecutionContextCreated(payload: Protocol.Runtime.executionContextCreatedPayload) {
const {executionContextId, auxData} = payload;
const frame = this._page._frameManager.frame(auxData ? auxData.frameId : null);
Expand Down Expand Up @@ -500,3 +524,7 @@ export class FFPage implements PageDelegate {
return result.handle;
}
}

function webSocketId(frameId: string, wsid: string): string {
return `${frameId}---${wsid}`;
}
33 changes: 33 additions & 0 deletions src/server/firefox/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,34 @@ export module Protocol {
screencastId: string;
file: string;
}
export type webSocketCreatedPayload = {
frameId: string;
wsid: string;
requestURL: string;
}
export type webSocketOpenedPayload = {
frameId: string;
requestId: string;
wsid: string;
effectiveURL: string;
}
export type webSocketClosedPayload = {
frameId: string;
wsid: string;
error: string;
}
export type webSocketFrameSentPayload = {
frameId: string;
wsid: string;
opcode: number;
data: string;
}
export type webSocketFrameReceivedPayload = {
frameId: string;
wsid: string;
opcode: number;
data: string;
}
export type closeParameters = {
runBeforeUnload?: boolean;
};
Expand Down Expand Up @@ -979,6 +1007,11 @@ export module Protocol {
"Page.workerDestroyed": Page.workerDestroyedPayload;
"Page.dispatchMessageFromWorker": Page.dispatchMessageFromWorkerPayload;
"Page.screencastStarted": Page.screencastStartedPayload;
"Page.webSocketCreated": Page.webSocketCreatedPayload;
"Page.webSocketOpened": Page.webSocketOpenedPayload;
"Page.webSocketClosed": Page.webSocketClosedPayload;
"Page.webSocketFrameSent": Page.webSocketFrameSentPayload;
"Page.webSocketFrameReceived": Page.webSocketFrameReceivedPayload;
"Runtime.executionContextCreated": Runtime.executionContextCreatedPayload;
"Runtime.executionContextDestroyed": Runtime.executionContextDestroyedPayload;
"Runtime.console": Runtime.consolePayload;
Expand Down
12 changes: 1 addition & 11 deletions test/checkCoverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@ if (browserName !== 'chromium') {
api.delete('cDPSession.detach');
}

if (browserName === 'firefox') {
// WebSockets on FF are work in progress.
api.delete('webSocket.url');
api.delete('webSocket.emit("close")');
api.delete('webSocket.emit("socketerror")');
api.delete('webSocket.emit("framereceived")');
api.delete('webSocket.emit("framesent")');
api.delete('page.emit("websocket")');
}

// Some permissions tests are disabled in webkit. See permissions.jest.js
if (browserName === 'webkit')
api.delete('browserContext.clearPermissions');
Expand Down Expand Up @@ -77,4 +67,4 @@ function * getCoverageFiles(dir) {
else
yield path.join(dir, entry.name);
}
}
}
15 changes: 10 additions & 5 deletions test/web-socket.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import { it, describe, expect } from './fixtures';

describe('web socket', (test, { browserName }) => {
test.fixme(browserName === 'firefox');
}, () => {
it('should work', async ({ page, server }) => {
const value = await page.evaluate(port => {
Expand Down Expand Up @@ -47,7 +46,7 @@ describe('web socket', (test, { browserName }) => {
expect(log.join(':')).toBe(`open<ws://localhost:${server.PORT}/ws>:close`);
});

it('should emit frame events', async ({ page, server }) => {
it('should emit frame events', async ({ page, server, isFirefox }) => {
let socketClosed;
const socketClosePromise = new Promise(f => socketClosed = f);
const log = [];
Expand All @@ -63,7 +62,10 @@ describe('web socket', (test, { browserName }) => {
ws.addEventListener('message', () => { ws.close(); });
}, server.PORT);
await socketClosePromise;
expect(log.join(':')).toBe('open:sent<outgoing>:received<incoming>:close');
if (isFirefox)
expect(log.join(':')).toBe('open:received<incoming>:sent<outgoing>:close');
else
expect(log.join(':')).toBe('open:sent<outgoing>:received<incoming>:close');
});

it('should emit binary frame events', async ({ page, server }) => {
Expand Down Expand Up @@ -91,14 +93,17 @@ describe('web socket', (test, { browserName }) => {
expect(sent[1][i]).toBe(i);
});

it('should emit error', async ({page, server}) => {
it('should emit error', async ({page, server, isFirefox}) => {
let callback;
const result = new Promise(f => callback = f);
page.on('websocket', ws => ws.on('socketerror', callback));
page.evaluate(port => {
new WebSocket('ws://localhost:' + port + '/bogus-ws');
}, server.PORT);
const message = await result;
expect(message).toContain(': 400');
if (isFirefox)
expect(message).toBe('CLOSE_ABNORMAL');
else
expect(message).toContain(': 400');
});
});

0 comments on commit 7fbbd18

Please sign in to comment.