Skip to content
Merged
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
6 changes: 6 additions & 0 deletions docs/src/api/class-consolemessage.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ The page that produced this console message, if any.

The text of the console message.

## method: ConsoleMessage.timestamp
* since: v1.59
- returns: <[float]>

The timestamp of the console message in milliseconds since the Unix epoch.

## method: ConsoleMessage.type
* since: v1.8
* langs: js, python
Expand Down
5 changes: 5 additions & 0 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19267,6 +19267,11 @@ export interface ConsoleMessage {
*/
text(): string;

/**
* The timestamp of the console message in milliseconds since the Unix epoch.
*/
timestamp(): number;

type(): "log"|"debug"|"info"|"error"|"warning"|"dir"|"dirxml"|"table"|"trace"|"clear"|"startGroup"|"startGroupCollapsed"|"endGroup"|"assert"|"profile"|"profileEnd"|"count"|"time"|"timeEnd";

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/playwright-core/src/client/consoleMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export class ConsoleMessage implements api.ConsoleMessage {
return this._event.location;
}

timestamp(): number {
return this._event.timestamp;
}

private _inspect() {
return this.text();
}
Expand Down
3 changes: 3 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ scheme.BrowserContextConsoleEvent = tObject({
lineNumber: tInt,
columnNumber: tInt,
}),
timestamp: tFloat,
page: tOptional(tChannel(['Page'])),
worker: tOptional(tChannel(['Worker'])),
});
Expand Down Expand Up @@ -1227,6 +1228,7 @@ scheme.PageConsoleMessagesResult = tObject({
lineNumber: tInt,
columnNumber: tInt,
}),
timestamp: tFloat,
})),
});
scheme.PageEmulateMediaParams = tObject({
Expand Down Expand Up @@ -2604,6 +2606,7 @@ scheme.ElectronApplicationConsoleEvent = tObject({
lineNumber: tInt,
columnNumber: tInt,
}),
timestamp: tFloat,
});
scheme.ElectronApplicationBrowserWindowParams = tObject({
page: tChannel(['Page']),
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/bidi/bidiBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export class BidiBrowserContext extends BrowserContext {
expiry: (c.expires === -1 || c.expires === undefined) ? undefined : Math.round(c.expires),
};
return this._browser._browserSession.send('storage.setCookie',
{ cookie, partition: { type: 'storageKey', userContext: this._browserContextId } });
{ cookie, partition: { type: 'storageKey', userContext: this._browserContextId, sourceOrigin: c.partitionKey } });
});
await Promise.all(promises);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { Page } from '../page';
import type * as types from '../types';
import type { BidiSession } from './bidiConnection';

const REQUEST_BODY_HEADERS = new Set(['content-encoding', 'content-language', 'content-location', 'content-type']);

