Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ export class CRNetworkManager {
if (this._page) {
sessionInfo.eventListeners.push(...[
eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page!.frameManager.onWebSocketCreated(e.requestId, e.url)),
eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page!.frameManager.onWebSocketRequest(e.requestId)),
eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page!.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page!.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page!.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page!.frameManager.onWebSocketRequest(e.requestId, headersObjectToArray(e.request.headers, '\n'), e.wallTime, e.timestamp)),
eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page!.frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText, headersObjectToArray(e.response.headers, '\n'))),
eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page!.frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData, e.timestamp)),
eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page!.frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData, e.timestamp)),
eventsHelper.addEventListener(session, 'Network.webSocketClosed', e => this._page!.frameManager.webSocketClosed(e.requestId)),
eventsHelper.addEventListener(session, 'Network.webSocketFrameError', e => this._page!.frameManager.webSocketError(e.requestId, e.errorMessage)),
]);
Expand Down
37 changes: 27 additions & 10 deletions packages/playwright-core/src/server/firefox/ffNetworkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@ import { eventsHelper } from '@utils/eventsHelper';
import * as network from '../network';

import type { FFSession } from './ffConnection';
import type { FFPage } from './ffPage';
import type { HeadersArray } from '../../server/types';
import type { RegisteredListener } from '@utils/eventsHelper';
import type * as frames from '../frames';
import type { Page } from '../page';
import type * as types from '../types';
import type { Protocol } from './protocol';

