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
15 changes: 15 additions & 0 deletions docs/src/api/class-weberror.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,18 @@ Unhandled error that was thrown.
- returns: <[string]>

Unhandled error that was thrown.

## method: WebError.location
* since: v1.59
* langs: js, python
- returns: <[Object]>
- `url` <[string]> URL of the resource.
- `line` <[int]> 0-based line number in the resource.
- `column` <[int]> 0-based column number in the resource.

## method: WebError.location
* since: v1.59
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.

1.60

* langs: csharp, java
- returns: <[string]>

URL of the resource followed by 0-based line and column numbers in the resource formatted as `URL:line:column`.
17 changes: 17 additions & 0 deletions packages/playwright-client/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22144,6 +22144,23 @@ export interface WebError {
*/
error(): Error;

location(): {
/**
* URL of the resource.
*/
url: string;

/**
* 0-based line number in the resource.
*/
line: number;

/**
* 0-based column number in the resource.
*/
column: number;
};

/**
* The page that produced this unhandled exception, if any.
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/client/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel>
}
this.emit(Events.BrowserContext.Console, consoleMessage);
});
this._channel.on('pageError', ({ error, page }) => {
this._channel.on('pageError', ({ error, page, location }) => {
const pageObject = Page.from(page);
const parsedError = parseError(error);
this.emit(Events.BrowserContext.WebError, new WebError(pageObject, parsedError));
this.emit(Events.BrowserContext.WebError, new WebError(pageObject, parsedError, location));
if (pageObject)
pageObject.emit(Events.Page.PageError, parsedError);
});
Expand Down
11 changes: 10 additions & 1 deletion packages/playwright-core/src/client/webError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,19 @@

import type { Page } from './page';
import type * as api from '../../types/types';
import type * as channels from '@protocol/channels';

type WebErrorLocation = channels.BrowserContextPageErrorEvent['location'];

export class WebError implements api.WebError {
private _page: Page | null;
private _error: Error;
private _location: WebErrorLocation;

constructor(page: Page | null, error: Error) {
constructor(page: Page | null, error: Error, location: WebErrorLocation) {
this._page = page;
this._error = error;
this._location = location;
}

page() {
Expand All @@ -33,4 +38,8 @@ export class WebError implements api.WebError {
error() {
return this._error;
}

location() {
return this._location;
}
}
5 changes: 5 additions & 0 deletions packages/playwright-core/src/protocol/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,11 @@ scheme.BrowserContextPageEvent = tObject({
scheme.BrowserContextPageErrorEvent = tObject({
error: tType('SerializedError'),
page: tChannel(['Page']),
location: tObject({
url: tString,
line: tInt,
column: tInt,
}),
});
scheme.BrowserContextRouteEvent = tObject({
route: tChannel(['Route']),
Expand Down
4 changes: 3 additions & 1 deletion packages/playwright-core/src/server/bidi/bidiPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,9 @@ export class BidiPage implements PageDelegate {
const location = `${f.url}:${f.lineNumber + 1}:${f.columnNumber + 1}`;
return f.functionName ? ` at ${f.functionName} (${location})` : ` at ${location}`;
}).join('\n')}`;
this._page.addPageError(error);
const callFrame = params.stackTrace?.callFrames[0];
const location = callFrame ?? { url: '', lineNumber: 1, columnNumber: 1 };
this._page.addPageError(error, location);
return;
}
if (params.type !== 'console')
Expand Down
3 changes: 2 additions & 1 deletion packages/playwright-core/src/server/browserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type { Browser, BrowserOptions } from './browser';
import type { ConsoleMessage } from './console';
import type { Download } from './download';
import type * as frames from './frames';
import type { PageError } from './page';
import type { Progress } from './progress';
import type { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
import type { SerializedStorage } from '@injected/storageScript';
Expand Down Expand Up @@ -68,7 +69,7 @@ export type BrowserContextEventMap = {
[BrowserContextEvent.Console]: [message: ConsoleMessage];
[BrowserContextEvent.Close]: [];
[BrowserContextEvent.Page]: [page: Page];
[BrowserContextEvent.PageError]: [error: Error, page: Page];
[BrowserContextEvent.PageError]: [pageError: PageError, page: Page];
[BrowserContextEvent.Request]: [request: network.Request];
[BrowserContextEvent.Response]: [response: network.Response];
[BrowserContextEvent.RequestFailed]: [request: network.Request];
Expand Down
10 changes: 5 additions & 5 deletions packages/playwright-core/src/server/chromium/crPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { createHandle, CRExecutionContext } from './crExecutionContext';
import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './crInput';
import { CRNetworkManager } from './crNetworkManager';
import { CRPDF } from './crPdf';
import { exceptionToError, releaseObject, toConsoleMessageLocation } from './crProtocolHelper';
import { exceptionToError, releaseObject, stackTraceToLocation } from './crProtocolHelper';
import { platformToFontFamilies } from './defaultFontFamilies';
import { TargetClosedError } from '../errors';
import { isSessionClosedError } from '../protocolError';
Expand Down Expand Up @@ -746,9 +746,9 @@ 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), undefined, event.timestamp);
this._page.addConsoleMessage(worker, event.type, args, stackTraceToLocation(event.stackTrace), undefined, event.timestamp);
});
session.on('Runtime.exceptionThrown', exception => this._page.addPageError(exceptionToError(exception.exceptionDetails)));
session.on('Runtime.exceptionThrown', exception => this._page.addPageError(exceptionToError(exception.exceptionDetails), stackTraceToLocation(exception.exceptionDetails.stackTrace)));
}

_onDetachedFromTarget(event: Protocol.Target.detachedFromTargetPayload) {
Expand Down Expand Up @@ -809,7 +809,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), undefined, event.timestamp);
this._page.addConsoleMessage(null, event.type, values, stackTraceToLocation(event.stackTrace), undefined, event.timestamp);
}

async _onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) {
Expand Down Expand Up @@ -838,7 +838,7 @@ class FrameSession {
}

_handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) {
this._page.addPageError(exceptionToError(exceptionDetails));
this._page.addPageError(exceptionToError(exceptionDetails), stackTraceToLocation(exceptionDetails.stackTrace));
}

async _onTargetCrashed() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export async function readProtocolStream(client: CRSession, handle: string): Pro
return Buffer.concat(chunks);
}

export function toConsoleMessageLocation(stackTrace: Protocol.Runtime.StackTrace | undefined): types.ConsoleMessageLocation {
export function stackTraceToLocation(stackTrace: Protocol.Runtime.StackTrace | undefined): types.ConsoleMessageLocation {
return stackTrace && stackTrace.callFrames.length ? {
url: stackTrace.callFrames[0].url,
lineNumber: stackTrace.callFrames[0].lineNumber,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { CRNetworkManager } from './crNetworkManager';
import { BrowserContext } from '../browserContext';
import * as network from '../network';
import { ConsoleMessage } from '../console';
import { toConsoleMessageLocation } from './crProtocolHelper';
import { stackTraceToLocation } from './crProtocolHelper';

import type { CRBrowserContext } from './crBrowser';
import type { CRSession } from './crConnection';
Expand Down Expand Up @@ -60,7 +60,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), event.timestamp);
const message = new ConsoleMessage(null, this, event.type, undefined, args, stackTraceToLocation(event.stackTrace), event.timestamp);
this.browserContext.emit(BrowserContext.Events.Console, message);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { disposeAll } from '../disposable';
import type { ConsoleMessage } from '../console';
import type { Dialog } from '../dialog';
import type { Request, Response, RouteHandler } from '../network';
import type { InitScript, Page } from '../page';
import type { InitScript, Page, PageError } from '../page';
import type { Disposable } from '../disposable';
import type { DispatcherScope } from './dispatcher';
import type * as channels from '@protocol/channels';
Expand Down Expand Up @@ -109,8 +109,16 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
this._dispatchEvent('close');
this._dispose();
});
this.addObjectListener(BrowserContext.Events.PageError, (error: Error, page: Page) => {
this._dispatchEvent('pageError', { error: serializeError(error), page: PageDispatcher.from(this, page) });
this.addObjectListener(BrowserContext.Events.PageError, (pageError: PageError, page: Page) => {
this._dispatchEvent('pageError', {
error: serializeError(pageError.error),
page: PageDispatcher.from(this, page),
location: {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber,
},
});
});
this.addObjectListener(BrowserContext.Events.Console, (message: ConsoleMessage) => {
const pageDispatcher = PageDispatcher.fromNullable(this, message.page());
Expand Down
4 changes: 2 additions & 2 deletions packages/playwright-core/src/server/electron/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { validateBrowserContextOptions } from '../browserContext';
import { CRBrowser } from '../chromium/crBrowser';
import { CRConnection } from '../chromium/crConnection';
import { createHandle, CRExecutionContext } from '../chromium/crExecutionContext';
import { toConsoleMessageLocation } from '../chromium/crProtocolHelper';
import { stackTraceToLocation } from '../chromium/crProtocolHelper';
import { ConsoleMessage } from '../console';
import { helper } from '../helper';
import { SdkObject } from '../instrumentation';
Expand Down Expand Up @@ -115,7 +115,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), event.timestamp);
const message = new ConsoleMessage(null, null, event.type, undefined, args, stackTraceToLocation(event.stackTrace), event.timestamp);
this.emit(ElectronApplication.Events.Console, message);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/playwright-core/src/server/firefox/ffPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export class FFPage implements PageDelegate {
const error = new Error(message);
error.stack = params.message + '\n' + params.stack.split('\n').filter(Boolean).map(a => a.replace(/([^@]*)@(.*)/, ' at $1 ($2)')).join('\n');
error.name = name;
this._page.addPageError(error);
this._page.addPageError(error, params.location);
}

_onConsole(payload: Protocol.Runtime.consolePayload) {
Expand Down
14 changes: 10 additions & 4 deletions packages/playwright-core/src/server/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ export type PageEventMap = {

const navigationMarkSymbol = Symbol('navigationMark');

export type PageError = {
error: Error,
location: types.ConsoleMessageLocation,
};

export class Page extends SdkObject<PageEventMap> {
static Events = PageEvent;

Expand All @@ -165,7 +170,7 @@ export class Page extends SdkObject<PageEventMap> {
private _initialized: Page | Error | undefined;
private _initializedPromise = new ManualPromise<Page | Error>();
private _consoleMessages: ConsoleMessage[] = [];
private _pageErrors: Error[] = [];
private _pageErrors: PageError[] = [];
private _crashed = false;
readonly openScope = new LongStandingScope();
readonly browserContext: BrowserContext;
Expand Down Expand Up @@ -422,7 +427,8 @@ export class Page extends SdkObject<PageEventMap> {
return marked === -1 ? this._consoleMessages : this._consoleMessages.slice(marked + 1);
}

addPageError(pageError: Error) {
addPageError(error: Error, location: types.ConsoleMessageLocation) {
const pageError: PageError = { error, location };
this._pageErrors.push(pageError);
ensureArrayLimit(this._pageErrors, 200); // Avoid unbounded memory growth.

Expand All @@ -439,9 +445,9 @@ export class Page extends SdkObject<PageEventMap> {

pageErrors(filter?: 'all' | 'since-navigation') {
if (filter === 'all')
return this._pageErrors;
return this._pageErrors.map(e => e.error);
const marked = this._pageErrors.findLastIndex(e => (e as any)[navigationMarkSymbol]);
return marked === -1 ? this._pageErrors : this._pageErrors.slice(marked + 1);
return (marked === -1 ? this._pageErrors : this._pageErrors.slice(marked + 1)).map(e => e.error);
}

async reload(progress: Progress, options: types.NavigateOptions): Promise<network.Response | null> {
Expand Down
12 changes: 10 additions & 2 deletions packages/playwright-core/src/server/trace/recorder/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import type { Download } from '../../download';
import type { APIRequestContext } from '../../fetch';
import type { HarTracerDelegate } from '../../har/harTracer';
import type { CallMetadata, InstrumentationListener } from '../../instrumentation';
import type { PageError } from '../../page';
import type { RecordHarOptions, StackFrame, TracingTracingStopChunkParams } from '@protocol/channels';
import type * as har from '@trace/har';
import type { FrameSnapshot } from '@trace/snapshot';
Expand Down Expand Up @@ -632,13 +633,20 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
this._started = false;
}

private _onPageError(error: Error, page: Page) {
private _onPageError(pageError: PageError, page: Page) {
const event: trace.EventTraceEvent = {
type: 'event',
time: monotonicTime(),
class: 'BrowserContext',
method: 'pageError',
params: { error: serializeError(error) },
params: {
error: serializeError(pageError.error),
location: {
url: pageError.location.url,
line: pageError.location.lineNumber,
column: pageError.location.columnNumber,
},
},
pageId: page.guid,
};
this._appendTraceEvent(event);
Expand Down
6 changes: 5 additions & 1 deletion packages/playwright-core/src/server/webkit/wkPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,11 @@ export class WKPage implements PageDelegate {
error.stack = stack;
error.name = name;

this._page.addPageError(error);
this._page.addPageError(error, {
url: url || '',
lineNumber: (lineNumber || 1) - 1,
columnNumber: (columnNumber || 1) - 1,
});
return;
}

Expand Down
17 changes: 17 additions & 0 deletions packages/playwright-core/types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22144,6 +22144,23 @@ export interface WebError {
*/
error(): Error;

location(): {
/**
* URL of the resource.
*/
url: string;

/**
* 0-based line number in the resource.
*/
line: number;

/**
* 0-based column number in the resource.
*/
column: number;
};

/**
* The page that produced this unhandled exception, if any.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/protocol/src/channels.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,11 @@ export type BrowserContextPageEvent = {
export type BrowserContextPageErrorEvent = {
error: SerializedError,
page: PageChannel,
location: {
url: string,
line: number,
column: number,
},
};
export type BrowserContextRouteEvent = {
route: RouteChannel,
Expand Down
6 changes: 6 additions & 0 deletions packages/protocol/src/protocol.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,12 @@ BrowserContext:
parameters:
error: SerializedError
page: Page
location:
type: object
properties:
url: string
line: int
column: int

route:
parameters:
Expand Down
Loading
Loading