export class BidiNetworkManager {
private readonly _session: BidiSession;
Expand Down Expand Up @@ -72,6 +73,10 @@ export class BidiNetworkManager {
if (redirectedFrom._originalRequestRoute?._alreadyContinuedHeaders) {
const originalHeaders = fromBidiHeaders(param.request.headers);
headersOverride = network.applyHeadersOverrides(originalHeaders, redirectedFrom._originalRequestRoute._alreadyContinuedHeaders);
// If the redirect turned a POST into a GET request, remove the request body headers,
// corresponding to step 12 of https://fetch.spec.whatwg.org/#http-redirect-fetch.
if (redirectedFrom.request.method() === 'POST' && param.request.method === 'GET')
headersOverride = headersOverride.filter(({ name }) => !REQUEST_BODY_HEADERS.has(name.toLowerCase()));
params = toBidiRequestHeaders(headersOverride);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/bidi/bidiPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export class BidiPage implements PageDelegate {

const callFrame = params.stackTrace?.callFrames[0];
const location = callFrame ?? { url: '', lineNumber: 1, columnNumber: 1 };
this._page.addConsoleMessage(null, entry.method, entry.args.map(arg => createHandle(context, arg)), location);
this._page.addConsoleMessage(null, entry.method, entry.args.map(arg => createHandle(context, arg)), location, undefined, params.timestamp);
}

private async _onFileDialogOpened(params: bidi.Input.FileDialogInfo) {
Expand Down
6 changes: 3 additions & 3 deletions packages/playwright-core/src/server/chromium/crPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ class FrameSession {
session.on('Target.detachedFromTarget', event => this._onDetachedFromTarget(event));
session.on('Runtime.consoleAPICalled', event => {
const args = event.args.map(o => createHandle(worker.existingExecutionContext!, o));
this._page.addConsoleMessage(worker, event.type, args, toConsoleMessageLocation(event.stackTrace));
this._page.addConsoleMessage(worker, event.type, args, toConsoleMessageLocation(event.stackTrace), undefined, event.timestamp);
});
session.on('Runtime.exceptionThrown', exception => this._page.addPageError(exceptionToError(exception.exceptionDetails)));
}
Expand Down Expand Up @@ -799,7 +799,7 @@ class FrameSession {
if (!context)
return;
const values = event.args.map(arg => createHandle(context, arg));
this._page.addConsoleMessage(null, event.type, values, toConsoleMessageLocation(event.stackTrace));
this._page.addConsoleMessage(null, event.type, values, toConsoleMessageLocation(event.stackTrace), undefined, event.timestamp);
}

async _onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) {
Expand Down Expand Up @@ -846,7 +846,7 @@ class FrameSession {
lineNumber: lineNumber || 0,
columnNumber: 0,
};
this._page.addConsoleMessage(null, level, [], location, text);
this._page.addConsoleMessage(null, level, [], location, text, event.entry.timestamp);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class CRServiceWorker extends Worker {
if (!this.existingExecutionContext || process.env.PLAYWRIGHT_DISABLE_SERVICE_WORKER_CONSOLE)
return;
const args = event.args.map(o => createHandle(this.existingExecutionContext!, o));
const message = new ConsoleMessage(null, this, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace));
const message = new ConsoleMessage(null, this, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace), event.timestamp);
this.browserContext.emit(BrowserContext.Events.Console, message);
});

Expand Down
8 changes: 7 additions & 1 deletion packages/playwright-core/src/server/console.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ export class ConsoleMessage {
private _location: ConsoleMessageLocation;
private _page: Page | null;
private _worker: Worker | null;
private _timestamp: number;

constructor(page: Page | null, worker: Worker | null, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
constructor(page: Page | null, worker: Worker | null, type: string, text: string | undefined, args: js.JSHandle[], location: ConsoleMessageLocation, timestamp: number) {
this._page = page;
this._worker = worker;
this._type = type;
this._text = text;
this._args = args;
this._location = location || { url: '', lineNumber: 0, columnNumber: 0 };
this._timestamp = timestamp;
}

page() {
Expand Down Expand Up @@ -60,4 +62,8 @@ export class ConsoleMessage {
location(): ConsoleMessageLocation {
return this._location;
}

timestamp(): number {
return this._timestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
return JSHandleDispatcher.fromJSHandle(jsScope, a);
}),
location: message.location(),
timestamp: message.timestamp(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
type: message.type(),
text: message.text(),
args: message.args().map(a => JSHandleDispatcher.fromJSHandle(this, a)),
location: message.location()
location: message.location(),
timestamp: message.timestamp(),
});
});
}
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/electron/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class ElectronApplication extends SdkObject {
if (!this._nodeExecutionContext)
return;
const args = event.args.map(arg => createHandle(this._nodeExecutionContext!, arg));
const message = new ConsoleMessage(null, null, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace));
const message = new ConsoleMessage(null, null, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace), event.timestamp);
this.emit(ElectronApplication.Events.Console, message);
}

Expand Down
5 changes: 3 additions & 2 deletions packages/playwright-core/src/server/firefox/ffPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,9 @@ export class FFPage implements PageDelegate {
const context = this._contextIdToContext.get(executionContextId);
if (!context)
return;
const timestamp = Date.now();
// Juggler reports 'warn' for some internal messages generated by the browser.
this._page.addConsoleMessage(null, type === 'warn' ? 'warning' : type, args.map(arg => createHandle(context, arg)), location);
this._page.addConsoleMessage(null, type === 'warn' ? 'warning' : type, args.map(arg => createHandle(context, arg)), location, undefined, timestamp);
}