export class FFNetworkManager {
private _session: FFSession;
private _requests: Map<string, InterceptableRequest>;
private _page: Page;
private _page: FFPage;
private _eventListeners: RegisteredListener[];
private _webSocketRequestIds = new Set<string>();

constructor(session: FFSession, page: Page) {
constructor(session: FFSession, page: FFPage) {
this._session = session;

this._requests = new Map();
Expand All @@ -59,26 +60,36 @@ export class FFNetworkManager {

_onRequestWillBeSent(event: Protocol.Network.requestWillBeSentPayload) {
const redirectedFrom = event.redirectedFrom ? (this._requests.get(event.redirectedFrom) || null) : null;
const frame = redirectedFrom ? redirectedFrom.request.frame() : (event.frameId ? this._page.frameManager.frame(event.frameId) : null);
const frame = redirectedFrom ? redirectedFrom.request.frame() : (event.frameId ? this._page._page.frameManager.frame(event.frameId) : null);
if (!frame)
return;
// Align with Chromium and WebKit and not expose preflight OPTIONS requests to the client.
if (event.method === 'OPTIONS' && !event.isIntercepted)
return;
if (redirectedFrom)
this._requests.delete(redirectedFrom._id);
// Align with Chromium and WebKit by having WebSocket be handled separately from other network activity.
if (event.cause === 'TYPE_WEBSOCKET') {
this._webSocketRequestIds.add(event.requestId);
this._page._onWebSocketRequestWillBeSent(event.requestId, event.url, event.headers);
return;
}
const request = new InterceptableRequest(frame, redirectedFrom, event);
let route;
if (event.isIntercepted)
route = new FFRouteImpl(this._session, request);
this._requests.set(request._id, request);
this._page.frameManager.requestStarted(request.request, route);
this._page._page.frameManager.requestStarted(request.request, route);
}

_onResponseReceived(event: Protocol.Network.responseReceivedPayload) {
const request = this._requests.get(event.requestId);
if (!request)
if (!request) {
// Align with Chromium and WebKit by having WebSocket be handled separately from other network activity.
if (this._webSocketRequestIds.has(event.requestId))
this._page._onWebSocketResponseReceived(event.requestId, event.status, event.statusText, event.headers);
return;
}
const getResponseBody = async () => {
const response = await this._session.send('Network.getResponseBody', {
requestId: request._id
Expand Down Expand Up @@ -124,13 +135,19 @@ export class FFNetworkManager {
response.setRawResponseHeaders(null);
// Headers size are not available in Firefox.
response.setResponseHeadersSize(null);
this._page.frameManager.requestReceivedResponse(response);
this._page._page.frameManager.requestReceivedResponse(response);
}

_onRequestFinished(event: Protocol.Network.requestFinishedPayload) {
const request = this._requests.get(event.requestId);
if (!request)
if (!request) {
// Align with Chromium and WebKit by having WebSocket be handled separately from other network activity.
if (this._webSocketRequestIds.has(event.requestId)) {
this._webSocketRequestIds.delete(event.requestId);
this._page._onWebSocketRequestFinished(event.requestId);
}
return;
}
const response = request.request._existingResponse()!;
response.setTransferSize(event.transferSize);
response.setEncodedBodySize(event.encodedBodySize);
Expand All @@ -145,7 +162,7 @@ export class FFNetworkManager {
response._requestFinished(responseEndTime);
}
response._setHttpVersion(event.protocolVersion ?? null);
this._page.frameManager.reportRequestFinished(request.request, response);
this._page._page.frameManager.reportRequestFinished(request.request, response);
}

_onRequestFailed(event: Protocol.Network.requestFailedPayload) {
Expand All @@ -161,7 +178,7 @@ export class FFNetworkManager {
response._setHttpVersion(null);
}
request.request._setFailureText(event.errorCode);
this._page.frameManager.requestFailed(request.request, event.errorCode === 'NS_BINDING_ABORTED');
this._page._page.frameManager.requestFailed(request.request, event.errorCode === 'NS_BINDING_ABORTED');
}
}

Expand Down
57 changes: 53 additions & 4 deletions packages/playwright-core/src/server/firefox/ffPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
* limitations under the License.
*/

import { assert } from '@isomorphic/assert';
import { splitErrorMessage } from '@isomorphic/stackTrace';
import { eventsHelper } from '@utils/eventsHelper';
import * as dialog from '../dialog';
import * as dom from '../dom';
import * as network from '../network';
import { InitScript } from '../page';
import { Page, Worker } from '../page';
import { FFSession } from './ffConnection';
Expand Down Expand Up @@ -53,6 +55,8 @@ export class FFPage implements PageDelegate {
private _eventListeners: RegisteredListener[];
private _workers = new Map<string, { frameId: string, session: FFSession }>();
private _initScripts: { initScript: InitScript, worldName?: string }[] = [];
private _webSocketRequests = new Map<string, { url: string, headers: types.HeadersArray }>();
private _webSocketResponses = new Map<string, { status: number, statusText: string, headers: types.HeadersArray }>();

constructor(session: FFSession, browserContext: FFBrowserContext, opener: FFPage | null) {
this._session = session;
Expand All @@ -64,7 +68,7 @@ export class FFPage implements PageDelegate {
this._browserContext = browserContext;
this._page = new Page(this, browserContext);
this.rawMouse.setPage(this._page);
this._networkManager = new FFNetworkManager(session, this._page);
this._networkManager = new FFNetworkManager(session, this);
this._page.on(Page.Events.FrameDetached, frame => this._removeContextsForFrame(frame));
// TODO: remove Page.willOpenNewWindowAsynchronously from the protocol.
this._eventListeners = [
Expand All @@ -90,6 +94,7 @@ export class FFPage implements PageDelegate {
eventsHelper.addEventListener(this._session, 'Page.crashed', this._onCrashed.bind(this)),

eventsHelper.addEventListener(this._session, 'Page.webSocketCreated', this._onWebSocketCreated.bind(this)),
eventsHelper.addEventListener(this._session, 'Page.webSocketOpened', this._onWebSocketOpened.bind(this)),
eventsHelper.addEventListener(this._session, 'Page.webSocketClosed', this._onWebSocketClosed.bind(this)),
eventsHelper.addEventListener(this._session, 'Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)),
eventsHelper.addEventListener(this._session, 'Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)),
Expand Down Expand Up @@ -119,7 +124,51 @@ export class FFPage implements PageDelegate {

_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));
}

_onWebSocketRequestWillBeSent(requestId: string, url: string, headers: types.HeadersArray) {
this._webSocketRequests.set(requestId, { url, headers });
}

_onWebSocketResponseReceived(requestId: string, status: number, statusText: string, headers: types.HeadersArray) {
this._webSocketResponses.set(requestId, { status, statusText, headers });
}

_onWebSocketRequestFinished(requestId: string) {
const response = this._webSocketResponses.get(requestId);
assert(response);
// If the request does not succeed then the WebSocket will never open, so pretend that it did.
if (response.status >= 400) {
const request = this._webSocketRequests.get(requestId);
assert(request);

this._webSocketRequests.delete(requestId);
this._webSocketResponses.delete(requestId);

const url = network.parseURL(request.url);
assert(url);
url.protocol = url.protocol === 'https' ? 'wss' : 'ws';

this._page.frameManager.onWebSocketCreated(requestId, url.toString());
this._page.frameManager.onWebSocketRequest(requestId, request.headers);
this._page.frameManager.onWebSocketResponse(requestId, response.status, response.statusText, response.headers);
this._page.frameManager.webSocketClosed(requestId);
return;
}
}

_onWebSocketOpened(event: Protocol.Page.webSocketOpenedPayload) {
const request = this._webSocketRequests.get(event.requestId);
assert(request);

const response = this._webSocketResponses.get(event.requestId);
assert(response);

this._webSocketRequests.delete(event.requestId);
this._webSocketResponses.delete(event.requestId);

this._page.frameManager.onWebSocketRequest(webSocketId(event.frameId, event.wsid), request.headers);
this._page.frameManager.onWebSocketResponse(webSocketId(event.frameId, event.wsid), response.status, response.statusText, response.headers);
}

_onWebSocketClosed(event: Protocol.Page.webSocketClosedPayload) {
Expand All @@ -129,11 +178,11 @@ export class FFPage implements PageDelegate {
}

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

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

_onExecutionContextCreated(payload: Protocol.Runtime.executionContextCreatedPayload) {
Expand Down
2 changes: 2 additions & 0 deletions packages/playwright-core/src/server/firefox/protocol.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,12 +486,14 @@ export namespace Protocol {
wsid: string;
opcode: number;
data: string;
timestamp: number;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file is regenerated during firefox rolls, no manual changes please

}
export type webSocketFrameReceivedPayload = {
frameId: string;
wsid: string;
opcode: number;
data: string;
timestamp: number;
}
export type screencastFramePayload = {
data: string;
Expand Down
35 changes: 24 additions & 11 deletions packages/playwright-core/src/server/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,43 +403,56 @@ export class FrameManager {
this._webSockets.set(requestId, ws);
}

onWebSocketRequest(requestId: string) {
onWebSocketRequest(requestId: string, headers: types.HeadersArray, wallTime?: number, timestamp?: number) {
const ws = this._webSockets.get(requestId);
if (ws && ws.markAsNotified())
if (!ws)
return;

if (ws.markAsNotified())
this._page.emit(Page.Events.WebSocket, ws);

ws.requestSent(headers, wallTime, timestamp);
}

onWebSocketResponse(requestId: string, status: number, statusText: string) {
onWebSocketResponse(requestId: string, status: number, statusText: string, headers: types.HeadersArray) {
const ws = this._webSockets.get(requestId);
if (status < 400)
if (!ws)
return;
if (ws)

ws.responseReceived(status, statusText, headers);
if (status >= 400)
ws.error(`${statusText}: ${status}`);
}

onWebSocketFrameSent(requestId: string, opcode: number, data: string) {
onWebSocketFrameSent(requestId: string, opcode: number, data: string, timestamp: number) {
const ws = this._webSockets.get(requestId);
if (ws)
ws.frameSent(opcode, data);
ws.frameSent(opcode, data, timestamp);
}

webSocketFrameReceived(requestId: string, opcode: number, data: string) {
webSocketFrameReceived(requestId: string, opcode: number, data: string, timestamp: number) {
const ws = this._webSockets.get(requestId);
if (ws)
ws.frameReceived(opcode, data);
ws.frameReceived(opcode, data, timestamp);
}

webSocketClosed(requestId: string) {
const ws = this._webSockets.get(requestId);
if (ws)
if (ws) {
if (ws.markAsNotified())
this._page.emit(Page.Events.WebSocket, ws);
ws.closed();
}
this._webSockets.delete(requestId);
}

webSocketError(requestId: string, errorMessage: string): void {
const ws = this._webSockets.get(requestId);
if (ws)
if (ws) {
if (ws.markAsNotified())
this._page.emit(Page.Events.WebSocket, ws);
ws.error(errorMessage);
}
}

private _fireInternalFrameNavigation(frame: Frame, event: NavigationEvent) {
Expand Down
Loading
Loading