_onDialogOpened(params: Protocol.Page.dialogOpenedPayload) {
Expand Down Expand Up @@ -284,7 +285,7 @@ export class FFPage implements PageDelegate {
workerSession.on('Runtime.console', event => {
const { type, args, location } = event;
const context = worker.existingExecutionContext!;
this._page.addConsoleMessage(worker, type, args.map(arg => createHandle(context, arg)), location);
this._page.addConsoleMessage(worker, type, args.map(arg => createHandle(context, arg)), location, undefined, Date.now());
});
// Note: we receive worker exceptions directly from the page.
}
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ export class Page extends SdkObject<PageEventMap> {
await PageBinding.dispatch(this, payload, context);
}

addConsoleMessage(worker: Worker | null, type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
const message = new ConsoleMessage(this, worker, type, text, args, location);
addConsoleMessage(worker: Worker | null, type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text: string | undefined, timestamp: number) {
const message = new ConsoleMessage(this, worker, type, text, args, location, timestamp);
const intercepted = this.frameManager.interceptConsoleMessage(message);
if (intercepted) {
args.forEach(arg => arg.dispose());
Expand Down
7 changes: 4 additions & 3 deletions packages/playwright-core/src/server/webkit/wkPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,9 +579,9 @@ export class WKPage implements PageDelegate {
url: url || '',
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1,
}
},
};
this._onConsoleRepeatCountUpdated({ count: 1 });
this._onConsoleRepeatCountUpdated({ count: 1, timestamp: event.message.timestamp });
}

_onConsoleRepeatCountUpdated(event: Protocol.Console.messageRepeatCountUpdatedPayload) {
Expand All @@ -593,8 +593,9 @@ export class WKPage implements PageDelegate {
count,
location
} = this._lastConsoleMessage;
const timestamp = event.timestamp ? event.timestamp * 1000 : Date.now();
for (let i = count; i < event.count; ++i)
this._page.addConsoleMessage(null, derivedType, handles, location, handles.length ? undefined : text);
this._page.addConsoleMessage(null, derivedType, handles, location, handles.length ? undefined : text, timestamp);
this._lastConsoleMessage.count = event.count;
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/playwright-core/src/server/webkit/wkWorkers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export class WKWorkers {
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1
};
this._page.addConsoleMessage(worker, derivedType, handles, location, handles.length ? undefined : text);
const timestamp = event.message.timestamp ? event.message.timestamp * 1000 : Date.now();
this._page.addConsoleMessage(worker, derivedType, handles, location, handles.length ? undefined : text, timestamp);
}
}
5 changes: 5 additions & 0 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19267,6 +19267,11 @@ export interface ConsoleMessage {
*/
text(): string;

/**
* The timestamp of the console message in milliseconds since the Unix epoch.
*/
timestamp(): number;

type(): "log"|"debug"|"info"|"error"|"warning"|"dir"|"dirxml"|"table"|"trace"|"clear"|"startGroup"|"startGroupCollapsed"|"endGroup"|"assert"|"profile"|"profileEnd"|"count"|"time"|"timeEnd";

/**
Expand Down
33 changes: 33 additions & 0 deletions packages/playwright/src/mcp/browser/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export type FilenameTemplate = {
date?: Date;
};

type VideoParams = NonNullable<Parameters<playwright.Video['start']>[0]>;

export class Context {
readonly config: FullConfig;
readonly sessionLog: SessionLog | undefined;
Expand All @@ -68,6 +70,10 @@ export class Context {
private _currentTab: Tab | undefined;
private _clientInfo: ClientInfo;
private _routes: RouteEntry[] = [];
private _video: {
allVideos: Set<playwright.Video>;
listener: (page: playwright.Page) => void;
} | undefined;

private static _allContexts: Set<Context> = new Set();
private _closeBrowserContextPromise: Promise<void> | undefined;
Expand Down Expand Up @@ -146,6 +152,33 @@ export class Context {
return await outputFile(this.config, this._clientInfo, baseName, options);
}

async startVideoRecording(params: VideoParams) {
if (this._video)
throw new Error('Video recording has already been started.');
const listener = (page: playwright.Page) => {
this._video?.allVideos.add(page.video());
page.video().start(params).catch(() => {});
};
this._video = { allVideos: new Set(), listener };
const browserContext = await this.ensureBrowserContext();
browserContext.pages().forEach(listener);
browserContext.on('page', listener);
}

async stopVideoRecording() {
if (!this._video)
throw new Error('Video recording has not been started.');
const video = this._video;
if (this._browserContextPromise) {
const { browserContext } = await this._browserContextPromise;
browserContext.off('page', video.listener);
for (const page of browserContext.pages())
await page.video().stop().catch(() => {});
}
this._video = undefined;
return video.allVideos;
}

private _onPageCreated(page: playwright.Page) {
const tab = new Tab(this, page, tab => this._onPageClosed(tab));
this._tabs.push(tab);
Expand Down
6 changes: 5 additions & 1 deletion packages/playwright/src/mcp/browser/tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export class Tab extends EventEmitter<TabEventsInterface> {

private _handleConsoleMessage(message: ConsoleMessage) {
this._consoleMessages.push(message);
const wallTime = Date.now();
const wallTime = message.timestamp;
this._addLogEntry({ type: 'console', wallTime, message });
const level = consoleLevelForMessageType(message.type);
if (level === 'error' || level === 'warning')
Expand Down Expand Up @@ -431,13 +431,15 @@ export class Tab extends EventEmitter<TabEventsInterface> {

export type ConsoleMessage = {
type: ReturnType<playwright.ConsoleMessage['type']>;
timestamp: number;
text: string;
toString(): string;
};

function messageToConsoleMessage(message: playwright.ConsoleMessage): ConsoleMessage {
return {
type: message.type(),
timestamp: message.timestamp(),
text: message.text(),
toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`,
};
Expand All @@ -447,12 +449,14 @@ function pageErrorToConsoleMessage(errorOrValue: Error | any): ConsoleMessage {
if (errorOrValue instanceof Error) {
return {
type: 'error',
timestamp: Date.now(),
text: errorOrValue.message,
toString: () => errorOrValue.stack || errorOrValue.message,
};
}
return {
type: 'error',
timestamp: Date.now(),
text: String(errorOrValue),
toString: () => String(errorOrValue),
};
Expand Down
Loading
Loading